Skydata001 commited on
Commit
19dd391
·
verified ·
1 Parent(s): 328c18c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +31 -75
app.py CHANGED
@@ -8,6 +8,7 @@ import shutil
8
  import zipfile
9
  import base64
10
  import asyncio
 
11
  import subprocess
12
  from datetime import datetime
13
  from typing import List, Dict, Any, Tuple
@@ -25,14 +26,12 @@ os.makedirs(OUTPUT_DIR, exist_ok=True)
25
 
26
  templates = Jinja2Templates(directory="templates")
27
 
28
- # الامتدادات المظلمة المستبعدة توفيراً للوقت
29
  IGNORED_EXTENSIONS = {
30
  '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.ico',
31
  '.mp4', '.avi', '.mkv', '.mov', '.flv', '.wmv', '.mp3', '.wav', '.ogg',
32
  '.ttf', '.otf', '.woff', '.woff2', '.eot', '.pdf'
33
  }
34
 
35
- # المجلدات الواهية المستبعدة (تصاميم وأيقونات لا تحتوي كوداً)
36
  IGNORED_SUBDIRS = [
37
  'res/drawable', 'res/color', 'res/anim', 'res/mipmap', 'res/values-'
38
  ]
@@ -67,7 +66,7 @@ class ConnectionManager:
67
  "message": message,
68
  "progress": progress
69
  }
70
- for connection in self.active_connections:
71
  try:
72
  await connection.send_json(log_entry)
73
  except Exception:
@@ -85,26 +84,18 @@ def clean_workspace():
85
  elif os.path.isdir(file_path):
86
  shutil.rmtree(file_path)
87
  except Exception as e:
88
- print(f"Cleanup error for {file_path}: {e}")
89
-
90
- def calculate_entropy(data: bytes) -> float:
91
- if not data:
92
- return 0.0
93
- entropy = 0.0
94
- length = len(data)
95
- byte_counts = [0] * 256
96
- for b in data:
97
- byte_counts[b] += 1
98
- for count in byte_counts:
99
- if count == 0:
100
- continue
101
- p = count / length
102
- entropy -= p * math.log2(p)
103
- return entropy
104
 
105
  def extract_strings_from_binary_file(file_path: str) -> str:
106
  try:
107
- res = subprocess.run(['strings', '-n', '4', file_path], capture_output=True, text=True, errors='ignore')
108
  return res.stdout
109
  except Exception:
110
  try:
@@ -115,36 +106,10 @@ def extract_strings_from_binary_file(file_path: str) -> str:
115
  except Exception:
116
  return ""
117
 
118
- def attempt_decompression_and_xor(data: bytes) -> List[Tuple[str, str]]:
119
- extracted_texts = []
120
- try:
121
- decompressed = zlib.decompress(data)
122
- extracted_texts.append(("Zlib Decompressed", decompressed.decode('utf-8', errors='ignore')))
123
- except Exception:
124
- pass
125
-
126
- try:
127
- decompressed = gzip.decompress(data)
128
- extracted_texts.append(("Gzip Decompressed", decompressed.decode('utf-8', errors='ignore')))
129
- except Exception:
130
- pass
131
-
132
- for key in XOR_KEYS:
133
- try:
134
- xor_bytes = bytes([b ^ key for b in data[:50000]])
135
- decoded = xor_bytes.decode('utf-8', errors='ignore')
136
- if any(k in decoded.lower() for k in ['http', 'discord', 'key', 'license', 'stuart']):
137
- extracted_texts.append((f"XOR Key (0x{key:02X})", decoded))
138
- except Exception:
139
- pass
140
-
141
- return extracted_texts
142
-
143
  def extract_snippet(text: str, start: int, end: int, margin: int = 50) -> str:
144
  s_start = max(0, start - margin)
145
  s_end = min(len(text), end + margin)
146
- snippet = text[s_start:s_end].replace('\n', ' ').replace('\r', ' ')
147
- return f"...{snippet}..."
148
 
149
  def classify_severity(match_type: str, match_value: str) -> str:
150
  val_lower = match_value.lower()
@@ -180,22 +145,23 @@ async def analyze_zip(file: UploadFile = File(...)):
180
  extract_folder = os.path.join(WORKSPACE_DIR, "extracted")
181
  os.makedirs(extract_folder, exist_ok=True)
182
 
183
- await manager.broadcast_log("INFO", f"استقبال الملف على الخادم: {file.filename}...", 5)
 
184
  with open(zip_path, "wb") as buffer:
185
- while chunk := await file.read(1024 * 1024):
186
  buffer.write(chunk)
187
 
188
- await manager.broadcast_log("INFO", "جاري فك ضغط الأرشيف...", 15)
 
189
  try:
190
- with zipfile.ZipFile(zip_path, 'r') as zip_ref:
191
- zip_file_list = zip_ref.namelist()
192
- zip_ref.extractall(extract_folder)
193
  await manager.broadcast_log("SUCCESS", f"تم فك الضغط بنجاح! إجمالي المخرجات: {len(zip_file_list)} ملف", 25)
194
  except Exception as e:
