Compare commits

..
8 Commits
Author SHA1 Message Date
87owo eb1955779b Add files via upload 2026-06-06 12:39:10 +08:00
87owo 2e5b4cc5a1 Add files via upload 2026-06-05 21:25:07 +08:00
87owo 38b4f96437 Add files via upload 2026-06-05 21:24:24 +08:00
87owo 173922d0ce Add files via upload 2026-06-05 19:21:52 +08:00
87owo 99303a237e Add files via upload 2026-06-05 14:30:56 +08:00
87owo 8f7baf2790 Add files via upload 2026-06-05 13:49:43 +08:00
87owo b673336b3f Add files via upload 2026-06-01 19:41:23 +08:00
87owo e37400a0f3 Add files via upload 2026-06-01 18:58:54 +08:00
10 changed files with 1845 additions and 532 deletions

No files matched your search

+57 -18
View File
@@ -9,12 +9,15 @@ try:
JSON_DUMPS = lambda x: orjson.dumps(x).decode('utf-8')
JSON_LOADS = orjson.loads
HAS_ORJSON = True
except ImportError:
import json
JSON_DUMPS = json.dumps
JSON_LOADS = json.loads
HAS_ORJSON = False
####################################################################################################
JSONL_PATH = "pe_features.jsonl"
BATCH_SIZE = 1000
MAX_FILE_SIZE = 4 * 1024 * 1024 * 1024
@@ -69,7 +72,8 @@ V2_GUID = GUID(0x00AAC56B, 0xCD44, 0x11D0, (ctypes.c_ubyte * 8)(0x8C, 0xC2, 0x00
####################################################################################################
def verify_signature(file_path):
if os.name != 'nt': return 0
if os.name != 'nt':
return 0
try:
wintrust = ctypes.windll.wintrust
wintrust.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.wintypes.LPVOID]
@@ -126,16 +130,20 @@ class FeatureExtractor:
try:
f = float(val)
return f if not (math.isinf(f) or math.isnan(f)) else 0.0
except Exception:
return 0.0
@staticmethod
def _calc_entropy(data):
if not data: return 0.0
if not data:
return 0.0
arr = np.frombuffer(data, dtype=np.uint8)
sz = len(arr)
counts = np.bincount(arr, minlength=256)
counts = counts[counts > 0]
return float(np.log2(sz) - np.sum(counts * np.log2(counts)) / sz)
@classmethod
@@ -189,6 +197,7 @@ class FeatureExtractor:
byte_bins = w_counts.reshape(16, 16).sum(axis=1)
idx_start = ent_bin * 16
ent_hist[idx_start : idx_start+16] += byte_bins
else:
num_windows = (sz - window) // step + 1
batch_windows = 10000
@@ -247,6 +256,7 @@ class FeatureExtractor:
def _extract_rich_header(cls, pe):
if not hasattr(pe, 'RICH_HEADER') or not pe.RICH_HEADER:
return {"HasRichHeader": 0.0, "RichHeaderCount": 0.0}
return {
"HasRichHeader": 1.0,
"RichHeaderCount": float(len(pe.RICH_HEADER.values) // 2) if hasattr(pe.RICH_HEADER, 'values') else 0.0
@@ -305,6 +315,7 @@ class FeatureExtractor:
for entry_lang in entry_id.directory.entries:
if hasattr(entry_lang, 'id'):
langs.add(entry_lang.id)
if hasattr(entry_lang, 'data'):
try:
data = pe.get_data(entry_lang.data.struct.OffsetToData, entry_lang.data.struct.Size)
@@ -328,10 +339,13 @@ class FeatureExtractor:
if hasattr(pe, 'DIRECTORY_ENTRY_LOAD_CONFIG'):
cfg["HasLoadConfig"] = 1.0
struct = pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct
if getattr(struct, 'GuardCFFunctionTable', 0) != 0:
cfg["HasCFG"] = 1.0
if getattr(struct, 'SEHandlerTable', 0) != 0:
cfg["HasSEHTable"] = 1.0
return cfg
@classmethod
@@ -364,6 +378,7 @@ class FeatureExtractor:
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
if dw_length < 8:
break
count += 1
curr += (dw_length + 7) & ~7
@@ -372,9 +387,10 @@ class FeatureExtractor:
@classmethod
def extract(cls, file_bytes, file_path, fsize):
if fsize == 0: return None
base, dlls, apis, pe = {}, set(), set(), None
if fsize == 0:
return None
base, dlls, apis, pe = {}, set(), set(), None
try:
pe = pefile.PE(data=file_bytes, fast_load=True)
@@ -410,7 +426,6 @@ class FeatureExtractor:
base['Is64Bit'] = 1.0 if base['Machine'] in (0x8664, 0xAA64, 0x0200) else 0.0
base['IsExe'] = 1.0 if pe.is_exe() else 0.0
base['IsDll'] = 1.0 if pe.is_dll() else 0.0
base['ExceptionCount'] = 0.0
if hasattr(pe, 'OPTIONAL_HEADER'):
@@ -452,10 +467,14 @@ class FeatureExtractor:
raw_sizes.append(section.SizeOfRawData)
v_sizes.append(section.Misc_VirtualSize)
if section.Characteristics & 0x20000000: exec_sec += 1
if section.Characteristics & 0x80000000: write_sec += 1
if section.Characteristics & 0x40000000: read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize: sec_exc = 1
if section.Characteristics & 0x20000000:
exec_sec += 1
if section.Characteristics & 0x80000000:
write_sec += 1
if section.Characteristics & 0x40000000:
read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize:
sec_exc = 1
for flag in cls._CHAR_FLAGS:
if section.Characteristics & flag:
@@ -495,11 +514,13 @@ class FeatureExtractor:
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
for st in getattr(fileinfo, 'StringTable', []):
for key, val in st.entries.items():
try:
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
if k in string_keys:
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
base[f'{k}Length'] = float(len(v))
except Exception:
continue
@@ -519,13 +540,19 @@ class FeatureExtractor:
func_count = 0
for entry in pe.DIRECTORY_ENTRY_IMPORT:
if getattr(entry, 'dll', None):
try: dlls.add(entry.dll.decode('ascii', 'ignore').lower())
except Exception: pass
try:
dlls.add(entry.dll.decode('ascii', 'ignore').lower())
except Exception:
pass
for imp in getattr(entry, 'imports', []):
func_count += 1
if getattr(imp, 'name', None):
try: apis.add(imp.name.decode('ascii', 'ignore'))
except Exception: pass
try:
apis.add(imp.name.decode('ascii', 'ignore'))
except Exception:
pass
base['ImportFunctionCount'] = float(func_count)
base['ExportCount'] = float(len(pe.DIRECTORY_ENTRY_EXPORT.symbols)) if (hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols')) else 0.0
@@ -550,10 +577,13 @@ class FeatureExtractor:
base.update(cls._extract_load_config(pe))
base.update(cls._extract_security_directory(pe, file_bytes, fsize))
for k in base: base[k] = cls._safe_float(base[k])
for k in base:
base[k] = cls._safe_float(base[k])
return {"Base": base, "DLLs": list(dlls), "APIs": list(apis)}
except Exception: return None
except Exception:
return None
finally:
if pe: pe.close()
@@ -570,6 +600,7 @@ def load_existing_hashes():
match = pattern.search(line)
if match:
hashes.add(match.group(1).decode('ascii'))
return hashes
GLOBAL_HASHES = set()
@@ -636,6 +667,7 @@ def scan_and_save(path, label, existing_hashes):
skipped += 1
elif status == 'success':
sha256, json_str = data
if sha256 not in existing_hashes:
existing_hashes.add(sha256)
batch_buffer.append(json_str)
@@ -660,10 +692,12 @@ def scan_and_save(path, label, existing_hashes):
print("\n\n[-] User aborted. Force terminating all child processes...")
pool.terminate()
pool.join()
except Exception as e:
print(f"\n[-] Critical error: {e}")
pool.terminate()
pool.join()
finally:
if batch_buffer:
try:
@@ -688,15 +722,20 @@ if __name__ == "__main__":
while True:
print("-" * 57)
target_path = input("\n[*] Enter path (file or directory): ").strip().strip('"').strip("'")
if target_path.lower() in ['exit', 'q']: break
if target_path.lower() in ['exit', 'q']:
break
if not target_path or not os.path.exists(target_path):
print("[-] Invalid path.")
continue
while True:
label_input = input("[*] Enter label (0=Safe, 1=Malware): ").strip()
if label_input.lower() in ['exit', 'q']: sys.exit()
if label_input in ['0', '1']: break
if label_input.lower() in ['exit', 'q']:
sys.exit()
if label_input in ['0', '1']:
break
print("[-] Please enter 0 or 1.")
scan_and_save(target_path, int(label_input), existing_hashes)
+47 -14
View File
@@ -116,16 +116,20 @@ class FeatureExtractor:
try:
f = float(val)
return f if not (math.isinf(f) or math.isnan(f)) else 0.0
except Exception:
return 0.0
@staticmethod
def _calc_entropy(data):
if not data: return 0.0
if not data:
return 0.0
arr = np.frombuffer(data, dtype=np.uint8)
sz = len(arr)
counts = np.bincount(arr, minlength=256)
counts = counts[counts > 0]
return float(np.log2(sz) - np.sum(counts * np.log2(counts)) / sz)
@classmethod
@@ -295,6 +299,7 @@ class FeatureExtractor:
for entry_lang in entry_id.directory.entries:
if hasattr(entry_lang, 'id'):
langs.add(entry_lang.id)
if hasattr(entry_lang, 'data'):
try:
data = pe.get_data(entry_lang.data.struct.OffsetToData, entry_lang.data.struct.Size)
@@ -318,10 +323,13 @@ class FeatureExtractor:
if hasattr(pe, 'DIRECTORY_ENTRY_LOAD_CONFIG'):
cfg["HasLoadConfig"] = 1.0
struct = pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct
if getattr(struct, 'GuardCFFunctionTable', 0) != 0:
cfg["HasCFG"] = 1.0
if getattr(struct, 'SEHandlerTable', 0) != 0:
cfg["HasSEHTable"] = 1.0
return cfg
@classmethod
@@ -354,6 +362,7 @@ class FeatureExtractor:
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
if dw_length < 8:
break
count += 1
curr += (dw_length + 7) & ~7
@@ -406,7 +415,6 @@ class FeatureExtractor:
base['Is64Bit'] = 1.0 if base['Machine'] in (0x8664, 0xAA64, 0x0200) else 0.0
base['IsExe'] = 1.0 if pe.is_exe() else 0.0
base['IsDll'] = 1.0 if pe.is_dll() else 0.0
base['ExceptionCount'] = 0.0
if hasattr(pe, 'OPTIONAL_HEADER'):
@@ -448,10 +456,14 @@ class FeatureExtractor:
raw_sizes.append(section.SizeOfRawData)
v_sizes.append(section.Misc_VirtualSize)
if section.Characteristics & 0x20000000: exec_sec += 1
if section.Characteristics & 0x80000000: write_sec += 1
if section.Characteristics & 0x40000000: read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize: sec_exc = 1
if section.Characteristics & 0x20000000:
exec_sec += 1
if section.Characteristics & 0x80000000:
write_sec += 1
if section.Characteristics & 0x40000000:
read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize:
sec_exc = 1
for flag in cls._CHAR_FLAGS:
if section.Characteristics & flag:
@@ -491,11 +503,13 @@ class FeatureExtractor:
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
for st in getattr(fileinfo, 'StringTable', []):
for key, val in st.entries.items():
try:
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
if k in string_keys:
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
base[f'{k}Length'] = float(len(v))
except Exception:
continue
@@ -515,13 +529,19 @@ class FeatureExtractor:
func_count = 0
for entry in pe.DIRECTORY_ENTRY_IMPORT:
if getattr(entry, 'dll', None):
try: dlls.add(entry.dll.decode('ascii', 'ignore').lower())
except Exception: pass
try:
dlls.add(entry.dll.decode('ascii', 'ignore').lower())
except Exception:
pass
for imp in getattr(entry, 'imports', []):
func_count += 1
if getattr(imp, 'name', None):
try: apis.add(imp.name.decode('ascii', 'ignore'))
except Exception: pass
try:
apis.add(imp.name.decode('ascii', 'ignore'))
except Exception:
pass
base['ImportFunctionCount'] = float(func_count)
base['ExportCount'] = float(len(pe.DIRECTORY_ENTRY_EXPORT.symbols)) if (hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols')) else 0.0
@@ -546,12 +566,16 @@ class FeatureExtractor:
base.update(cls._extract_load_config(pe))
base.update(cls._extract_security_directory(pe, file_bytes, fsize))
for k in base: base[k] = cls._safe_float(base[k])
for k in base:
base[k] = cls._safe_float(base[k])
return {"Base": base, "DLLs": list(dlls), "APIs": list(apis)}
except Exception: return None
except Exception:
return None
finally:
if pe: pe.close()
if pe:
pe.close()
####################################################################################################
@@ -571,6 +595,7 @@ class ModelPredictor:
def _load_features(self, path):
if not os.path.exists(path):
raise FileNotFoundError(f"Feature file not found: {path}")
with open(path, 'r') as f:
return json.load(f)
@@ -604,9 +629,11 @@ class ModelPredictor:
prob_dict = result[0]
if hasattr(prob_dict, 'get'):
return float(prob_dict.get(1, prob_dict.get('1', 0.0)))
elif isinstance(result, np.ndarray):
if result.ndim == 2 and result.shape[1] > 1:
return float(result[0][1])
return 0.0
####################################################################################################
@@ -615,6 +642,7 @@ def scan_target(target, predictor):
files = []
if os.path.isfile(target):
files.append(target)
elif os.path.isdir(target):
for r, _, fs in os.walk(target):
for f in fs:
@@ -679,6 +707,7 @@ if __name__ == "__main__":
print()
print("-" * 80)
scan_target(target, predictor)
else:
print(f"[-] Path does not exist: {target}")
else:
@@ -687,11 +716,15 @@ if __name__ == "__main__":
print()
print("-" * 80)
path = input("\n[?] Enter File or Folder Path (or 'q' to exit): ").strip().strip('"').strip("'")
if path.lower() in ['q', 'exit']: break
if path.lower() in ['q', 'exit']:
break
if not os.path.exists(path):
print("[-] Path does not exist.")
continue
scan_target(path, predictor)
except KeyboardInterrupt:
break
except Exception as e:
+8 -4
View File
@@ -12,6 +12,7 @@ from sklearn.metrics import accuracy_score, roc_auc_score
try:
import orjson
JSON_LOADS = orjson.loads
except ImportError:
JSON_LOADS = json.loads
@@ -21,9 +22,9 @@ JSONL_PATH = "pe_features.jsonl"
MODEL_FILE = "model.txt"
ONNX_FILE = "Pefile_General_S1.onnx"
FEATURE_FILE = "features.json"
TEST_SIZE = 0.0001
RANDOM_SEED = 42
DLL_HASH_DIM = 256
API_HASH_DIM = 1024
@@ -56,12 +57,14 @@ def analyze_schema(jsonl_path):
with open(jsonl_path, 'rb') as f:
for line in f:
num_lines += 1
if not base_keys:
try:
data = JSON_LOADS(line)
base_data = data.get('Base')
if base_data:
base_keys.update(base_data.keys())
except Exception:
pass
@@ -76,6 +79,7 @@ def analyze_schema(jsonl_path):
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
print(f"[*] Stage 2: Building sparse feature matrix dynamically via Multiprocessing...")
final_features = base_schema + dll_schema + api_schema
num_features = len(final_features)
offset_dll = len(base_schema)
offset_api = offset_dll + DLL_HASH_DIM
@@ -118,6 +122,7 @@ def _chunk_worker(args):
if f.read(1) != b'\n':
f.seek(start_byte)
f.readline()
else:
f.seek(start_byte)
else:
@@ -181,9 +186,7 @@ def _calculate_dynamic_lr(current_iter: int) -> float:
def train_process(X, y, feature_names):
print(f"[*] Dataset shape: {X.shape}")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y
)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y)
n_pos = (y_train == 1).sum()
n_neg = (y_train == 0).sum()
@@ -300,6 +303,7 @@ if __name__ == "__main__":
try:
model, X_test, y_test = train_process(X, y, final_features)
evaluate_and_save(model, X_test, y_test)
except KeyboardInterrupt:
print("\n\n[-] Training aborted.")
except Exception as e:
+8 -4
View File
@@ -12,6 +12,7 @@ from sklearn.metrics import accuracy_score, roc_auc_score
try:
import orjson
JSON_LOADS = orjson.loads
except ImportError:
JSON_LOADS = json.loads
@@ -21,9 +22,9 @@ JSONL_PATH = "pe_features.jsonl"
MODEL_FILE = "model.txt"
ONNX_FILE = "Pefile_General_T1.onnx"
FEATURE_FILE = "features.json"
TEST_SIZE = 0.0001
RANDOM_SEED = 42
DLL_HASH_DIM = 256
API_HASH_DIM = 1024
@@ -56,12 +57,14 @@ def analyze_schema(jsonl_path):
with open(jsonl_path, 'rb') as f:
for line in f:
num_lines += 1
if not base_keys:
try:
data = JSON_LOADS(line)
base_data = data.get('Base')
if base_data:
base_keys.update(base_data.keys())
except Exception:
pass
@@ -76,6 +79,7 @@ def analyze_schema(jsonl_path):
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
print(f"[*] Stage 2: Building sparse feature matrix dynamically via Multiprocessing...")
final_features = base_schema + dll_schema + api_schema
num_features = len(final_features)
offset_dll = len(base_schema)
offset_api = offset_dll + DLL_HASH_DIM
@@ -118,6 +122,7 @@ def _chunk_worker(args):
if f.read(1) != b'\n':
f.seek(start_byte)
f.readline()
else:
f.seek(start_byte)
else:
@@ -181,9 +186,7 @@ def _calculate_dynamic_lr(current_iter: int) -> float:
def train_process(X, y, feature_names):
print(f"[*] Dataset shape: {X.shape}")
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y
)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y)
n_pos = (y_train == 1).sum()
n_neg = (y_train == 0).sum()
@@ -300,6 +303,7 @@ if __name__ == "__main__":
try:
model, X_test, y_test = train_process(X, y, final_features)
evaluate_and_save(model, X_test, y_test)
except KeyboardInterrupt:
print("\n\n[-] Training aborted.")
except Exception as e:
+253 -11
View File
@@ -157,7 +157,29 @@ window.AppI18n = {
"col_conn_count": "連接數",
"btn_extract_item": "提取項目",
"msg_extract_success": "成功提取選取項目",
"msg_key_activated": "產品金鑰已啟用"
"msg_key_activated": "產品金鑰已啟用",
"INFO": "資訊",
"WARN": "警告",
"BLOCK": "攔截",
"SCAN": "掃描",
"System": "系統",
"Config Update": "設定更新",
"File Extract": "檔案提取",
"Load Engine": "載入引擎",
"Scan Completed": "掃描完成",
"Scan Engine": "掃描引擎",
"Virus Detected": "發現病毒",
"Virus Delete": "刪除病毒",
"Cloud API": "雲端 API",
"MBR Repaired": "MBR 修復",
"System Repair": "系統修復",
"Process Block": "進程攔截",
"Process DLL Block": "進程 DLL 攔截",
"File Block": "檔案攔截",
"Network Block": "網路攔截",
"Driver Block": "驅動攔截",
"System Restart": "系統重啟",
"Clean Junk": "清理垃圾"
},
"simplified_switch": {
"app_name": "PYAS Security",
@@ -316,7 +338,29 @@ window.AppI18n = {
"col_conn_count": "连接数",
"btn_extract_item": "提取项目",
"msg_extract_success": "成功提取选取项目",
"msg_key_activated": "产品密钥已启用"
"msg_key_activated": "产品密钥已启用",
"INFO": "信息",
"WARN": "警告",
"BLOCK": "拦截",
"SCAN": "扫描",
"System": "系统",
"Config Update": "设置更新",
"File Extract": "文件提取",
"Load Engine": "加载引擎",
"Scan Completed": "扫描完成",
"Scan Engine": "扫描引擎",
"Virus Detected": "发现病毒",
"Virus Delete": "删除病毒",
"Cloud API": "云端 API",
"MBR Repaired": "MBR 修复",
"System Repair": "系统修复",
"Process Block": "进程拦截",
"Process DLL Block": "进程 DLL 拦截",
"File Block": "文件拦截",
"Network Block": "网络拦截",
"Driver Block": "驱动拦截",
"System Restart": "系统重启",
"Clean Junk": "清理垃圾"
},
"english_switch": {
"app_name": "PYAS Security",
@@ -475,7 +519,29 @@ window.AppI18n = {
"col_conn_count": "Connections",
"btn_extract_item": "Extract Item",
"msg_extract_success": "Successfully extracted selected items",
"msg_key_activated": "Product key activated"
"msg_key_activated": "Product key activated",
"INFO": "INFO",
"WARN": "WARN",
"BLOCK": "BLOCK",
"SCAN": "SCAN",
"System": "System",
"Config Update": "Config Update",
"File Extract": "File Extract",
"Load Engine": "Load Engine",
"Scan Completed": "Scan Completed",
"Scan Engine": "Scan Engine",
"Virus Detected": "Virus Detected",
"Virus Delete": "Virus Delete",
"Cloud API": "Cloud API",
"MBR Repaired": "MBR Repaired",
"System Repair": "System Repair",
"Process Block": "Process Block",
"Process DLL Block": "Process DLL Block",
"File Block": "File Block",
"Network Block": "Network Block",
"Driver Block": "Driver Block",
"System Restart": "System Restart",
"Clean Junk": "Clean Junk"
},
"japanese_switch": {
"app_name": "PYAS Security",
@@ -634,7 +700,29 @@ window.AppI18n = {
"col_conn_count": "接続数",
"btn_extract_item": "アイテムを抽出",
"msg_extract_success": "選択したアイテムを正常に抽出しました",
"msg_key_activated": "プロダクトキーが有効化されました"
"msg_key_activated": "プロダクトキーが有効化されました",
"INFO": "情報",
"WARN": "警告",
"BLOCK": "ブロック",
"SCAN": "スキャン",
"System": "システム",
"Config Update": "設定の更新",
"File Extract": "ファイルの抽出",
"Load Engine": "エンジンのロード",
"Scan Completed": "スキャン完了",
"Scan Engine": "スキャンエンジン",
"Virus Detected": "ウイルス検出",
"Virus Delete": "ウイルス削除",
"Cloud API": "クラウド API",
"MBR Repaired": "MBR 修復",
"System Repair": "システム修復",
"Process Block": "プロセスブロック",
"Process DLL Block": "プロセス DLL ブロック",
"File Block": "ファイルブロック",
"Network Block": "ネットワークブロック",
"Driver Block": "ドライバーブロック",
"System Restart": "システムの再起動",
"Clean Junk": "ジャンククリーンアップ"
},
"korean_switch": {
"app_name": "PYAS Security",
@@ -793,7 +881,29 @@ window.AppI18n = {
"col_conn_count": "연결 수",
"btn_extract_item": "항목 추출",
"msg_extract_success": "선택한 항목을 성공적으로 추출했습니다",
"msg_key_activated": "제품 키가 활성화되었습니다"
"msg_key_activated": "제품 키가 활성화되었습니다",
"INFO": "정보",
"WARN": "경고",
"BLOCK": "차단",
"SCAN": "스캔",
"System": "시스템",
"Config Update": "설정 업데이트",
"File Extract": "파일 추출",
"Load Engine": "엔진 로드",
"Scan Completed": "스캔 완료",
"Scan Engine": "스캔 엔진",
"Virus Detected": "바이러스 발견",
"Virus Delete": "바이러스 삭제",
"Cloud API": "클라우드 API",
"MBR Repaired": "MBR 복구",
"System Repair": "시스템 복구",
"Process Block": "프로세스 차단",
"Process DLL Block": "프로세스 DLL 차단",
"File Block": "파일 차단",
"Network Block": "네트워크 차단",
"Driver Block": "드라이버 차단",
"System Restart": "시스템 재시작",
"Clean Junk": "정크 정리"
},
"french_switch": {
"app_name": "PYAS Security",
@@ -952,7 +1062,29 @@ window.AppI18n = {
"col_conn_count": "Connexions",
"btn_extract_item": "Extraire l'élément",
"msg_extract_success": "Éléments sélectionnés extraits avec succès",
"msg_key_activated": "Clé de produit activée"
"msg_key_activated": "Clé de produit activée",
"INFO": "INFO",
"WARN": "AVERT",
"BLOCK": "BLOCAGE",
"SCAN": "ANALYSE",
"System": "Système",
"Config Update": "Mise à jour config",
"File Extract": "Extraction de fichier",
"Load Engine": "Chargement moteur",
"Scan Completed": "Analyse terminée",
"Scan Engine": "Moteur d'analyse",
"Virus Detected": "Virus détecté",
"Virus Delete": "Suppression de virus",
"Cloud API": "API Cloud",
"MBR Repaired": "MBR réparé",
"System Repair": "Réparation système",
"Process Block": "Processus bloqué",
"Process DLL Block": "Blocage DLL Processus",
"File Block": "Fichier bloqué",
"Network Block": "Réseau bloqué",
"Driver Block": "Pilote bloqué",
"System Restart": "Redémarrage système",
"Clean Junk": "Nettoyage"
},
"spanish_switch": {
"app_name": "PYAS Security",
@@ -1111,7 +1243,29 @@ window.AppI18n = {
"col_conn_count": "Conexiones",
"btn_extract_item": "Extraer elemento",
"msg_extract_success": "Elementos seleccionados extraídos con éxito",
"msg_key_activated": "Clave de producto activada"
"msg_key_activated": "Clave de producto activada",
"INFO": "INFO",
"WARN": "ADVERT",
"BLOCK": "BLOQUEO",
"SCAN": "ESCANEO",
"System": "Sistema",
"Config Update": "Actualización config",
"File Extract": "Extracción de archivo",
"Load Engine": "Cargar motor",
"Scan Completed": "Escaneo completado",
"Scan Engine": "Motor de escaneo",
"Virus Detected": "Virus detectado",
"Virus Delete": "Eliminación de virus",
"Cloud API": "API en la nube",
"MBR Repaired": "MBR reparado",
"System Repair": "Reparación del sistema",
"Process Block": "Proceso bloqueado",
"Process DLL Block": "Bloqueo de DLL de proceso",
"File Block": "Archivo bloqueado",
"Network Block": "Red bloqueada",
"Driver Block": "Controlador bloqueado",
"System Restart": "Reinicio del sistema",
"Clean Junk": "Limpieza de basura"
},
"hindi_switch": {
"app_name": "PYAS Security",
@@ -1270,7 +1424,29 @@ window.AppI18n = {
"col_conn_count": "कनेक्शन",
"btn_extract_item": "आइटम निकालें",
"msg_extract_success": "चयनित आइटम सफलतापूर्वक निकाले गए",
"msg_key_activated": "उत्पाद कुंजी सक्रिय हो गई"
"msg_key_activated": "उत्पाद कुंजी सक्रिय हो गई",
"INFO": "जानकारी",
"WARN": "चेतावनी",
"BLOCK": "अवरोध",
"SCAN": "स्कैन",
"System": "सिस्टम",
"Config Update": "कॉन्फ़िगरेशन अपडेट",
"File Extract": "फ़ाइल निकालें",
"Load Engine": "इंजन लोड",
"Scan Completed": "स्कैन पूरा हुआ",
"Scan Engine": "स्कैन इंजन",
"Virus Detected": "वायरस का पता चला",
"Virus Delete": "वायरस हटाएँ",
"Cloud API": "क्लाउड API",
"MBR Repaired": "MBR मरम्मत",
"System Repair": "सिस्टम मरम्मत",
"Process Block": "प्रक्रिया अवरुद्ध",
"Process DLL Block": "प्रक्रिया DLL अवरोध",
"File Block": "फ़ाइल अवरुद्ध",
"Network Block": "नेटवर्क अवरुद्ध",
"Driver Block": "ड्राइवर अवरुद्ध",
"System Restart": "सिस्टम पुनरारंभ",
"Clean Junk": "कचरा साफ़ करें"
},
"arabic_switch": {
"app_name": "PYAS Security",
@@ -1429,7 +1605,29 @@ window.AppI18n = {
"col_conn_count": "الاتصالات",
"btn_extract_item": "استخراج العنصر",
"msg_extract_success": "تم استخراج العناصر المحددة بنجاح",
"msg_key_activated": "تم تنشيط مفتاح المنتج"
"msg_key_activated": "تم تنشيط مفتاح المنتج",
"INFO": "معلومات",
"WARN": "تحذير",
"BLOCK": "حظر",
"SCAN": "فحص",
"System": "النظام",
"Config Update": "تحديث التكوين",
"File Extract": "استخراج ملف",
"Load Engine": "تحميل المحرك",
"Scan Completed": "اكتمل الفحص",
"Scan Engine": "محرك الفحص",
"Virus Detected": "تم اكتشاف فيروس",
"Virus Delete": "حذف فيروس",
"Cloud API": "واجهة السحابة",
"MBR Repaired": "تم إصلاح MBR",
"System Repair": "إصلاح النظام",
"Process Block": "حظر عملية",
"Process DLL Block": "حظر DLL للعملية",
"File Block": "حظر ملف",
"Network Block": "حظر شبكة",
"Driver Block": "حظر محرك",
"System Restart": "إعادة تشغيل النظام",
"Clean Junk": "تنظيف المخلفات"
},
"russian_switch": {
"app_name": "PYAS Security",
@@ -1588,7 +1786,29 @@ window.AppI18n = {
"col_conn_count": "Соединения",
"btn_extract_item": "Извлечь элемент",
"msg_extract_success": "Выбранные элементы успешно извлечены",
"msg_key_activated": "Ключ продукта активирован"
"msg_key_activated": "Ключ продукта активирован",
"INFO": "ИНФО",
"WARN": "ПРЕДУПРЕЖДЕНИЕ",
"BLOCK": "БЛОКИРОВКА",
"SCAN": "СКАНИРОВАНИЕ",
"System": "Система",
"Config Update": "Обновление конфигурации",
"File Extract": "Извлечение файла",
"Load Engine": "Загрузка движка",
"Scan Completed": "Сканирование завершено",
"Scan Engine": "Движок сканирования",
"Virus Detected": "Обнаружен вирус",
"Virus Delete": "Удаление вируса",
"Cloud API": "Облачный API",
"MBR Repaired": "MBR восстановлен",
"System Repair": "Восстановление системы",
"Process Block": "Блокировка процесса",
"Process DLL Block": "Блокировка DLL процесса",
"File Block": "Блокировка файла",
"Network Block": "Блокировка сети",
"Driver Block": "Блокировка драйвера",
"System Restart": "Перезапуск системы",
"Clean Junk": "Очистка мусора"
},
"slovenian_switch": {
"app_name": "PYAS Security",
@@ -1747,7 +1967,29 @@ window.AppI18n = {
"col_conn_count": "Povezave",
"btn_extract_item": "Izvleči element",
"msg_extract_success": "Izbrani elementi so bili uspešno izvlečeni",
"msg_key_activated": "Ključ izdelka je aktiviran"
"msg_key_activated": "Ključ izdelka je aktiviran",
"INFO": "INFO",
"WARN": "OPOZORILO",
"BLOCK": "BLOKADA",
"SCAN": "SKENIRANJE",
"System": "Sistem",
"Config Update": "Posodobitev konfiguracije",
"File Extract": "Izvleček datoteke",
"Load Engine": "Nalaganje mehanizma",
"Scan Completed": "Skeniranje končano",
"Scan Engine": "Mehanizem skeniranja",
"Virus Detected": "Zaznan virus",
"Virus Delete": "Izbris virusa",
"Cloud API": "Oblačni API",
"MBR Repaired": "MBR popravljen",
"System Repair": "Popravilo sistema",
"Process Block": "Proces blokiran",
"Process DLL Block": "Blokada procesne DLL",
"File Block": "Datoteka blokirana",
"Network Block": "Omrežje blokirano",
"Driver Block": "Gonilnik blokiran",
"System Restart": "Ponovni zagon sistema",
"Clean Junk": "Čiščenje nesnage"
}
},
langMap: {
+18 -6
View File
@@ -80,7 +80,7 @@ document.addEventListener('DOMContentLoaded', () => {
};
const cols = {
log: [{key: 'time_str', label: 'col_date', flex: 1.5}, {key: 'level', label: 'col_type', flex: 0.8}, {key: 'action', label: 'col_func', flex: 1.2, isI18n: true}, {key: 'source', label: 'col_path', flex: 3}],
log: [{key: 'time_str', label: 'col_date', flex: 1.5}, {key: 'level', label: 'col_type', flex: 0.8, isI18n: true}, {key: 'action', label: 'col_func', flex: 1.2, isI18n: true}, {key: 'source', label: 'col_path', flex: 3}],
process: [{key: 'name', label: 'col_name', flex: 1.5}, {key: 'pid', label: 'col_pid', flex: 0.5}, {key: 'path', label: 'col_path', flex: 3}],
startup: [{key: 'type', label: 'col_type', flex: 0.5, isI18n: true}, {key: 'name', label: 'col_name', flex: 1.5}, {key: 'status', label: 'col_status', flex: 0.5, isI18n: true}, {key: 'path', label: 'col_path', flex: 3}],
virus: [{key: 'label', label: 'col_type', flex: 1, isI18n: true}, {key: 'path', label: 'col_path', flex: 3}],
@@ -341,7 +341,15 @@ document.addEventListener('DOMContentLoaded', () => {
if (selectAllCb) selectAllCb.disabled = dataList.length === 0;
const ft = filterText.toLowerCase();
let displayData = dataList.filter(item => !filterText || columns.some(col => String(item[col.key] || '').toLowerCase().includes(ft)));
let displayData = dataList.filter(item => !filterText || columns.some(col => {
const rawVal = String(item[col.key] || '');
if (rawVal.toLowerCase().includes(ft)) return true;
if (col.isI18n) {
const transVal = getMsg(rawVal);
if (transVal && transVal.toLowerCase().includes(ft)) return true;
}
return false;
}));
displayData.sort((a, b) => {
const valA = a[state.sortKey] ?? '', valB = b[state.sortKey] ?? '';
@@ -510,7 +518,7 @@ document.addEventListener('DOMContentLoaded', () => {
const fetchProcs = () => {
if (!appState.taskmgrActive || !window.pywebview) return;
const widget = document.querySelector('#taskmgr_window .manage-widget');
const blockFetch = widget && (widget.gridState?.checkedSet.size > 0 || widget.matches(':hover')) || document.querySelector('.custom-context-menu.show') || document.body.classList.contains('resizing-active');
const blockFetch = (widget && widget.gridState?.checkedSet.size > 0) || document.querySelector('.custom-context-menu.show') || document.body.classList.contains('resizing-active');
if (!blockFetch) {
window.pywebview.api.get_process_list().then(procs => {
@@ -526,7 +534,7 @@ document.addEventListener('DOMContentLoaded', () => {
const fetchNetmon = () => {
if (!appState.netmonActive || !window.pywebview) return;
const widget = document.querySelector('#netmon_window .manage-widget');
const blockFetch = widget && (widget.gridState?.checkedSet.size > 0 || widget.matches(':hover')) || document.querySelector('.custom-context-menu.show') || document.body.classList.contains('resizing-active');
const blockFetch = (widget && widget.gridState?.checkedSet.size > 0) || document.querySelector('.custom-context-menu.show') || document.body.classList.contains('resizing-active');
if (!blockFetch) {
window.pywebview.api.get_traffic_list().then(list => {
@@ -684,13 +692,17 @@ document.addEventListener('DOMContentLoaded', () => {
window.updateLogs = (entry) => {
const logWidget = document.querySelector('.log-text');
if (logWidget) {
let parts = [`[${entry.time_str}]`, entry.level, getMsg(entry.action || '')];
let parts = [`[${entry.time_str}]`, getMsg(entry.level || ''), getMsg(entry.action || '')];
if (entry.source) parts.push(`Src: ${entry.source}`);
if (entry.target) parts.push(`Tgt: ${entry.target}`);
if (entry.code) parts.push(`Code: ${entry.code}`);
if (entry.pid) parts.push(`PID: ${entry.pid}`);
if (entry.hash) parts.push(`Hash: ${entry.hash}`);
if (entry.detail) parts.push(`Detail: ${entry.detail}`);
if (entry.detail) {
const transDetail = getMsg(entry.detail);
parts.push(`Detail: ${transDetail !== entry.detail ? transDetail : entry.detail}`);
}
if (entry.operate !== null) parts.push(`Op: ${entry.operate}`);
parts.push(`Success: ${entry.success}`);
let val = logWidget.value + parts.join(' | ') + '\n';
+1132 -427
View File
File diff suppressed because it is too large. Load diff
+68 -47
View File
@@ -57,6 +57,7 @@ class sign_scanner:
setattr(self, name.lower(), ctypes.WinDLL(name, use_last_error=True))
except Exception:
pass
try:
self.WinVerifyTrust = self.wintrust.WinVerifyTrust
self.WinVerifyTrust.restype = ctypes.wintypes.LONG
@@ -75,8 +76,10 @@ class sign_scanner:
fi.pcwszFilePath = os.path.abspath(file_path)
fi.hFile = None
fi.pgKnownSubject = None
wt_union = WINTRUST_DATA_UNION()
wt_union.pFile = ctypes.pointer(fi)
td = WINTRUST_DATA()
td.cbStruct = ctypes.sizeof(WINTRUST_DATA)
td.pPolicyCallbackData = None
@@ -91,6 +94,7 @@ class sign_scanner:
td.dwProvFlags = 0
td.dwUIContext = 0
td.pSignatureSettings = None
return self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(td)) == 0
except Exception:
return False
@@ -106,15 +110,19 @@ class rule_scanner:
yara_files = {}
for root, _, files in os.walk(path):
for file in files:
full_path = os.path.join(root, file)
if callback:
callback(full_path)
ext = os.path.splitext(file)[1].lower()
if ext in ('.yara', '.yar'):
namespace = os.path.relpath(full_path, path).replace(os.sep, '_')
yara_files[namespace] = full_path
elif ext in ('.yc', '.yrc'):
self.load_compiled_rule(full_path)
elif ext in ('.ip', '.txt'):
self.load_network_list(full_path)
@@ -155,6 +163,7 @@ class rule_scanner:
label = rule_name.split("_")[0]
level = rule_name.split("_")[-1]
return f"{label}:WinPE/Unknown.{level}!yr", level
return False, False
except Exception:
return False, False
@@ -170,6 +179,7 @@ class rule_scanner:
label = rule_name.split("_")[0]
level = rule_name.split("_")[-1]
return f"{label}:WinPE/Unknown.{level}!ym", level
return False, False
except Exception:
return False, False
@@ -183,6 +193,7 @@ class pe_scanner:
_BYTE_ENT_KEYS = [f'ByteEnt_{i:02X}' for i in range(256)]
_STRING_KEYS = [f'StringHist_{i:02d}' for i in range(95)]
_SECTION_HASH_KEYS = [f'SectionHash_{i:02d}' for i in range(50)]
_CHAR_FLAGS = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
_CHAR_COUNT_KEYS = {flag: f'Char_{flag:08X}_Count' for flag in _CHAR_FLAGS}
_CHAR_ENT_KEYS = {flag: f'Char_{flag:08X}_MeanEntropy' for flag in _CHAR_FLAGS}
@@ -201,21 +212,26 @@ class pe_scanner:
for root, _, files in os.walk(path):
for file in files:
full_path = os.path.join(root, file)
if callback:
callback(full_path)
if full_path.endswith('.onnx'):
self.load_model(full_path)
def load_model(self, model_path):
if not os.path.exists(model_path):
return
try:
self.model = onnxruntime.InferenceSession(model_path, providers=['CPUExecutionProvider'])
self.input_name = self.model.get_inputs()[0].name
feat_path = os.path.join(os.path.dirname(model_path), "features.json")
if os.path.exists(feat_path):
with open(feat_path, 'r', encoding='utf-8') as f:
self.feature_order = json.load(f)
except Exception:
pass
@@ -225,16 +241,19 @@ class pe_scanner:
if math.isinf(f) or math.isnan(f):
return 0.0
return f
except Exception:
return 0.0
def _calc_entropy(self, data):
if not data:
return 0.0
arr = numpy.frombuffer(data, dtype=numpy.uint8)
sz = len(arr)
counts = numpy.bincount(arr, minlength=256)
counts = counts[counts > 0]
return float(numpy.log2(sz) - numpy.sum(counts * numpy.log2(counts)) / sz)
def _extract_strings(self, file_bytes):
@@ -342,6 +361,7 @@ class pe_scanner:
def _extract_rich_header(self, pe):
if not hasattr(pe, 'RICH_HEADER') or not pe.RICH_HEADER:
return {"HasRichHeader": 0.0, "RichHeaderCount": 0.0}
return {
"HasRichHeader": 1.0,
"RichHeaderCount": float(len(pe.RICH_HEADER.values) // 2) if hasattr(pe.RICH_HEADER, 'values') else 0.0
@@ -398,6 +418,7 @@ class pe_scanner:
for entry_lang in entry_id.directory.entries:
if hasattr(entry_lang, 'id'):
langs.add(entry_lang.id)
if hasattr(entry_lang, 'data'):
try:
data = pe.get_data(entry_lang.data.struct.OffsetToData, entry_lang.data.struct.Size)
@@ -417,13 +438,16 @@ class pe_scanner:
def _extract_load_config(self, pe):
cfg = {"HasLoadConfig": 0.0, "HasCFG": 0.0, "HasSEHTable": 0.0}
if hasattr(pe, 'DIRECTORY_ENTRY_LOAD_CONFIG'):
cfg["HasLoadConfig"] = 1.0
struct = pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct
if getattr(struct, 'GuardCFFunctionTable', 0) != 0:
cfg["HasCFG"] = 1.0
if getattr(struct, 'SEHandlerTable', 0) != 0:
cfg["HasSEHTable"] = 1.0
return cfg
def _extract_security_directory(self, pe, file_bytes, fsize):
@@ -455,6 +479,7 @@ class pe_scanner:
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
if dw_length < 8:
break
count += 1
curr += (dw_length + 7) & ~7
@@ -508,7 +533,6 @@ class pe_scanner:
base['Is64Bit'] = 1.0 if base['Machine'] in (0x8664, 0xAA64, 0x0200) else 0.0
base['IsExe'] = 1.0 if pe.is_exe() else 0.0
base['IsDll'] = 1.0 if pe.is_dll() else 0.0
base['ExceptionCount'] = 0.0
if hasattr(pe, 'OPTIONAL_HEADER'):
@@ -563,10 +587,14 @@ class pe_scanner:
raw_sizes.append(section.SizeOfRawData)
v_sizes.append(section.Misc_VirtualSize)
if section.Characteristics & 0x20000000: exec_sec += 1
if section.Characteristics & 0x80000000: write_sec += 1
if section.Characteristics & 0x40000000: read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize: sec_exc = 1
if section.Characteristics & 0x20000000:
exec_sec += 1
if section.Characteristics & 0x80000000:
write_sec += 1
if section.Characteristics & 0x40000000:
read_sec += 1
if section.SizeOfRawData + section.PointerToRawData > fsize:
sec_exc = 1
for flag in self._CHAR_FLAGS:
if section.Characteristics & flag:
@@ -605,12 +633,14 @@ class pe_scanner:
for fileinfo in fileinfo_list:
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
for st in getattr(fileinfo, 'StringTable', []):
for key, val in st.entries.items():
try:
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
if k in string_keys:
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
base[f'{k}Length'] = float(len(v))
except Exception:
continue
@@ -629,12 +659,14 @@ class pe_scanner:
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
base['ImportCount'] = float(len(pe.DIRECTORY_ENTRY_IMPORT))
func_count = 0
for entry in pe.DIRECTORY_ENTRY_IMPORT:
if getattr(entry, 'dll', None):
try:
dlls.add(entry.dll.decode('ascii', 'ignore').lower())
except Exception:
pass
for imp in getattr(entry, 'imports', []):
func_count += 1
if getattr(imp, 'name', None):
@@ -642,6 +674,7 @@ class pe_scanner:
apis.add(imp.name.decode('ascii', 'ignore'))
except Exception:
pass
base['ImportFunctionCount'] = float(func_count)
else:
base['ImportCount'] = 0.0
@@ -708,6 +741,7 @@ class pe_scanner:
old_fn = f"Dll_{d}"
if old_fn in feat_map:
vec[0, feat_map[old_fn]] = 1.0
h = zlib.crc32(d.encode('utf-8', 'ignore')) % 256
new_fn = f"DllHash_{h:03d}"
if new_fn in feat_map:
@@ -717,6 +751,7 @@ class pe_scanner:
old_fn = f"Api_{a}"
if old_fn in feat_map:
vec[0, feat_map[old_fn]] = 1.0
h = zlib.crc32(a.encode('utf-8', 'ignore')) % 1024
new_fn = f"ApiHash_{h:04d}"
if new_fn in feat_map:
@@ -730,6 +765,7 @@ class pe_scanner:
prob_dict = result[0]
if hasattr(prob_dict, 'get'):
prob = float(prob_dict.get(1, prob_dict.get('1', 0.0)))
elif isinstance(result, numpy.ndarray):
if result.ndim == 2 and result.shape[1] > 1:
prob = float(result[0][1])
@@ -749,67 +785,52 @@ class pe_scanner:
class cloud_scanner:
def __init__(self):
self.session = None
self.api_host = None
self.api_key = None
self.timeout = 30
self.lock = threading.RLock()
self.local = threading.local()
def _init_session(self, api_host, api_key):
with self.lock:
if self.session is None or self.api_host != api_host or self.api_key != api_key:
self.api_host = api_host.rstrip('/')
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({"X-API-Key": api_key, "User-Agent": "PYAS-Engine/1.1"})
def _get_session(self, api_host, api_key):
if not hasattr(self.local, 'session') or getattr(self.local, 'host', None) != api_host:
self.local.session = requests.Session()
self.local.session.headers.update({"X-API-Key": api_key, "User-Agent": "PYAS-Engine/1.1"})
self.local.host = api_host
def _request(self, method, endpoint, **kwargs):
with self.lock:
current_session = self.session
current_host = self.api_host
if not current_session:
return None
return self.local.session
def _request(self, method, endpoint, api_host, api_key, **kwargs):
session = self._get_session(api_host, api_key)
try:
r = current_session.request(method, f"{current_host}{endpoint}", timeout=self.timeout, **kwargs)
r = session.request(method, f"{api_host}{endpoint}", timeout=self.timeout, **kwargs)
if r.status_code == 200:
return r
except Exception:
pass
return None
def calc_hash(self, file_path, block_size=65536):
try:
h = hashlib.sha256()
with open(file_path, "rb") as f:
for chunk in iter(lambda: f.read(block_size), b""):
h.update(chunk)
return h.hexdigest()
except Exception:
return ""
####################################################################################################
def rescan(self, sha256, api_host, api_key):
self._init_session(api_host, api_key)
r = self._request("POST", f"/api/rescan/{sha256}")
r = self._request("POST", f"/api/rescan/{sha256}", api_host, api_key)
return r is not None and r.json().get('status') == 'success'
def upload_file(self, file_path, api_host, api_key, chunk_size=4194304, need_rescan=False, max_retries=3):
def upload_file(self, file_path, api_host, api_key, chunk_size=4194304, need_rescan=False, max_retries=3, file_hash=None):
try:
self._init_session(api_host, api_key)
sha256 = self.calc_hash(file_path)
sha256 = file_hash
if not sha256:
return False, None
status_req = self._request("GET", f"/api/processing_status/{sha256}")
status_req = self._request("GET", f"/api/processing_status/{sha256}", api_host, api_key)
if status_req:
current_status = status_req.json().get('status')
if current_status == 'done':
if need_rescan:
self.rescan(sha256, api_host, api_key)
return True, sha256
elif current_status in ('queued', 'processing'):
return True, sha256
@@ -823,15 +844,11 @@ class cloud_scanner:
with open(file_path, 'rb') as f:
for i in range(total_chunks):
chunk_data = f.read(chunk_size)
headers = {
"X-Chunk-Index": str(i),
"X-Total-Chunks": str(total_chunks),
"X-Upload-ID": upload_id
}
headers = {"X-Chunk-Index": str(i), "X-Total-Chunks": str(total_chunks), "X-Upload-ID": upload_id}
chunk_success = False
for attempt in range(max_retries):
r = self._request("POST", "/api/upload", files={'file': (os.path.basename(file_path), chunk_data)}, headers=headers)
r = self._request("POST", "/api/upload", api_host, api_key, files={'file': (os.path.basename(file_path), chunk_data)}, headers=headers)
if r:
if i == total_chunks - 1:
try:
@@ -840,6 +857,7 @@ class cloud_scanner:
sha256 = resp.get('url', '').split('/')[-1]
except Exception:
pass
chunk_success = True
break
time.sleep(2 ** attempt)
@@ -857,24 +875,25 @@ class cloud_scanner:
try:
if not sha256:
return False
self._init_session(api_host, api_key)
is_done = False
for _ in range(max_retries):
r = self._request("GET", f"/api/processing_status/{sha256}")
r = self._request("GET", f"/api/processing_status/{sha256}", api_host, api_key)
if r:
st = r.json().get('status', 'error')
if st == 'done':
is_done = True
break
if st in ['error', 'failed']:
return False
time.sleep(interval)
if not is_done:
return False
r = self._request("GET", f"/api/report/{sha256}")
r = self._request("GET", f"/api/report/{sha256}", api_host, api_key)
if r:
data = r.json().get('data', {})
metadata = data.get('metadata', {})
@@ -885,6 +904,7 @@ class cloud_scanner:
is_malicious = 'General' in label
sim_malicious_count = 0
valid_sim_count = 0
for s in sims:
if s.get('similarity', 0) > 80:
valid_sim_count += 1
@@ -893,6 +913,7 @@ class cloud_scanner:
if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count):
return f"General:WinPE/Malware.{score}!cl"
except Exception:
pass
return False
+253
View File
@@ -0,0 +1,253 @@
#define AppId "{{a7d7bac3-93b8-4630-8308-c7a56bf7fdf4}"
#define AppName "PYAS"
#define AppVersion "3.5.9.0"
#define AppPublisher "PYAS Security"
#define AppURL "https://github.com/87owo/PYAS"
#define AppExeName "PYAS.exe"
[Setup]
AppId={#AppId}
AppName={#AppName}
AppVersion={#AppVersion}
AppVerName={#AppName} {#AppVersion}
AppPublisher={#AppPublisher}
AppPublisherURL={#AppURL}
AppSupportURL={#AppURL}
AppUpdatesURL={#AppURL}
VersionInfoVersion={#AppVersion}
VersionInfoCompany={#AppPublisher}
VersionInfoDescription={#AppName} Setup
VersionInfoProductName={#AppName} Setup
VersionInfoProductVersion={#AppVersion}
DefaultDirName={autopf}\{#AppName}
DefaultGroupName={#AppPublisher}\{#AppName}
AllowNoIcons=yes
LicenseFile=SetupResources\licence.rtf
ShowLanguageDialog=yes
WizardStyle=modern
WizardImageFile=SetupResources\wizardImage.bmp
WizardSmallImageFile=SetupResources\headerImage.png
SetupIconFile=Payload\Interface\static\img\icon.ico
UninstallDisplayIcon={app}\{#AppExeName}
PrivilegesRequired=admin
ArchitecturesAllowed=x64compatible
ArchitecturesInstallIn64BitMode=x64compatible
MinVersion=10.0
Compression=lzma2/max
SolidCompression=yes
OutputDir=Output
OutputBaseFilename=PYAS_Setup
UsePreviousTasks=no
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "chinesesimplified"; MessagesFile: "SetupResources\ChineseSimplified.isl"
Name: "chinesetraditional"; MessagesFile: "SetupResources\ChineseTraditional.isl"
Name: "japanese"; MessagesFile: "SetupResources\Japanese.isl"
Name: "korean"; MessagesFile: "SetupResources\Korean.isl"
Name: "french"; MessagesFile: "SetupResources\French.isl"
Name: "spanish"; MessagesFile: "SetupResources\Spanish.isl"
Name: "hindi"; MessagesFile: "SetupResources\Hindi.isl"
Name: "arabic"; MessagesFile: "SetupResources\Arabic.isl"
Name: "russian"; MessagesFile: "SetupResources\Russian.isl"
Name: "slovenian"; MessagesFile: "SetupResources\Slovenian.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"
Name: "autostart"; Description: "{cm:AutoStartTask}"; GroupDescription: "{cm:AdditionalIcons}"
Name: "install_webview2"; Description: "{cm:InstallWebView2}"; GroupDescription: "{cm:Dependencies}"
Name: "install_vcredist"; Description: "{cm:InstallVCRedist}"; GroupDescription: "{cm:Dependencies}"
[Files]
Source: "Payload\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "Redist\VC_redist.x64.exe"; DestDir: "{tmp}\PYAS_Redist"; Flags: deleteafterinstall ignoreversion
Source: "Redist\MicrosoftEdgeWebview2Setup.exe"; DestDir: "{tmp}\PYAS_Redist"; Flags: deleteafterinstall ignoreversion
[Registry]
Root: HKCU; Subkey: "Software\Classes\*\shell\PYAS_Scan"; Flags: uninsdeletekey dontcreatekey
Root: HKCU; Subkey: "Software\Classes\Directory\shell\PYAS_Scan"; Flags: uninsdeletekey dontcreatekey
[UninstallDelete]
Type: filesandordirs; Name: "{commonappdata}\PYAS"
[Icons]
Name: "{autodesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon
Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}"
Name: "{group}\Uninstall {#AppName}"; Filename: "{uninstallexe}"
[Run]
Filename: "{tmp}\PYAS_Redist\VC_redist.x64.exe"; Parameters: "/quiet /norestart"; Flags: waituntilterminated runhidden; StatusMsg: "{cm:InstallingVCRuntime}"; Tasks: install_vcredist
Filename: "{tmp}\PYAS_Redist\MicrosoftEdgeWebview2Setup.exe"; Parameters: "/silent /install"; Flags: waituntilterminated runhidden; StatusMsg: "{cm:InstallingWebView2Runtime}"; Tasks: install_webview2
Filename: "{app}\{#AppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(AppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent runascurrentuser
[CustomMessages]
english.InstallingVCRuntime=Installing Microsoft Visual C++ Runtime...
chinesesimplified.InstallingVCRuntime=正在安装 Microsoft Visual C++ 运行库...
chinesetraditional.InstallingVCRuntime=正在安裝 Microsoft Visual C++ 執行庫...
english.InstallingWebView2Runtime=Installing Microsoft Edge WebView2 Runtime...
chinesesimplified.InstallingWebView2Runtime=正在安装 Microsoft Edge WebView2 Runtime...
chinesetraditional.InstallingWebView2Runtime=正在安裝 Microsoft Edge WebView2 Runtime...
english.AutoStartTask=Run PYAS automatically at system startup
chinesesimplified.AutoStartTask=开机时自动运行 PYAS
chinesetraditional.AutoStartTask=開機時自動執行 PYAS
english.Dependencies=Dependencies:
chinesesimplified.Dependencies=运行环境:
chinesetraditional.Dependencies=執行環境:
english.InstallWebView2=Install Microsoft Edge WebView2 Runtime
chinesesimplified.InstallWebView2=安装 Microsoft Edge WebView2 运行环境
chinesetraditional.InstallWebView2=安裝 Microsoft Edge WebView2 執行環境
english.InstallVCRedist=Install Microsoft Visual C++ Runtime
chinesesimplified.InstallVCRedist=安装 Microsoft Visual C++ 运行库
chinesetraditional.InstallVCRedist=安裝 Microsoft Visual C++ 執行庫
english.LegacyVersionDetected=An older version of PYAS was detected. Please uninstall it manually before installing.
chinesesimplified.LegacyVersionDetected=检测到存在旧版 PYAS。请先手动卸载旧版后,再运行本安装程序。
chinesetraditional.LegacyVersionDetected=檢測到存在舊版 PYAS。請先手動卸載舊版後,再執行本安裝程式。
[Code]
var
TasksInitialized: Boolean;
function IsWebView2Installed: Boolean;
var
RegKeyPath: string;
Version: string;
begin
RegKeyPath := 'SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}';
Result := False;
if RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}', 'pv', Version) then
if Length(Version) > 0 then Result := True;
if not Result and RegQueryStringValue(HKCU, RegKeyPath, 'pv', Version) then
if Length(Version) > 0 then Result := True;
if not Result and RegQueryStringValue(HKLM, RegKeyPath, 'pv', Version) then
if Length(Version) > 0 then Result := True;
end;
function IsVCRedistInstalled: Boolean;
var
Bld: Cardinal;
begin
Result := False;
if RegQueryDWordValue(HKLM, 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64', 'Bld', Bld) then
if Bld > 0 then Result := True;
end;
procedure CurPageChanged(CurPageID: Integer);
var
I: Integer;
ItemText: string;
begin
if (CurPageID = wpSelectTasks) and not TasksInitialized then
begin
TasksInitialized := True;
for I := 0 to WizardForm.TasksList.Items.Count - 1 do
begin
ItemText := WizardForm.TasksList.Items[I];
if Pos('Visual C++', ItemText) > 0 then
WizardForm.TasksList.Checked[I] := not IsVCRedistInstalled
else if Pos('WebView2', ItemText) > 0 then
WizardForm.TasksList.Checked[I] := not IsWebView2Installed;
end;
end;
end;
procedure QuitOldInstance(InstallPath: string);
var
ResultCode: Integer;
ExePath: string;
begin
ExePath := InstallPath + '\{#AppExeName}';
if FileExists(ExePath) then
begin
Exec(ExePath, '-quit', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
Sleep(500);
Exec(ExpandConstant('{sys}\taskkill.exe'), '/F /IM {#AppExeName} /T', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;
end;
function InitializeSetup(): Boolean;
var
OldInstallPath: string;
LegacyPath: string;
begin
LegacyPath := ExpandConstant('{pf32}\{#AppName}');
if DirExists(LegacyPath) and FileExists(LegacyPath + '\{#AppExeName}') then
begin
MsgBox(CustomMessage('LegacyVersionDetected'), mbCriticalError, MB_OK);
Result := False;
Exit;
end;
if RegQueryStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + ExpandConstant('{#AppId}') + '_is1', 'InstallLocation', OldInstallPath) or
RegQueryStringValue(HKCU, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\' + ExpandConstant('{#AppId}') + '_is1', 'InstallLocation', OldInstallPath) then
begin
QuitOldInstance(OldInstallPath);
end;
Result := True;
end;
function InitializeUninstall(): Boolean;
begin
QuitOldInstance(ExpandConstant('{app}'));
Result := True;
end;
procedure TryCreateStartupTask;
var
ResultCode: Integer;
PSExe: string;
PSCommand: string;
AppPath: string;
begin
AppPath := ExpandConstant('{app}\{#AppExeName}');
PSExe := ExpandConstant('{sys}\WindowsPowerShell\v1.0\powershell.exe');
PSCommand := '-NoProfile -NonInteractive -ExecutionPolicy Bypass -WindowStyle Hidden -Command ' +
'$Action = New-ScheduledTaskAction -Execute ''' + AppPath + ''' -Argument ''-hide''; ' +
'$Trigger = New-ScheduledTaskTrigger -AtLogOn; ' +
'$Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries; ' +
'Register-ScheduledTask -TaskName ''PYAS_Security_ATS'' -Action $Action -Trigger $Trigger -Settings $Settings -RunLevel Highest -Force';
if not Exec(PSExe, PSCommand, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
Log(Format('Failed to create scheduled task, Exec error code: %d', [ResultCode]));
end
else if ResultCode <> 0 then
begin
Log(Format('powershell returned exit code %d while creating the scheduled task', [ResultCode]));
end;
end;
procedure TryDeleteStartupTask;
var
ResultCode: Integer;
begin
if not Exec(ExpandConstant('{sys}\schtasks.exe'), '/Delete /TN "PYAS_Security_ATS" /F', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then
begin
Log(Format('Failed to delete scheduled task, Exec error code: %d', [ResultCode]));
end
else if ResultCode <> 0 then
begin
Log(Format('schtasks returned exit code %d while deleting the scheduled task', [ResultCode]));
end;
end;
procedure CurStepChanged(CurStep: TSetupStep);
begin
if CurStep = ssPostInstall then
begin
if WizardIsTaskSelected('autostart') then
begin
TryCreateStartupTask;
end;
end;
end;
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
if CurUninstallStep = usUninstall then
begin
TryDeleteStartupTask;
end
else if CurUninstallStep = usPostUninstall then
begin
DelTree(ExpandConstant('{app}'), True, True, True);
end;
end;
+1 -1
View File
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
pyinstaller_versionfile.create_versionfile(
output_file="versionfile.txt",
version='3.5.8.0',
version='3.5.9.0',
company_name="PYAS Security",
file_description="PYAS Security Antivirus",
internal_name="PYAS",