195
  return {"status": "error", "message": f"فشل فك ضغط الأرشيف: {str(e)}"}
196
 
 
197
  files_to_process = []
198
- max_file_size = 50 * 1024 * 1024
199
 
200
  for root, _, files in os.walk(extract_folder):
201
  for f in files:
@@ -206,7 +172,6 @@ async def analyze_zip(file: UploadFile = File(...)):
206
  full_path = os.path.join(root, f)
207
  rel_p = os.path.relpath(full_path, extract_folder).replace('\\', '/')
208
 
209
- # استبعاد مجلدات الموارد غير البرمجية لتسريع الفحص
210
  if any(rel_p.startswith(sub) for sub in IGNORED_SUBDIRS):
211
  continue
212
 
@@ -224,16 +189,22 @@ async def analyze_zip(file: UploadFile = File(...)):
224
  "medium_priority_count": 0,
225
  "low_priority_count": 0,
226
  "matches_by_type": {
227
- "DISCORD": 0, "URL": 0, "EMAIL": 0, "KEYWORD": 0, "BASE64_DECODED": 0, "UNPACKED_XOR": 0
228
  }
229
  }
230
 
 
 
 
231
  for idx, file_path in enumerate(files_to_process):
232
  rel_path = os.path.relpath(file_path, extract_folder)
233
 
234
- if idx % 15 == 0 or idx == total_files - 1:
 
 
235
  progress = 30 + int((idx / total_files) * 65)
236
- await manager.broadcast_log("INFO", f"جاري تفتيش الملف ({idx + 1}/{total_files}): {rel_path}", progress)
 
237
  await asyncio.sleep(0.001)
238
 
239
  file_findings = []
@@ -242,7 +213,6 @@ async def analyze_zip(file: UploadFile = File(...)):
242
  with open(file_path, 'rb') as f:
243
  raw_bytes = f.read()
244
 
245
- file_entropy = calculate_entropy(raw_bytes)
246
  is_binary = b'\x00' in raw_bytes[:1024] or rel_path.endswith(('.so', '.dex', '.dat'))
247
 
248
  if is_binary:
@@ -288,7 +258,7 @@ async def analyze_zip(file: UploadFile = File(...)):
288
  stats[f"{sev.lower()}_priority_count"] += 1
289
 
290
  # Base64
291
- for b64_match in BASE64_PATTERN.findall(content_text)[:10]:
292
  try:
293
  decoded = base64.b64decode(b64_match).decode('utf-8', errors='ignore')
294
  if any(k in decoded.lower() for k in ['http', 'discord', 'key', 'stuart', 'license', 'verify']):
@@ -303,20 +273,6 @@ async def analyze_zip(file: UploadFile = File(...)):
303
  except Exception:
304
  pass
305
 
306
- if file_entropy > 7.0:
307
- unpacked = attempt_decompression_and_xor(raw_bytes)
308
- for method, text_data in unpacked:
309
- for match in DISCORD_PATTERN.finditer(text_data):
310
- val = match.group()
311
- file_findings.append({
312
- "type": "UNPACKED_XOR",
313
- "value": f"[{method}] {val}",
314
- "snippet": f"مستخرج عبر {method}: {val}",
315
- "severity": "HIGH"
316
- })
317
- stats["matches_by_type"]["UNPACKED_XOR"] += 1
318
- stats["high_priority_count"] += 1
319
-
320
  if file_findings:
321
  stats["total_matches"] += len(file_findings)
322
  findings_by_file.append({
@@ -325,7 +281,7 @@ async def analyze_zip(file: UploadFile = File(...)):
325
  "results": file_findings
326
  })
327
 
328
- except Exception as e:
329
  pass
330
 
331
  await manager.broadcast_log("SUCCESS", f"اكتمل الفحص بنجاح! إجمالي التطابقات المكتشفة: {stats['total_matches']}", 100)
 
8
  import zipfile
9
  import base64
10
  import asyncio
11
+ import time
12
  import subprocess
13
  from datetime import datetime
14
  from typing import List, Dict, Any, Tuple
 
26
 
27
  templates = Jinja2Templates(directory="templates")
28
 
 
29
  IGNORED_EXTENSIONS = {
30
  '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp', '.ico',
31
  '.mp4', '.avi', '.mkv', '.mov', '.flv', '.wmv', '.mp3', '.wav', '.ogg',
32
  '.ttf', '.otf', '.woff', '.woff2', '.eot', '.pdf'
33
  }
34
 
 
35
  IGNORED_SUBDIRS = [
36
  'res/drawable', 'res/color', 'res/anim', 'res/mipmap', 'res/values-'
37
  ]
 
66
  "message": message,
67
  "progress": progress
68
  }
69
+ for connection in list(self.active_connections):
70
  try:
71
  await connection.send_json(log_entry)
72
  except Exception:
 
84
  elif os.path.isdir(file_path):
85
  shutil.rmtree(file_path)
86
  except Exception as e:
87
+ pass
88
+
89
+ def extract_zip_sync(zip_path: str, extract_to: str) -> List[str]:
90
+ """فك ضغط الملف بمسار مستقل عن Event Loop لمنع التجمّد"""
91
+ with zipfile.ZipFile(zip_path, 'r') as zip_ref:
92
+ file_list = zip_ref.namelist()
93
+ zip_ref.extractall(extract_to)
94
+ return file_list
 
 
 
 
 
 
 
 
95
 
96
  def extract_strings_from_binary_file(file_path: str) -> str:
97
  try:
98
+ res = subprocess.run(['strings', '-n', '4', file_path], capture_output=True, text=True, errors='ignore', timeout=5)
99
  return res.stdout
100
  except Exception:
101
  try:
 
106
  except Exception:
107
  return ""
108
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  def extract_snippet(text: str, start: int, end: int, margin: int = 50) -> str:
110
  s_start = max(0, start - margin)
111
  s_end = min(len(text), end + margin)
112
+ return f"...{text[s_start:s_end].replace('\n', ' ').replace('\r', ' ')}..."
 
113
 
114
  def classify_severity(match_type: str, match_value: str) -> str:
115
  val_lower = match_value.lower()
 
145
  extract_folder = os.path.join(WORKSPACE_DIR, "extracted")
146
  os.makedirs(extract_folder, exist_ok=True)
147
 
148
+ # 1. حفظ الملف بسلاسة لتجنب احتكار الذاكرة
149
+ await manager.broadcast_log("INFO", f"جاري استلام الملف: {file.filename}...", 5)
150
  with open(zip_path, "wb") as buffer:
151
+ while chunk := await file.read(2 * 1024 * 1024): # قراءة بإنتاجية 2MB
152
  buffer.write(chunk)
153
 
154
+ # 2. فك الضغط بـ Thread خفي لمنع تجمّد السيرفر عند 12%
155
+ await manager.broadcast_log("INFO", "جاري فك ضغط الأرشيف عبر الأنوية متعددة...", 15)
156
  try:
157
+ zip_file_list = await asyncio.to_thread(extract_zip_sync, zip_path, extract_folder)
 
 
158
  await manager.broadcast_log("SUCCESS", f"تم فك الضغط بنجاح! إجمالي المخرجات: {len(zip_file_list)} ملف", 25)
159
  except Exception as e:
160
  return {"status": "error", "message": f"فشل فك ضغط الأرشيف: {str(e)}"}
161
 
162
+ # 3. تجميع الملفات المفلترة
163
  files_to_process = []
164
+ max_file_size = 30 * 1024 * 1024 # حد أقصى 30MB للملف الواحد لتفادي البطء
165
 
166
  for root, _, files in os.walk(extract_folder):
167
  for f in files:
 
172
  full_path = os.path.join(root, f)
173
  rel_p = os.path.relpath(full_path, extract_folder).replace('\\', '/')
174
 
 
175
  if any(rel_p.startswith(sub) for sub in IGNORED_SUBDIRS):
176
  continue
177
 
 
189
  "medium_priority_count": 0,
190
  "low_priority_count": 0,
191
  "matches_by_type": {
192
+ "DISCORD": 0, "URL": 0, "EMAIL": 0, "KEYWORD": 0, "BASE64_DECODED": 0
193
  }
194
  }
195
 
196
+ last_log_time = time.time()
197
+
198
+ # 4. مسح وتسريع الفحص
199
  for idx, file_path in enumerate(files_to_process):
200
  rel_path = os.path.relpath(file_path, extract_folder)
201
 
202
+ # إرسال تحديث السجل كل 200 ملي ثانية فقط لحماية المتصفح من التجمد
203
+ current_time = time.time()
204
+ if (current_time - last_log_time > 0.2) or idx == total_files - 1:
205
  progress = 30 + int((idx / total_files) * 65)
206
+ await manager.broadcast_log("INFO", f"تفتيش ({idx + 1}/{total_files}): {rel_path}", progress)
207
+ last_log_time = current_time
208
  await asyncio.sleep(0.001)
209
 
210
  file_findings = []
 
213
  with open(file_path, 'rb') as f:
214
  raw_bytes = f.read()
215
 
 
216
  is_binary = b'\x00' in raw_bytes[:1024] or rel_path.endswith(('.so', '.dex', '.dat'))
217
 
218
  if is_binary:
 
258
  stats[f"{sev.lower()}_priority_count"] += 1
259
 
260
  # Base64
261
+ for b64_match in BASE64_PATTERN.findall(content_text)[:5]:
262
  try:
263
  decoded = base64.b64decode(b64_match).decode('utf-8', errors='ignore')
264
  if any(k in decoded.lower() for k in ['http', 'discord', 'key', 'stuart', 'license', 'verify']):
 
273
  except Exception:
274
  pass
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  if file_findings:
277
  stats["total_matches"] += len(file_findings)
278
  findings_by_file.append({
 
281
  "results": file_findings
282
  })
283
 
284
+ except Exception:
285
  pass
286
 
287
  await manager.broadcast_log("SUCCESS", f"اكتمل الفحص بنجاح! إجمالي التطابقات المكتشفة: {stats['total_matches']}", 100)