Compare commits

..
41 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
87owo 7b9474521d Add files via upload 2026-06-01 17:37:51 +08:00
87owo 78c3f2c9b8 Add files via upload 2026-06-01 16:59:04 +08:00
87owo ad256abb7c Add files via upload 2026-06-01 13:59:39 +08:00
87owo 86d160ae5f Add files via upload 2026-06-01 12:55:09 +08:00
87owo fde6afb39e Add files via upload 2026-05-31 23:59:07 +08:00
87owo 7c3e78296a Add files via upload 2026-05-31 23:35:58 +08:00
87owo 9cc06ff767 Add files via upload 2026-05-31 23:09:51 +08:00
87owo 766b6e81d0 Add files via upload 2026-05-28 16:20:20 +08:00
87owo 0349e01629 Add files via upload 2026-05-28 15:53:52 +08:00
87owo 0bd9da2bbc Add files via upload 2026-05-28 14:31:24 +08:00
87owo f14c7c3f55 Add files via upload 2026-05-28 14:05:15 +08:00
87owo ef5793fb15 Add files via upload 2026-05-26 09:30:27 +08:00
87owo 9f22fe6d97 Add files via upload 2026-05-26 09:29:51 +08:00
87owo 3147d576ee Add files via upload 2026-05-25 22:53:18 +08:00
87owo 2ceec2588c Add files via upload 2026-05-25 14:19:26 +08:00
87owo 5b4d88b9c7 Add files via upload 2026-05-25 14:18:52 +08:00
87owo 55043cca36 Add files via upload 2026-05-24 02:44:05 +08:00
87owo 097ebe025f Add files via upload 2026-05-23 14:10:33 +08:00
87owo 9f92307f64 Add files via upload 2026-05-22 13:49:52 +08:00
87owo c220035fd9 Add files via upload 2026-05-21 00:27:56 +08:00
87owo d7b98d3cde Add files via upload 2026-05-21 00:27:38 +08:00
87owo 0ab876633f Add files via upload 2026-05-21 00:27:01 +08:00
87owo cabe76ca54 Add files via upload 2026-05-21 00:26:14 +08:00
87owo c57224e258 Add files via upload 2026-05-21 00:25:35 +08:00
87owo b56101d2e6 Add files via upload 2026-05-18 21:31:43 +08:00
87owo 16211c28d1 Add files via upload 2026-05-17 16:48:13 +08:00
87owo 139d3e0c3d Add files via upload 2026-05-17 16:47:14 +08:00
87owo f0652fc5ec Add files via upload 2026-05-17 01:32:52 +08:00
87owo 55489cd634 Add files via upload 2026-05-17 01:31:49 +08:00
87owo 742887c0e4 Add files via upload 2026-05-15 22:34:23 +08:00
87owo c3e5cd2c0d Add files via upload 2026-05-15 22:33:13 +08:00
87owo 63e125627c Update README.md 2026-05-15 14:39:15 +08:00
87owo c0189e9d05 Add files via upload 2026-05-14 22:31:46 +08:00
17 changed files with 3887 additions and 1655 deletions

No files matched your search

+253 -64
View File
@@ -1,11 +1,15 @@
import os, sys, math, time, datetime, pefile, hashlib
import ctypes, ctypes.wintypes, multiprocessing
import re, os, sys, math, time, zlib, datetime, hashlib
import ctypes, ctypes.wintypes, pefile, multiprocessing
import numpy as np
####################################################################################################
try:
import orjson
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
@@ -68,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]
@@ -107,22 +112,127 @@ def verify_signature(file_path):
####################################################################################################
class FeatureExtractor:
_STRING_PATTERN = re.compile(b'[\x20-\x7E]{5,}')
_BYTE_HIST_KEYS = [f'ByteHist_{i:02X}' for i in range(256)]
_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}
_C_LOG_C_TABLE = np.zeros(2049, dtype=np.float64)
_C_LOG_C_TABLE[1:] = np.arange(1, 2049) * np.log2(np.arange(1, 2049))
@staticmethod
def _safe_float(val):
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
import numpy as np
if not data:
return 0.0
arr = np.frombuffer(data, dtype=np.uint8)
sz = len(arr)
counts = np.bincount(arr, minlength=256)
probs = counts[counts > 0] / len(arr)
return float(-np.sum(probs * np.log2(probs)))
counts = counts[counts > 0]
return float(np.log2(sz) - np.sum(counts * np.log2(counts)) / sz)
@classmethod
def _extract_strings(cls, file_bytes):
res = {"StringCount": 0.0, "StringMeanLength": 0.0, "StringEntropy": 0.0}
res.update(dict.fromkeys(cls._STRING_KEYS, 0.0))
matches = cls._STRING_PATTERN.findall(file_bytes)
if not matches:
return res
combined_data = b''.join(matches)
count = len(matches)
total_len = len(combined_data)
res["StringCount"] = float(count)
res["StringMeanLength"] = float(total_len) / count
res["StringEntropy"] = cls._calc_entropy(combined_data)
arr = np.frombuffer(combined_data, dtype=np.uint8)
counts = np.bincount(arr, minlength=127)
char_counts = counts[0x20:0x7F]
res.update(dict(zip(cls._STRING_KEYS, char_counts.astype(np.float64) / total_len)))
return res
@classmethod
def _extract_histograms(cls, file_bytes):
arr = np.frombuffer(file_bytes, dtype=np.uint8)
sz = len(arr)
res = {}
if sz == 0:
res.update(dict.fromkeys(cls._BYTE_HIST_KEYS, 0.0))
res.update(dict.fromkeys(cls._BYTE_ENT_KEYS, 0.0))
return res
byte_counts = np.bincount(arr, minlength=256)
byte_hist = byte_counts.astype(np.float64) / sz
res.update(dict(zip(cls._BYTE_HIST_KEYS, byte_hist)))
ent_hist = np.zeros(256, dtype=np.float64)
window = 2048
step = 1024
if sz < window:
w_counts = byte_counts
p = w_counts[w_counts > 0] / float(sz)
entropy = float(-np.sum(p * np.log2(p)))
ent_bin = min(int(entropy * 2.0), 15)
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
for w_start in range(0, num_windows, batch_windows):
w_end = min(w_start + batch_windows, num_windows)
b_num = w_end - w_start
start_byte = w_start * step
end_byte = start_byte + b_num * step + step
b_arr = arr[start_byte:end_byte]
arr_2d = b_arr.reshape(b_num + 1, step)
offsets = (np.arange(b_num + 1, dtype=np.int32)[:, None] * 256)
flat_idx = (arr_2d.astype(np.int32) + offsets).ravel()
chunk_counts = np.bincount(flat_idx, minlength=(b_num + 1) * 256).reshape(b_num + 1, 256)
window_counts = chunk_counts[:-1] + chunk_counts[1:]
sum_c_log_c = np.sum(cls._C_LOG_C_TABLE[window_counts], axis=1)
entropies = 11.0 - sum_c_log_c / 2048.0
ent_bins = (entropies * 2.0).astype(np.int32)
np.clip(ent_bins, 0, 15, out=ent_bins)
byte_bins = window_counts.reshape(b_num, 16, 16).sum(axis=2)
flat_ent_bins = (ent_bins[:, None] * 16 + np.arange(16)).ravel()
np.add.at(ent_hist, flat_ent_bins, byte_bins.ravel())
ent_sum = np.sum(ent_hist)
if ent_sum > 0:
ent_hist /= ent_sum
res.update(dict(zip(cls._BYTE_ENT_KEYS, ent_hist)))
return res
@classmethod
def _extract_overlay_features(cls, pe, fsize):
@@ -146,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
@@ -204,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)
@@ -227,23 +339,77 @@ 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
def extract(cls, file_bytes, file_path, fsize):
if fsize == 0: return None
base, dlls, apis, pe = {}, set(), set(), None
def _extract_security_directory(cls, pe, file_bytes, fsize):
res = {"HasSignature": 0.0, "SignatureCount": 0.0}
if not hasattr(pe, 'OPTIONAL_HEADER') or not hasattr(pe.OPTIONAL_HEADER, 'DATA_DIRECTORY'):
return res
sec_idx = 4
directories = pe.OPTIONAL_HEADER.DATA_DIRECTORY
if len(directories) <= sec_idx:
return res
sec_dir = directories[sec_idx]
if sec_dir.Size == 0 or sec_dir.VirtualAddress == 0:
return res
res["HasSignature"] = 1.0
offset = sec_dir.VirtualAddress
size = sec_dir.Size
if offset + size > fsize or offset < 0 or size < 0:
return res
count = 0
curr = offset
end = offset + size
while curr + 8 <= end:
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
if dw_length < 8:
break
count += 1
curr += (dw_length + 7) & ~7
res["SignatureCount"] = float(count)
return res
@classmethod
def extract(cls, file_bytes, file_path, fsize):
if fsize == 0:
return None
base, dlls, apis, pe = {}, set(), set(), None
try:
pe = pefile.PE(data=file_bytes, fast_load=True)
base['TrustSigned'] = verify_signature(file_path)
try:
pe.parse_rich_header()
pe.parse_data_directories(directories=[
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']
])
except Exception:
pass
base['TrustSigned'] = float(verify_signature(file_path))
base['FileEntropy'] = cls._calc_entropy(file_bytes)
base['FileSize'] = fsize
base['FileSize'] = float(fsize)
fh = pe.FILE_HEADER
base['Machine'] = getattr(fh, 'Machine', 0)
@@ -260,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'):
@@ -278,10 +443,9 @@ class FeatureExtractor:
if len(op.DATA_DIRECTORY) > exc_idx and op.DATA_DIRECTORY[exc_idx].Size > 0:
base['ExceptionCount'] = op.DATA_DIRECTORY[exc_idx].Size // 12 if base['Machine'] in (0x8664, 0xAA64) else 0.0
char_flags = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
for flag in char_flags:
base[f'Char_{flag:08X}_Count'] = 0.0
base[f'Char_{flag:08X}_MeanEntropy'] = 0.0
res_flags_count = dict.fromkeys(cls._CHAR_COUNT_KEYS.values(), 0.0)
res_flags_ent = dict.fromkeys(cls._CHAR_ENT_KEYS.values(), 0.0)
res_sec_hash = dict.fromkeys(cls._SECTION_HASH_KEYS, 0.0)
if hasattr(pe, 'sections'):
base['SectionCount'] = len(pe.sections)
@@ -289,6 +453,10 @@ class FeatureExtractor:
exec_sec, write_sec, read_sec, sec_exc = 0, 0, 0, 0
for section in pe.sections:
sec_name = section.Name.rstrip(b'\x00')
hash_val = zlib.crc32(sec_name) % 50
res_sec_hash[cls._SECTION_HASH_KEYS[hash_val]] += 1.0
try:
s_data = section.get_data()
s_entropy = cls._calc_entropy(s_data)
@@ -299,15 +467,19 @@ 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 char_flags:
for flag in cls._CHAR_FLAGS:
if section.Characteristics & flag:
base[f'Char_{flag:08X}_Count'] += 1.0
base[f'Char_{flag:08X}_MeanEntropy'] += s_entropy
res_flags_count[cls._CHAR_COUNT_KEYS[flag]] += 1.0
res_flags_ent[cls._CHAR_ENT_KEYS[flag]] += s_entropy
base['SectionMaxEntropy'] = max(entropies) if entropies else 0.0
base['SectionMinEntropy'] = min(entropies) if entropies else 0.0
@@ -318,14 +490,19 @@ class FeatureExtractor:
base['SectionMaxVSize'] = max(v_sizes) if v_sizes else 0.0
base['SectionMinVSize'] = min(v_sizes) if v_sizes else 0.0
base['SectionMeanVSize'] = sum(v_sizes) / len(v_sizes) if v_sizes else 0.0
base['ExecutableSections'] = exec_sec
base['WritableSections'] = write_sec
base['ReadableSections'] = read_sec
base['ExecutableSections'] = float(exec_sec)
base['WritableSections'] = float(write_sec)
base['ReadableSections'] = float(read_sec)
base['SectionException'] = float(sec_exc)
for flag in char_flags:
if base[f'Char_{flag:08X}_Count'] > 0:
base[f'Char_{flag:08X}_MeanEntropy'] = base[f'Char_{flag:08X}_MeanEntropy'] / base[f'Char_{flag:08X}_Count']
for flag in cls._CHAR_FLAGS:
cnt_key = cls._CHAR_COUNT_KEYS[flag]
if res_flags_count[cnt_key] > 0:
res_flags_ent[cls._CHAR_ENT_KEYS[flag]] /= res_flags_count[cnt_key]
base.update(res_flags_count)
base.update(res_flags_ent)
base.update(res_sec_hash)
string_keys = ['FileDescription', 'FileVersion', 'ProductName', 'ProductVersion', 'CompanyName', 'LegalCopyright', 'Comments', 'InternalName', 'LegalTrademarks', 'SpecialBuild', 'PrivateBuild']
for key in string_keys:
@@ -337,25 +514,16 @@ 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
try:
pe.parse_data_directories(directories=[
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']
])
except Exception: pass
if hasattr(pe, 'DIRECTORY_ENTRY_TLS') and hasattr(pe.DIRECTORY_ENTRY_TLS, 'struct'):
base['HasTlsCallbacks'] = 1.0 if getattr(pe.DIRECTORY_ENTRY_TLS.struct, 'AddressOfCallBacks', 0) != 0 else 0.0
@@ -368,42 +536,54 @@ class FeatureExtractor:
base['IsSpecialBuild'] = 1.0 if flags & 0x20 else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
base['ImportCount'] = len(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
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
base['ImportFunctionCount'] = func_count
try:
apis.add(imp.name.decode('ascii', 'ignore'))
except Exception:
pass
base['ExportCount'] = len(pe.DIRECTORY_ENTRY_EXPORT.symbols) if (hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols')) else 0.0
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
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
icon_count = 0
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if getattr(entry, 'id', None) == 3 and hasattr(entry, 'directory'):
icon_count += len(getattr(entry.directory, 'entries', []))
base['IconCount'] = icon_count
base['IconCount'] = float(icon_count)
else:
base['IconCount'] = 0.0
base['DebugCount'] = len(pe.DIRECTORY_ENTRY_DEBUG) if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG') else 0.0
base['DebugCount'] = float(len(pe.DIRECTORY_ENTRY_DEBUG)) if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG') else 0.0
base.update(cls._extract_strings(file_bytes))
base.update(cls._extract_histograms(file_bytes))
base.update(cls._extract_overlay_features(pe, fsize))
base.update(cls._extract_rich_header(pe))
base.update(cls._extract_ep_anomalies(pe))
base.update(cls._extract_advanced_resources(pe))
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()
@@ -411,16 +591,17 @@ class FeatureExtractor:
def load_existing_hashes():
hashes = set()
if os.path.exists(JSONL_PATH):
with open(JSONL_PATH, 'r', encoding='utf-8') as f:
for line in f:
try:
data = JSON_LOADS(line)
if 'FileHash' in data: hashes.add(data['FileHash'])
except Exception: pass
return hashes
if not os.path.exists(JSONL_PATH):
return hashes
pattern = re.compile(rb'"FileHash"\s*:\s*"([a-fA-F0-9]{64})"')
with open(JSONL_PATH, 'rb') as f:
for line in f:
match = pattern.search(line)
if match:
hashes.add(match.group(1).decode('ascii'))
####################################################################################################
return hashes
GLOBAL_HASHES = set()
@@ -486,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)
@@ -510,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:
@@ -538,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)
+250 -64
View File
@@ -1,16 +1,13 @@
import os, sys, math, json, pefile, datetime
import os, re, sys, math, json, zlib, pefile, datetime
import ctypes, ctypes.wintypes
import numpy as np
import onnxruntime as ort
import ctypes, ctypes.wintypes
####################################################################################################
MODEL_FILE = "Pefile_General_S1.onnx"
FEATURE_FILE = "features.json"
MAX_FILE_SIZE = 4 * 1024 * 1024 * 1024
TARGET_EXTENSIONS = {
'.exe', '.dll', '.sys', '.ocx', '.scr', '.efi', '.acm', '.ax', '.cpl', '.drv', '.com', '.mui', '.pyd'
}
####################################################################################################
@@ -101,22 +98,126 @@ class WinTrust:
####################################################################################################
class FeatureExtractor:
_STRING_PATTERN = re.compile(b'[\x20-\x7E]{5,}')
_BYTE_HIST_KEYS = [f'ByteHist_{i:02X}' for i in range(256)]
_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}
_C_LOG_C_TABLE = np.zeros(2049, dtype=np.float64)
_C_LOG_C_TABLE[1:] = np.arange(1, 2049) * np.log2(np.arange(1, 2049))
@staticmethod
def _safe_float(val):
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
import numpy as np
if not data:
return 0.0
arr = np.frombuffer(data, dtype=np.uint8)
sz = len(arr)
counts = np.bincount(arr, minlength=256)
probs = counts[counts > 0] / len(arr)
return float(-np.sum(probs * np.log2(probs)))
counts = counts[counts > 0]
return float(np.log2(sz) - np.sum(counts * np.log2(counts)) / sz)
@classmethod
def _extract_strings(cls, file_bytes):
res = {"StringCount": 0.0, "StringMeanLength": 0.0, "StringEntropy": 0.0}
res.update(dict.fromkeys(cls._STRING_KEYS, 0.0))
matches = cls._STRING_PATTERN.findall(file_bytes)
if not matches:
return res
combined_data = b''.join(matches)
count = len(matches)
total_len = len(combined_data)
res["StringCount"] = float(count)
res["StringMeanLength"] = float(total_len) / count
res["StringEntropy"] = cls._calc_entropy(combined_data)
arr = np.frombuffer(combined_data, dtype=np.uint8)
counts = np.bincount(arr, minlength=127)
char_counts = counts[0x20:0x7F]
res.update(dict(zip(cls._STRING_KEYS, char_counts.astype(np.float64) / total_len)))
return res
@classmethod
def _extract_histograms(cls, file_bytes):
arr = np.frombuffer(file_bytes, dtype=np.uint8)
sz = len(arr)
res = {}
if sz == 0:
res.update(dict.fromkeys(cls._BYTE_HIST_KEYS, 0.0))
res.update(dict.fromkeys(cls._BYTE_ENT_KEYS, 0.0))
return res
byte_counts = np.bincount(arr, minlength=256)
byte_hist = byte_counts.astype(np.float64) / sz
res.update(dict(zip(cls._BYTE_HIST_KEYS, byte_hist)))
ent_hist = np.zeros(256, dtype=np.float64)
window = 2048
step = 1024
if sz < window:
w_counts = byte_counts
p = w_counts[w_counts > 0] / float(sz)
entropy = float(-np.sum(p * np.log2(p)))
ent_bin = min(int(entropy * 2.0), 15)
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
for w_start in range(0, num_windows, batch_windows):
w_end = min(w_start + batch_windows, num_windows)
b_num = w_end - w_start
start_byte = w_start * step
end_byte = start_byte + b_num * step + step
b_arr = arr[start_byte:end_byte]
arr_2d = b_arr.reshape(b_num + 1, step)
offsets = (np.arange(b_num + 1, dtype=np.int32)[:, None] * 256)
flat_idx = (arr_2d.astype(np.int32) + offsets).ravel()
chunk_counts = np.bincount(flat_idx, minlength=(b_num + 1) * 256).reshape(b_num + 1, 256)
window_counts = chunk_counts[:-1] + chunk_counts[1:]
sum_c_log_c = np.sum(cls._C_LOG_C_TABLE[window_counts], axis=1)
entropies = 11.0 - sum_c_log_c / 2048.0
ent_bins = (entropies * 2.0).astype(np.int32)
np.clip(ent_bins, 0, 15, out=ent_bins)
byte_bins = window_counts.reshape(b_num, 16, 16).sum(axis=2)
flat_ent_bins = (ent_bins[:, None] * 16 + np.arange(16)).ravel()
np.add.at(ent_hist, flat_ent_bins, byte_bins.ravel())
ent_sum = np.sum(ent_hist)
if ent_sum > 0:
ent_hist /= ent_sum
res.update(dict(zip(cls._BYTE_ENT_KEYS, ent_hist)))
return res
@classmethod
def _extract_overlay_features(cls, pe, fsize):
@@ -198,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)
@@ -221,12 +323,52 @@ 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
def _extract_security_directory(cls, pe, file_bytes, fsize):
res = {"HasSignature": 0.0, "SignatureCount": 0.0}
if not hasattr(pe, 'OPTIONAL_HEADER') or not hasattr(pe.OPTIONAL_HEADER, 'DATA_DIRECTORY'):
return res
sec_idx = 4
directories = pe.OPTIONAL_HEADER.DATA_DIRECTORY
if len(directories) <= sec_idx:
return res
sec_dir = directories[sec_idx]
if sec_dir.Size == 0 or sec_dir.VirtualAddress == 0:
return res
res["HasSignature"] = 1.0
offset = sec_dir.VirtualAddress
size = sec_dir.Size
if offset + size > fsize or offset < 0 or size < 0:
return res
count = 0
curr = offset
end = offset + size
while curr + 8 <= end:
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
if dw_length < 8:
break
count += 1
curr += (dw_length + 7) & ~7
res["SignatureCount"] = float(count)
return res
@classmethod
def extract(cls, file_path):
pe = None
@@ -240,6 +382,19 @@ class FeatureExtractor:
base, dlls, apis = {}, set(), set()
pe = pefile.PE(data=file_bytes, fast_load=True)
try:
pe.parse_rich_header()
pe.parse_data_directories(directories=[
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']
])
except Exception:
pass
base['TrustSigned'] = float(WinTrust.verify(file_path))
base['FileEntropy'] = cls._calc_entropy(file_bytes)
@@ -260,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'):
@@ -278,10 +432,9 @@ class FeatureExtractor:
if len(op.DATA_DIRECTORY) > exc_idx and op.DATA_DIRECTORY[exc_idx].Size > 0:
base['ExceptionCount'] = op.DATA_DIRECTORY[exc_idx].Size // 12 if base['Machine'] in (0x8664, 0xAA64) else 0.0
char_flags = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
for flag in char_flags:
base[f'Char_{flag:08X}_Count'] = 0.0
base[f'Char_{flag:08X}_MeanEntropy'] = 0.0
res_flags_count = dict.fromkeys(cls._CHAR_COUNT_KEYS.values(), 0.0)
res_flags_ent = dict.fromkeys(cls._CHAR_ENT_KEYS.values(), 0.0)
res_sec_hash = dict.fromkeys(cls._SECTION_HASH_KEYS, 0.0)
if hasattr(pe, 'sections'):
base['SectionCount'] = len(pe.sections)
@@ -289,6 +442,10 @@ class FeatureExtractor:
exec_sec, write_sec, read_sec, sec_exc = 0, 0, 0, 0
for section in pe.sections:
sec_name = section.Name.rstrip(b'\x00')
hash_val = zlib.crc32(sec_name) % 50
res_sec_hash[cls._SECTION_HASH_KEYS[hash_val]] += 1.0
try:
s_data = section.get_data()
s_entropy = cls._calc_entropy(s_data)
@@ -299,15 +456,19 @@ 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 char_flags:
for flag in cls._CHAR_FLAGS:
if section.Characteristics & flag:
base[f'Char_{flag:08X}_Count'] += 1.0
base[f'Char_{flag:08X}_MeanEntropy'] += s_entropy
res_flags_count[cls._CHAR_COUNT_KEYS[flag]] += 1.0
res_flags_ent[cls._CHAR_ENT_KEYS[flag]] += s_entropy
base['SectionMaxEntropy'] = max(entropies) if entropies else 0.0
base['SectionMinEntropy'] = min(entropies) if entropies else 0.0
@@ -318,14 +479,19 @@ class FeatureExtractor:
base['SectionMaxVSize'] = max(v_sizes) if v_sizes else 0.0
base['SectionMinVSize'] = min(v_sizes) if v_sizes else 0.0
base['SectionMeanVSize'] = sum(v_sizes) / len(v_sizes) if v_sizes else 0.0
base['ExecutableSections'] = exec_sec
base['WritableSections'] = write_sec
base['ReadableSections'] = read_sec
base['ExecutableSections'] = float(exec_sec)
base['WritableSections'] = float(write_sec)
base['ReadableSections'] = float(read_sec)
base['SectionException'] = float(sec_exc)
for flag in char_flags:
if base[f'Char_{flag:08X}_Count'] > 0:
base[f'Char_{flag:08X}_MeanEntropy'] = base[f'Char_{flag:08X}_MeanEntropy'] / base[f'Char_{flag:08X}_Count']
for flag in cls._CHAR_FLAGS:
cnt_key = cls._CHAR_COUNT_KEYS[flag]
if res_flags_count[cnt_key] > 0:
res_flags_ent[cls._CHAR_ENT_KEYS[flag]] /= res_flags_count[cnt_key]
base.update(res_flags_count)
base.update(res_flags_ent)
base.update(res_sec_hash)
string_keys = ['FileDescription', 'FileVersion', 'ProductName', 'ProductVersion', 'CompanyName', 'LegalCopyright', 'Comments', 'InternalName', 'LegalTrademarks', 'SpecialBuild', 'PrivateBuild']
for key in string_keys:
@@ -337,25 +503,16 @@ 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
try:
pe.parse_data_directories(directories=[
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']
])
except Exception: pass
if hasattr(pe, 'DIRECTORY_ENTRY_TLS') and hasattr(pe.DIRECTORY_ENTRY_TLS, 'struct'):
base['HasTlsCallbacks'] = 1.0 if getattr(pe.DIRECTORY_ENTRY_TLS.struct, 'AddressOfCallBacks', 0) != 0 else 0.0
@@ -368,44 +525,57 @@ class FeatureExtractor:
base['IsSpecialBuild'] = 1.0 if flags & 0x20 else 0.0
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
base['ImportCount'] = len(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
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
base['ImportFunctionCount'] = func_count
try:
apis.add(imp.name.decode('ascii', 'ignore'))
except Exception:
pass
base['ExportCount'] = len(pe.DIRECTORY_ENTRY_EXPORT.symbols) if (hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols')) else 0.0
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
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
icon_count = 0
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
if getattr(entry, 'id', None) == 3 and hasattr(entry, 'directory'):
icon_count += len(getattr(entry.directory, 'entries', []))
base['IconCount'] = icon_count
base['IconCount'] = float(icon_count)
else:
base['IconCount'] = 0.0
base['DebugCount'] = len(pe.DIRECTORY_ENTRY_DEBUG) if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG') else 0.0
base['DebugCount'] = float(len(pe.DIRECTORY_ENTRY_DEBUG)) if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG') else 0.0
base.update(cls._extract_strings(file_bytes))
base.update(cls._extract_histograms(file_bytes))
base.update(cls._extract_overlay_features(pe, fsize))
base.update(cls._extract_rich_header(pe))
base.update(cls._extract_ep_anomalies(pe))
base.update(cls._extract_advanced_resources(pe))
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()
####################################################################################################
@@ -420,27 +590,36 @@ class ModelPredictor:
if current_dim != expected_dim:
raise ValueError(f"Dimension mismatch! Model expects {expected_dim} features, but feature file lists {current_dim}.")
self.feat_map = {feat: i for i, feat in enumerate(self.model_features)}
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)
def predict(self, raw_data):
vec = np.zeros((1, len(self.model_features)), dtype=np.float32)
base = raw_data.get('Base', {})
dlls = set(raw_data.get('DLLs', []))
apis = set(raw_data.get('APIs', []))
dlls = raw_data.get('DLLs', [])
apis = raw_data.get('APIs', [])
for i, feat in enumerate(self.model_features):
if feat.startswith('Dll_'):
if feat[4:] in dlls:
vec[0, i] = 1.0
elif feat.startswith('Api_'):
if feat[4:] in apis:
vec[0, i] = 1.0
else:
vec[0, i] = base.get(feat, 0.0)
for k, v in base.items():
if k in self.feat_map:
vec[0, self.feat_map[k]] = v
for d in dlls:
h = zlib.crc32(d.encode('utf-8', 'ignore')) % 256
feat_name = f"DllHash_{h:03d}"
if feat_name in self.feat_map:
vec[0, self.feat_map[feat_name]] += 1.0
for a in apis:
h = zlib.crc32(a.encode('utf-8', 'ignore')) % 1024
feat_name = f"ApiHash_{h:04d}"
if feat_name in self.feat_map:
vec[0, self.feat_map[feat_name]] += 1.0
outputs = self.sess.run(None, {self.input_name: vec})
@@ -450,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
####################################################################################################
@@ -461,11 +642,11 @@ 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:
if os.path.splitext(f)[1].lower() in TARGET_EXTENSIONS:
files.append(os.path.join(r, f))
files.append(os.path.join(r, f))
if not files:
print("[-] No valid PE files found.")
@@ -526,6 +707,7 @@ if __name__ == "__main__":
print()
print("-" * 80)
scan_target(target, predictor)
else:
print(f"[-] Path does not exist: {target}")
else:
@@ -534,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:
+113 -50
View File
@@ -1,4 +1,4 @@
import os, sys, time, json, onnxmltools
import os, sys, time, json, zlib, array, onnxmltools, multiprocessing
import numpy as np
import pandas as pd
import lightgbm as lgb
@@ -9,14 +9,24 @@ from onnxmltools.convert.common.data_types import FloatTensorType
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, roc_auc_score
try:
import orjson
JSON_LOADS = orjson.loads
except ImportError:
JSON_LOADS = json.loads
####################################################################################################
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
EXCLUDE_FEATURES = {
'label', 'filehash',
@@ -42,78 +52,131 @@ LGBM_PARAMS = {
def analyze_schema(jsonl_path):
print("[*] Stage 1: Scanning global schema...")
base_keys = set()
dll_counts = defaultdict(int)
api_counts = defaultdict(int)
num_lines = 0
with open(jsonl_path, 'r') as f:
with open(jsonl_path, 'rb') as f:
for line in f:
try:
data = json.loads(line)
base_keys.update(data.get('Base', {}).keys())
for d in data.get('DLLs', []):
dll_counts[d] += 1
for a in data.get('APIs', []):
api_counts[a] += 1
num_lines += 1
except Exception:
continue
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
base_schema = sorted([k for k in base_keys if k.lower().strip() not in EXCLUDE_FEATURES])
dll_schema = sorted([f"Dll_{k}" for k, v in dll_counts.items() if v >= 1000])
api_schema = sorted([f"Api_{k}" for k, v in api_counts.items() if v >= 1000])
dll_schema = [f"DllHash_{i:03d}" for i in range(DLL_HASH_DIM)]
api_schema = [f"ApiHash_{i:04d}" for i in range(API_HASH_DIM)]
return base_schema, dll_schema, api_schema, num_lines
####################################################################################################
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
print(f"[*] Stage 2: Building sparse feature matrix dynamically (Expected samples: {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)
feature_idx = {feat: i for i, feat in enumerate(final_features)}
offset_dll = len(base_schema)
offset_api = offset_dll + DLL_HASH_DIM
rows, cols, vals = [], [], []
y = np.zeros(num_lines, dtype=np.int8)
file_size = os.path.getsize(jsonl_path)
workers = min(os.cpu_count() or 4, 16)
chunk_size = file_size // workers
tasks = []
for i in range(workers):
start = i * chunk_size
end = file_size if i == workers - 1 else (i + 1) * chunk_size
tasks.append((jsonl_path, start, end, base_schema, num_features, offset_dll, offset_api))
with multiprocessing.Pool(workers) as pool:
results = pool.map(_chunk_worker, tasks)
Xs = [r[0] for r in results if r[0].shape[0] > 0]
ys = [r[1] for r in results if r[1].shape[0] > 0]
X_final = sp.vstack(Xs, format='csr') if Xs else sp.csr_matrix((0, num_features), dtype=np.float32)
y_final = np.concatenate(ys) if ys else np.array([], dtype=np.int8)
return X_final, pd.Series(y_final), final_features
with open(jsonl_path, 'r') as f:
def _chunk_worker(args):
filepath, start_byte, end_byte, base_schema, num_features, offset_dll, offset_api = args
base_idx = {feat: i for i, feat in enumerate(base_schema)}
base_idx_get = base_idx.get
rows = array.array('i')
cols = array.array('i')
vals = array.array('f')
y_list = array.array('b')
with open(filepath, 'rb') as f:
if start_byte > 0:
f.seek(start_byte - 1)
if f.read(1) != b'\n':
f.seek(start_byte)
f.readline()
else:
f.seek(start_byte)
else:
f.seek(0)
row_idx = 0
for line in f:
while f.tell() < end_byte:
line = f.readline()
if not line:
break
try:
data = json.loads(line)
y[row_idx] = data.get('Label', 0)
data = JSON_LOADS(line)
y_list.append(data.get('Label', 0))
for k, v in data.get('Base', {}).items():
idx = feature_idx.get(k)
if idx is not None:
val = float(v)
if val != 0.0:
rows.append(row_idx)
cols.append(idx)
vals.append(val)
base_data = data.get('Base')
if base_data:
for k, v in base_data.items():
idx = base_idx_get(k)
if idx is not None:
val = float(v)
if val != 0.0:
rows.append(row_idx)
cols.append(idx)
vals.append(val)
for d in data.get('DLLs', []):
idx = feature_idx.get(f"Dll_{d}")
if idx is not None:
dll_data = data.get('DLLs')
if dll_data:
for d in dll_data:
idx = zlib.crc32(d.encode('utf-8', 'ignore')) % DLL_HASH_DIM
rows.append(row_idx)
cols.append(idx)
cols.append(offset_dll + idx)
vals.append(1.0)
for a in data.get('APIs', []):
idx = feature_idx.get(f"Api_{a}")
if idx is not None:
api_data = data.get('APIs')
if api_data:
for a in api_data:
idx = zlib.crc32(a.encode('utf-8', 'ignore')) % API_HASH_DIM
rows.append(row_idx)
cols.append(idx)
cols.append(offset_api + idx)
vals.append(1.0)
row_idx += 1
except Exception:
continue
y = y[:row_idx]
X = sp.csr_matrix((vals, (rows, cols)), shape=(row_idx, num_features), dtype=np.float32)
y_arr = np.frombuffer(y_list, dtype=np.int8)
np_rows = np.frombuffer(rows, dtype=np.int32)
np_cols = np.frombuffer(cols, dtype=np.int32)
np_vals = np.frombuffer(vals, dtype=np.float32)
return X, pd.Series(y), final_features
X_local = sp.csr_matrix((np_vals, (np_rows, np_cols)), shape=(row_idx, num_features), dtype=np.float32)
return X_local, y_arr
####################################################################################################
@@ -123,15 +186,13 @@ 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()
base_weight = n_neg / n_pos if n_pos > 0 else 1.0
weight_ratio_target = 0.01
weight_ratio_target = 0.005
final_pos_weight = base_weight * weight_ratio_target
print(f"[*] Sample distribution: Safe={n_neg}, Malware={n_pos}")
@@ -221,6 +282,7 @@ def evaluate_and_save(model, X_test, y_test):
####################################################################################################
if __name__ == "__main__":
multiprocessing.freeze_support()
print("\n---------------- PE Malware Trainer v4.0 ----------------\n")
if not os.path.exists(JSONL_PATH):
@@ -233,14 +295,15 @@ if __name__ == "__main__":
sys.exit(1)
print(f"[*] Detected Base features: {len(base_schema)}")
print(f"[*] Filtered DLL features: {len(dll_schema)}")
print(f"[*] Filtered API features: {len(api_schema)}")
print(f"[*] Hashing DLL features: {len(dll_schema)}")
print(f"[*] Hashing API features: {len(api_schema)}")
X, y, final_features = load_data_efficiently(JSONL_PATH, base_schema, dll_schema, api_schema, num_lines)
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:
+112 -49
View File
@@ -1,4 +1,4 @@
import os, sys, time, json, onnxmltools
import os, sys, time, json, zlib, array, onnxmltools, multiprocessing
import numpy as np
import pandas as pd
import lightgbm as lgb
@@ -9,14 +9,24 @@ from onnxmltools.convert.common.data_types import FloatTensorType
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, roc_auc_score
try:
import orjson
JSON_LOADS = orjson.loads
except ImportError:
JSON_LOADS = json.loads
####################################################################################################
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
EXCLUDE_FEATURES = {
'label', 'filehash',
@@ -42,78 +52,131 @@ LGBM_PARAMS = {
def analyze_schema(jsonl_path):
print("[*] Stage 1: Scanning global schema...")
base_keys = set()
dll_counts = defaultdict(int)
api_counts = defaultdict(int)
num_lines = 0
with open(jsonl_path, 'r') as f:
with open(jsonl_path, 'rb') as f:
for line in f:
try:
data = json.loads(line)
base_keys.update(data.get('Base', {}).keys())
for d in data.get('DLLs', []):
dll_counts[d] += 1
for a in data.get('APIs', []):
api_counts[a] += 1
num_lines += 1
except Exception:
continue
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
base_schema = sorted([k for k in base_keys if k.lower().strip() not in EXCLUDE_FEATURES])
dll_schema = sorted([f"Dll_{k}" for k, v in dll_counts.items() if v >= 1000])
api_schema = sorted([f"Api_{k}" for k, v in api_counts.items() if v >= 1000])
dll_schema = [f"DllHash_{i:03d}" for i in range(DLL_HASH_DIM)]
api_schema = [f"ApiHash_{i:04d}" for i in range(API_HASH_DIM)]
return base_schema, dll_schema, api_schema, num_lines
####################################################################################################
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
print(f"[*] Stage 2: Building sparse feature matrix dynamically (Expected samples: {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)
feature_idx = {feat: i for i, feat in enumerate(final_features)}
offset_dll = len(base_schema)
offset_api = offset_dll + DLL_HASH_DIM
rows, cols, vals = [], [], []
y = np.zeros(num_lines, dtype=np.int8)
file_size = os.path.getsize(jsonl_path)
workers = min(os.cpu_count() or 4, 16)
chunk_size = file_size // workers
tasks = []
for i in range(workers):
start = i * chunk_size
end = file_size if i == workers - 1 else (i + 1) * chunk_size
tasks.append((jsonl_path, start, end, base_schema, num_features, offset_dll, offset_api))
with multiprocessing.Pool(workers) as pool:
results = pool.map(_chunk_worker, tasks)
Xs = [r[0] for r in results if r[0].shape[0] > 0]
ys = [r[1] for r in results if r[1].shape[0] > 0]
X_final = sp.vstack(Xs, format='csr') if Xs else sp.csr_matrix((0, num_features), dtype=np.float32)
y_final = np.concatenate(ys) if ys else np.array([], dtype=np.int8)
return X_final, pd.Series(y_final), final_features
with open(jsonl_path, 'r') as f:
def _chunk_worker(args):
filepath, start_byte, end_byte, base_schema, num_features, offset_dll, offset_api = args
base_idx = {feat: i for i, feat in enumerate(base_schema)}
base_idx_get = base_idx.get
rows = array.array('i')
cols = array.array('i')
vals = array.array('f')
y_list = array.array('b')
with open(filepath, 'rb') as f:
if start_byte > 0:
f.seek(start_byte - 1)
if f.read(1) != b'\n':
f.seek(start_byte)
f.readline()
else:
f.seek(start_byte)
else:
f.seek(0)
row_idx = 0
for line in f:
while f.tell() < end_byte:
line = f.readline()
if not line:
break
try:
data = json.loads(line)
y[row_idx] = data.get('Label', 0)
data = JSON_LOADS(line)
y_list.append(data.get('Label', 0))
for k, v in data.get('Base', {}).items():
idx = feature_idx.get(k)
if idx is not None:
val = float(v)
if val != 0.0:
rows.append(row_idx)
cols.append(idx)
vals.append(val)
base_data = data.get('Base')
if base_data:
for k, v in base_data.items():
idx = base_idx_get(k)
if idx is not None:
val = float(v)
if val != 0.0:
rows.append(row_idx)
cols.append(idx)
vals.append(val)
for d in data.get('DLLs', []):
idx = feature_idx.get(f"Dll_{d}")
if idx is not None:
dll_data = data.get('DLLs')
if dll_data:
for d in dll_data:
idx = zlib.crc32(d.encode('utf-8', 'ignore')) % DLL_HASH_DIM
rows.append(row_idx)
cols.append(idx)
cols.append(offset_dll + idx)
vals.append(1.0)
for a in data.get('APIs', []):
idx = feature_idx.get(f"Api_{a}")
if idx is not None:
api_data = data.get('APIs')
if api_data:
for a in api_data:
idx = zlib.crc32(a.encode('utf-8', 'ignore')) % API_HASH_DIM
rows.append(row_idx)
cols.append(idx)
cols.append(offset_api + idx)
vals.append(1.0)
row_idx += 1
except Exception:
continue
y = y[:row_idx]
X = sp.csr_matrix((vals, (rows, cols)), shape=(row_idx, num_features), dtype=np.float32)
y_arr = np.frombuffer(y_list, dtype=np.int8)
np_rows = np.frombuffer(rows, dtype=np.int32)
np_cols = np.frombuffer(cols, dtype=np.int32)
np_vals = np.frombuffer(vals, dtype=np.float32)
return X, pd.Series(y), final_features
X_local = sp.csr_matrix((np_vals, (np_rows, np_cols)), shape=(row_idx, num_features), dtype=np.float32)
return X_local, y_arr
####################################################################################################
@@ -123,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()
@@ -221,6 +282,7 @@ def evaluate_and_save(model, X_test, y_test):
####################################################################################################
if __name__ == "__main__":
multiprocessing.freeze_support()
print("\n---------------- PE Malware Trainer v4.0 ----------------\n")
if not os.path.exists(JSONL_PATH):
@@ -233,14 +295,15 @@ if __name__ == "__main__":
sys.exit(1)
print(f"[*] Detected Base features: {len(base_schema)}")
print(f"[*] Filtered DLL features: {len(dll_schema)}")
print(f"[*] Filtered API features: {len(api_schema)}")
print(f"[*] Hashing DLL features: {len(dll_schema)}")
print(f"[*] Hashing API features: {len(api_schema)}")
X, y, final_features = load_data_efficiently(JSONL_PATH, base_schema, dll_schema, api_schema, num_lines)
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:
+566 -71
View File
@@ -49,6 +49,7 @@ window.AppI18n = {
"scan_context_desc": "此選項可以啟用右鍵掃描功能",
"theme_color": "顯色主題",
"theme_color_desc": "此選項可以選擇主題顏色",
"theme_system": "跟隨系統",
"theme_white": "白色主題",
"theme_red": "紅色主題",
"theme_yellow": "黃色主題",
@@ -67,18 +68,14 @@ window.AppI18n = {
"update_check": "檢查更新",
"update_check_desc": "此選項可以檢查可用更新",
"search_proc": "搜尋進程...",
"btn_view": "查看",
"btn_end": "結束",
"manage_white": "白名單管理",
"search_file": "搜尋檔案...",
"btn_add": "加入",
"btn_remove": "移除",
"manage_quarantine": "隔離區管理",
"btn_scan": "掃描",
"search_window": "搜尋窗口...",
"search_rule": "搜尋規則...",
"search_log": "搜尋日誌...",
"btn_export": "導出",
"btn_export": "導出項目",
"msg_init": "正在初始化中",
"msg_update_fail": "檢查更新失敗",
"msg_new_version": "發現新版本",
@@ -97,7 +94,6 @@ window.AppI18n = {
"msg_click_target": "請在5秒內點擊目標視窗...",
"msg_confirm_block": "確認攔截",
"msg_repaired": "已修復選取項目",
"btn_repair": "修復",
"title_prompt": "提示",
"title_error": "錯誤",
"path_unknown": "未知路徑",
@@ -134,7 +130,56 @@ window.AppI18n = {
"res_net_block": "網路攔截",
"res_drv_block": "驅動攔截",
"res_sys_repair": "系統修復",
"res_cfg_update": "設定更新"
"res_cfg_update": "設定更新",
"manage_startup": "啟動項管理",
"manage_startup_desc": "此選項可以管理各應用啟動項",
"col_status": "狀態",
"status_enabled": "已啟用",
"status_disabled": "已禁用",
"search_item": "搜尋項目...",
"type_reg": "登錄檔",
"type_srv": "服務",
"type_tsk": "排程任務",
"btn_view_item": "查看項目",
"btn_end": "結束進程",
"btn_enable_item": "啟用項目",
"btn_disable_item": "禁用項目",
"btn_delete_item": "刪除項目",
"btn_repair_item": "修復項目",
"btn_add_item": "新增項目",
"btn_remove_item": "移除項目",
"btn_add_file": "新增檔案",
"btn_add_path": "新增路徑",
"manage_netmon": "流量監控",
"manage_netmon_desc": "此選項可以實時監控網路流量",
"col_down_speed": "下載速度",
"col_up_speed": "上傳速度",
"col_conn_count": "連接數",
"btn_extract_item": "提取項目",
"msg_extract_success": "成功提取選取項目",
"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",
@@ -185,6 +230,7 @@ window.AppI18n = {
"scan_context_desc": "此选项可以启用右键扫描功能",
"theme_color": "显色主题",
"theme_color_desc": "此选项可以选择主题颜色",
"theme_system": "跟随系统",
"theme_white": "白色主题",
"theme_red": "红色主题",
"theme_yellow": "黄色主题",
@@ -203,18 +249,14 @@ window.AppI18n = {
"update_check": "检查更新",
"update_check_desc": "此选项可以检查可用更新",
"search_proc": "搜索进程...",
"btn_view": "查看",
"btn_end": "结束",
"manage_white": "白名单管理",
"search_file": "搜索文件...",
"btn_add": "加入",
"btn_remove": "移除",
"manage_quarantine": "隔离区管理",
"btn_scan": "扫描",
"search_window": "搜索窗口...",
"search_rule": "搜索规则...",
"search_log": "搜索日志...",
"btn_export": "导出",
"btn_export": "导出项目",
"msg_init": "正在初始化中",
"msg_update_fail": "检查更新失败",
"msg_new_version": "发现新版本",
@@ -233,7 +275,6 @@ window.AppI18n = {
"msg_click_target": "请在5秒内点击目标窗口...",
"msg_confirm_block": "确认拦截",
"msg_repaired": "已修复选取项目",
"btn_repair": "修复",
"title_prompt": "提示",
"title_error": "错误",
"path_unknown": "未知路径",
@@ -270,7 +311,56 @@ window.AppI18n = {
"res_net_block": "网络拦截",
"res_drv_block": "驱动拦截",
"res_sys_repair": "系统修复",
"res_cfg_update": "设置更新"
"res_cfg_update": "设置更新",
"manage_startup": "启动项管理",
"manage_startup_desc": "此选项可以管理各应用启动项",
"col_status": "状态",
"status_enabled": "已启用",
"status_disabled": "已禁用",
"search_item": "搜索项目...",
"type_reg": "注册表",
"type_srv": "服务",
"type_tsk": "计划任务",
"btn_view_item": "查看项目",
"btn_end": "结束进程",
"btn_enable_item": "启用项目",
"btn_disable_item": "禁用项目",
"btn_delete_item": "删除项目",
"btn_repair_item": "修复项目",
"btn_add_item": "新增项目",
"btn_remove_item": "移除项目",
"btn_add_file": "新增文件",
"btn_add_path": "新增路径",
"manage_netmon": "流量监控",
"manage_netmon_desc": "此选项可以实时监控网络流量",
"col_down_speed": "下载速度",
"col_up_speed": "上传速度",
"col_conn_count": "连接数",
"btn_extract_item": "提取项目",
"msg_extract_success": "成功提取选取项目",
"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",
@@ -321,6 +411,7 @@ window.AppI18n = {
"scan_context_desc": "Enable right-click scan functionality",
"theme_color": "Color Theme",
"theme_color_desc": "Select application color theme",
"theme_system": "System Theme",
"theme_white": "Light Mode",
"theme_red": "Red Theme",
"theme_yellow": "Yellow Theme",
@@ -339,18 +430,14 @@ window.AppI18n = {
"update_check": "Check for Updates",
"update_check_desc": "Check for the latest updates",
"search_proc": "Search processes...",
"btn_view": "View",
"btn_end": "End Task",
"manage_white": "Whitelist Manager",
"search_file": "Search files...",
"btn_add": "Add",
"btn_remove": "Remove",
"manage_quarantine": "Quarantine Manager",
"btn_scan": "Scan",
"search_window": "Search windows...",
"search_rule": "Search rules...",
"search_log": "Search logs...",
"btn_export": "Export",
"btn_export": "Export Items",
"msg_init": "Initializing...",
"msg_update_fail": "Update check failed",
"msg_new_version": "New version found",
@@ -369,7 +456,6 @@ window.AppI18n = {
"msg_click_target": "Click the target window within 5s...",
"msg_confirm_block": "Confirm Block",
"msg_repaired": "Selected items repaired",
"btn_repair": "Repair",
"title_prompt": "Prompt",
"title_error": "Error",
"path_unknown": "Unknown Path",
@@ -406,7 +492,56 @@ window.AppI18n = {
"res_net_block": "Network Blocked",
"res_drv_block": "Driver Blocked",
"res_sys_repair": "System Repair",
"res_cfg_update": "Config Update"
"res_cfg_update": "Config Update",
"manage_startup": "Startup Manager",
"manage_startup_desc": "Manage startup applications and services",
"col_status": "Status",
"status_enabled": "Enabled",
"status_disabled": "Disabled",
"search_item": "Search items...",
"type_reg": "Registry",
"type_srv": "Service",
"type_tsk": "Task",
"btn_view_item": "View Item",
"btn_end": "End Process",
"btn_enable_item": "Enable Item",
"btn_disable_item": "Disable Item",
"btn_delete_item": "Delete Item",
"btn_repair_item": "Repair Item",
"btn_add_item": "Add Item",
"btn_remove_item": "Remove Item",
"btn_add_file": "Add File",
"btn_add_path": "Add Path",
"manage_netmon": "Traffic Monitor",
"manage_netmon_desc": "Monitor network traffic in real-time",
"col_down_speed": "Download Speed",
"col_up_speed": "Upload Speed",
"col_conn_count": "Connections",
"btn_extract_item": "Extract Item",
"msg_extract_success": "Successfully extracted selected items",
"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",
@@ -457,6 +592,7 @@ window.AppI18n = {
"scan_context_desc": "右クリックでスキャン機能を有効にします",
"theme_color": "カラーテーマ",
"theme_color_desc": "アプリケーションのテーマカラーを選択します",
"theme_system": "システムに従う",
"theme_white": "ライトモード",
"theme_red": "レッドテーマ",
"theme_yellow": "イエローテーマ",
@@ -475,12 +611,8 @@ window.AppI18n = {
"update_check": "アップデートの確認",
"update_check_desc": "利用可能なアップデートを確認します",
"search_proc": "プロセスを検索...",
"btn_view": "表示",
"btn_end": "タスクの終了",
"manage_white": "ホワイトリスト管理",
"search_file": "ファイルを検索...",
"btn_add": "追加",
"btn_remove": "削除",
"manage_quarantine": "隔離管理",
"btn_scan": "スキャン",
"search_window": "ウィンドウを検索...",
@@ -505,7 +637,6 @@ window.AppI18n = {
"msg_click_target": "5秒以内にターゲットウィンドウをクリックしてください...",
"msg_confirm_block": "ブロックの確認",
"msg_repaired": "選択された項目が修復されました",
"btn_repair": "修復",
"title_prompt": "プロンプト",
"title_error": "エラー",
"path_unknown": "不明なパス",
@@ -542,7 +673,56 @@ window.AppI18n = {
"res_net_block": "ネットワークブロック",
"res_drv_block": "ドライバーブロック",
"res_sys_repair": "システム修復",
"res_cfg_update": "設定の更新"
"res_cfg_update": "設定の更新",
"manage_startup": "起動項管理",
"manage_startup_desc": "アプリケーションの起動項目を管理します",
"col_status": "ステータス",
"status_enabled": "有効",
"status_disabled": "無効",
"search_item": "項目を検索...",
"type_reg": "レジストリ",
"type_srv": "サービス",
"type_tsk": "タスク",
"btn_view_item": "項目を表示",
"btn_end": "プロセスを終了",
"btn_enable_item": "項目を有効化",
"btn_disable_item": "項目を無効化",
"btn_delete_item": "項目を削除",
"btn_repair_item": "項目を修復",
"btn_add_item": "項目を追加",
"btn_remove_item": "項目を削除",
"btn_add_file": "ファイルを追加",
"btn_add_path": "パスを追加",
"manage_netmon": "トラフィック監視",
"manage_netmon_desc": "ネットワークトラフィックをリアルタイムで監視します",
"col_down_speed": "ダウンロード速度",
"col_up_speed": "アップロード速度",
"col_conn_count": "接続数",
"btn_extract_item": "アイテムを抽出",
"msg_extract_success": "選択したアイテムを正常に抽出しました",
"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",
@@ -593,6 +773,7 @@ window.AppI18n = {
"scan_context_desc": "우클릭 스캔 기능을 활성화합니다",
"theme_color": "색상 테마",
"theme_color_desc": "응용 프로그램 테마 색상을 선택합니다",
"theme_system": "시스템 테마",
"theme_white": "라이트 모드",
"theme_red": "레드 테마",
"theme_yellow": "옐로우 테마",
@@ -611,12 +792,8 @@ window.AppI18n = {
"update_check": "업데이트 확인",
"update_check_desc": "사용 가능한 최신 업데이트를 확인합니다",
"search_proc": "프로세스 검색...",
"btn_view": "보기",
"btn_end": "작업 끝내기",
"manage_white": "화이트리스트 관리",
"search_file": "파일 검색...",
"btn_add": "추가",
"btn_remove": "제거",
"manage_quarantine": "격리소 관리",
"btn_scan": "스캔",
"search_window": "창 검색...",
@@ -641,7 +818,6 @@ window.AppI18n = {
"msg_click_target": "5초 이내에 대상 창을 클릭하십시오...",
"msg_confirm_block": "차단 확인",
"msg_repaired": "선택한 항목이 복구되었습니다",
"btn_repair": "복구",
"title_prompt": "프롬프트",
"title_error": "오류",
"path_unknown": "알 수 없는 경로",
@@ -678,7 +854,56 @@ window.AppI18n = {
"res_net_block": "네트워크 차단",
"res_drv_block": "드라이버 차단",
"res_sys_repair": "시스템 복구",
"res_cfg_update": "설정 업데이트"
"res_cfg_update": "설정 업데이트",
"manage_startup": "시작 항목 관리",
"manage_startup_desc": "애플리케이션 시작 항목을 관리합니다",
"col_status": "상태",
"status_enabled": "사용",
"status_disabled": "사용 안 함",
"search_item": "항목 검색...",
"type_reg": "레지스트리",
"type_srv": "서비스",
"type_tsk": "작업",
"btn_view_item": "항목 보기",
"btn_end": "프로세스 종료",
"btn_enable_item": "항목 사용",
"btn_disable_item": "항목 사용 안 함",
"btn_delete_item": "항목 삭제",
"btn_repair_item": "항목 복구",
"btn_add_item": "항목 추가",
"btn_remove_item": "항목 제거",
"btn_add_file": "파일 추가",
"btn_add_path": "경로 추가",
"manage_netmon": "트래픽 모니터링",
"manage_netmon_desc": "실시간으로 네트워크 트래픽을 모니터링합니다",
"col_down_speed": "다운로드 속도",
"col_up_speed": "업로드 속도",
"col_conn_count": "연결 수",
"btn_extract_item": "항목 추출",
"msg_extract_success": "선택한 항목을 성공적으로 추출했습니다",
"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",
@@ -729,6 +954,7 @@ window.AppI18n = {
"scan_context_desc": "Activer la fonctionnalité d'analyse par clic droit",
"theme_color": "Thème de couleur",
"theme_color_desc": "Sélectionnez le thème de couleur de l'application",
"theme_system": "Thème du système",
"theme_white": "Mode clair",
"theme_red": "Thème rouge",
"theme_yellow": "Thème jaune",
@@ -747,12 +973,8 @@ window.AppI18n = {
"update_check": "Vérifier les mises à jour",
"update_check_desc": "Vérifier les dernières mises à jour",
"search_proc": "Rechercher des processus...",
"btn_view": "Voir",
"btn_end": "Fin de tâche",
"manage_white": "Gestionnaire de liste blanche",
"search_file": "Rechercher des fichiers...",
"btn_add": "Ajouter",
"btn_remove": "Retirer",
"manage_quarantine": "Gestionnaire de quarantaine",
"btn_scan": "Analyser",
"search_window": "Rechercher des fenêtres...",
@@ -777,7 +999,6 @@ window.AppI18n = {
"msg_click_target": "Cliquez sur la fenêtre cible dans les 5s...",
"msg_confirm_block": "Confirmer le blocage",
"msg_repaired": "Éléments sélectionnés réparés",
"btn_repair": "Réparer",
"title_prompt": "Indication",
"title_error": "Erreur",
"path_unknown": "Chemin inconnu",
@@ -814,7 +1035,56 @@ window.AppI18n = {
"res_net_block": "Réseau bloqué",
"res_drv_block": "Pilote bloqué",
"res_sys_repair": "Réparation du système",
"res_cfg_update": "Mise à jour de la configuration"
"res_cfg_update": "Mise à jour de la configuration",
"manage_startup": "Gestionnaire de démarrage",
"manage_startup_desc": "Gérer les applications et services de démarrage",
"col_status": "Statut",
"status_enabled": "Activé",
"status_disabled": "Désactivé",
"search_item": "Rechercher des éléments...",
"type_reg": "Registre",
"type_srv": "Service",
"type_tsk": "Tâche",
"btn_view_item": "Afficher l'élément",
"btn_end": "Terminer le processus",
"btn_enable_item": "Activer l'élément",
"btn_disable_item": "Désactiver l'élément",
"btn_delete_item": "Supprimer l'élément",
"btn_repair_item": "Réparer l'élément",
"btn_add_item": "Ajouter l'élément",
"btn_remove_item": "Retirer l'élément",
"btn_add_file": "Ajouter un fichier",
"btn_add_path": "Ajouter un chemin",
"manage_netmon": "Moniteur de trafic",
"manage_netmon_desc": "Surveille le trafic réseau en temps réel",
"col_down_speed": "Vitesse de téléchargement",
"col_up_speed": "Vitesse de téléversement",
"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",
"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",
@@ -822,7 +1092,7 @@ window.AppI18n = {
"scan_smart": "Escaneo inteligente",
"scan_smart_desc": "Escanear rápidamente la seguridad del dispositivo",
"log_export": "Exportar registros",
"log_export_desc": "Filtrar registros y exportar a un archivo",
"log_export_desc": "Filtrer registros y exportar a un archivo",
"scan_virus": "Escaneo de virus",
"scan_virus_desc": "Seleccione un método de escaneo avanzado",
"btn_delete": "Eliminar",
@@ -865,6 +1135,7 @@ window.AppI18n = {
"scan_context_desc": "Habilitar la función de escaneo con el botón derecho",
"theme_color": "Tema de color",
"theme_color_desc": "Seleccione el tema de color de la aplicación",
"theme_system": "Tema del sistema",
"theme_white": "Modo claro",
"theme_red": "Tema rojo",
"theme_yellow": "Tema amarillo",
@@ -883,12 +1154,8 @@ window.AppI18n = {
"update_check": "Buscar actualizaciones",
"update_check_desc": "Comprobar las últimas actualizaciones",
"search_proc": "Buscar procesos...",
"btn_view": "Ver",
"btn_end": "Finalizar tarea",
"manage_white": "Gestor de lista blanca",
"search_file": "Buscar archivos...",
"btn_add": "Añadir",
"btn_remove": "Eliminar",
"manage_quarantine": "Gestor de cuarentena",
"btn_scan": "Escanear",
"search_window": "Buscar ventanas...",
@@ -913,7 +1180,6 @@ window.AppI18n = {
"msg_click_target": "Haga clic en la ventana objetivo en 5s...",
"msg_confirm_block": "Confirmar bloqueo",
"msg_repaired": "Elementos seleccionados reparados",
"btn_repair": "Reparar",
"title_prompt": "Aviso",
"title_error": "Error",
"path_unknown": "Ruta desconocida",
@@ -950,7 +1216,56 @@ window.AppI18n = {
"res_net_block": "Red bloqueada",
"res_drv_block": "Controlador bloqueado",
"res_sys_repair": "Reparación del sistema",
"res_cfg_update": "Actualización de configuración"
"res_cfg_update": "Actualización de configuración",
"manage_startup": "Gestor de inicio",
"manage_startup_desc": "Administre aplicaciones y servicios de inicio",
"col_status": "Estado",
"status_enabled": "Habilitado",
"status_disabled": "Deshabilitado",
"search_item": "Buscar elementos...",
"type_reg": "Registro",
"type_srv": "Servicio",
"type_tsk": "Tarea",
"btn_view_item": "Ver elemento",
"btn_end": "Terminar proceso",
"btn_enable_item": "Habilitar elemento",
"btn_disable_item": "Deshabilitar elemento",
"btn_delete_item": "Eliminar elemento",
"btn_repair_item": "Reparar elemento",
"btn_add_item": "Añadir elemento",
"btn_remove_item": "Eliminar elemento",
"btn_add_file": "Añadir archivo",
"btn_add_path": "Añadir ruta",
"manage_netmon": "Monitor de tráfico",
"manage_netmon_desc": "Monitorea el tráfico de red en tiempo real",
"col_down_speed": "Velocidad de descarga",
"col_up_speed": "Velocidad de subida",
"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",
"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",
@@ -1001,6 +1316,7 @@ window.AppI18n = {
"scan_context_desc": "राइट-क्लिक स्कैन कार्यक्षमता सक्षम करें",
"theme_color": "रंग थीम",
"theme_color_desc": "एप्लिकेशन का रंग थीम चुनें",
"theme_system": "सिस्टम थीम",
"theme_white": "लाइट मोड",
"theme_red": "लाल थीम",
"theme_yellow": "पीला थीम",
@@ -1019,12 +1335,8 @@ window.AppI18n = {
"update_check": "अद्यतन जाँचे",
"update_check_desc": "नवीनतम अपडेट के लिए जाँचें",
"search_proc": "प्रक्रियाएँ खोजें...",
"btn_view": "देखें",
"btn_end": "कार्य समाप्त करें",
"manage_white": "श्वेतसूची प्रबंधक",
"search_file": "फ़ाइलें खोजें...",
"btn_add": "जोड़ें",
"btn_remove": "निकालें",
"manage_quarantine": "संगरोध प्रबंधक",
"btn_scan": "स्कैन",
"search_window": "विंडोज़ खोजें...",
@@ -1049,7 +1361,6 @@ window.AppI18n = {
"msg_click_target": "5 सेकंड के भीतर लक्ष्य विंडो पर क्लिक करें...",
"msg_confirm_block": "ब्लॉक की पुष्टि करें",
"msg_repaired": "चयनित आइटम मरम्मत किए गए",
"btn_repair": "मरम्मत",
"title_prompt": "सुझाव",
"title_error": "त्रुटि",
"path_unknown": "अज्ञात पथ",
@@ -1086,7 +1397,56 @@ window.AppI18n = {
"res_net_block": "नेटवर्क अवरुद्ध",
"res_drv_block": "ड्राइवर अवरुद्ध",
"res_sys_repair": "सिस्टम मरम्मत",
"res_cfg_update": "कॉन्फ़िगरेशन अपडेट"
"res_cfg_update": "कॉन्फ़िगरेशन अपडेट",
"manage_startup": "स्टार्टअप प्रबंधक",
"manage_startup_desc": "स्टार्टअप एप्लिकेशन और सेवाएं प्रबंधित करें",
"col_status": "स्थिति",
"status_enabled": "सक्षम",
"status_disabled": "अक्षम",
"search_item": "आइटम खोजें...",
"type_reg": "रजिस्ट्री",
"type_srv": "सेवा",
"type_tsk": "कार्य",
"btn_view_item": "आइटम देखें",
"btn_end": "प्रक्रिया समाप्त करें",
"btn_enable_item": "आइटम सक्षम करें",
"btn_disable_item": "आइटम अक्षम करें",
"btn_delete_item": "आइटम हटाएं",
"btn_repair_item": "आइटम की मरम्मत करें",
"btn_add_item": "आइटम जोड़ें",
"btn_remove_item": "आइटम निकालें",
"btn_add_file": "फ़ाइल जोड़ें",
"btn_add_path": "पथ जोड़ें",
"manage_netmon": "ट्रैफ़िक मॉनिटर",
"manage_netmon_desc": "रीयल-टाइम में नेटवर्क ट्रैफ़िक की निगरानी करें",
"col_down_speed": "डाउनलोड गति",
"col_up_speed": "अपलोड गति",
"col_conn_count": "कनेक्शन",
"btn_extract_item": "आइटम निकालें",
"msg_extract_success": "चयनित आइटम सफलतापूर्वक निकाले गए",
"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",
@@ -1137,6 +1497,7 @@ window.AppI18n = {
"scan_context_desc": "تمكين ميزة الفحص بزر الماوس الأيمن",
"theme_color": "سمة اللون",
"theme_color_desc": "تحديد سمة ألوان التطبيق",
"theme_system": "سمة النظام",
"theme_white": "الوضع الفاتح",
"theme_red": "سمة حمراء",
"theme_yellow": "سمة صفراء",
@@ -1155,12 +1516,8 @@ window.AppI18n = {
"update_check": "التحقق من التحديثات",
"update_check_desc": "التحقق من أحدث التحديثات",
"search_proc": "البحث في العمليات...",
"btn_view": "عرض",
"btn_end": "إنهاء المهمة",
"manage_white": "مدير القائمة البيضاء",
"search_file": "البحث عن الملفات...",
"btn_add": "إضافة",
"btn_remove": "إزالة",
"manage_quarantine": "مدير الحجر الصحي",
"btn_scan": "فحص",
"search_window": "البحث عن النوافذ...",
@@ -1185,7 +1542,6 @@ window.AppI18n = {
"msg_click_target": "انقر على النافذة المستهدفة خلال 5 ثوانٍ...",
"msg_confirm_block": "تأكيد الحظر",
"msg_repaired": "تم إصلاح العناصر المحددة",
"btn_repair": "إصلاح",
"title_prompt": "تلميح",
"title_error": "خطأ",
"path_unknown": "مسار غير معروف",
@@ -1222,7 +1578,56 @@ window.AppI18n = {
"res_net_block": "حظر الشبكة",
"res_drv_block": "حظر برنامج التشغيل",
"res_sys_repair": "إصلاح النظام",
"res_cfg_update": "تحديث التكوين"
"res_cfg_update": "تحديث التكوين",
"manage_startup": "مدير بدء التشغيل",
"manage_startup_desc": "إدارة تطبيقات وخدمات بدء التشغيل",
"col_status": "الحالة",
"status_enabled": "مُمكّن",
"status_disabled": "مُعطّل",
"search_item": "البحث في العناصر...",
"type_reg": "التسجيل",
"type_srv": "خدمة",
"type_tsk": "مهمة",
"btn_view_item": "عرض العنصر",
"btn_end": "إنهاء العملية",
"btn_enable_item": "تمكين العنصر",
"btn_disable_item": "تعطيل العنصر",
"btn_delete_item": "حذف العنصر",
"btn_repair_item": "إصلاح العنصر",
"btn_add_item": "إضافة عنصر",
"btn_remove_item": "إزالة العنصر",
"btn_add_file": "إضافة ملف",
"btn_add_path": "إضافة مسار",
"manage_netmon": "مراقبة حركة المرور",
"manage_netmon_desc": "مراقبة حركة مرور الشبكة في الوقت الفعلي",
"col_down_speed": "سرعة التنزيل",
"col_up_speed": "سرعة الرفع",
"col_conn_count": "الاتصالات",
"btn_extract_item": "استخراج العنصر",
"msg_extract_success": "تم استخراج العناصر المحددة بنجاح",
"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",
@@ -1272,7 +1677,8 @@ window.AppI18n = {
"scan_context": "Сканирование из контекстного меню",
"scan_context_desc": "Включить функцию сканирования по правому клику",
"theme_color": "Цветовая тема",
"theme_color_desc": "Выберите цветовую тему приложения",
"theme_color_desc": "Выберите цветовой тему приложения",
"theme_system": "Системная тема",
"theme_white": "Светлый режим",
"theme_red": "Красная тема",
"theme_yellow": "Желтая тема",
@@ -1291,12 +1697,8 @@ window.AppI18n = {
"update_check": "Проверить обновления",
"update_check_desc": "Проверить наличие последних обновлений",
"search_proc": "Поиск процессов...",
"btn_view": "Просмотр",
"btn_end": "Завершить",
"manage_white": "Управление белым списком",
"search_file": "Поиск файлов...",
"btn_add": "Добавить",
"btn_remove": "Удалить",
"manage_quarantine": "Управление карантином",
"btn_scan": "Сканировать",
"search_window": "Поиск окон...",
@@ -1321,7 +1723,6 @@ window.AppI18n = {
"msg_click_target": "Нажмите на целевое окно в течение 5 сек...",
"msg_confirm_block": "Подтвердить блокировку",
"msg_repaired": "Выбранные элементы восстановлены",
"btn_repair": "Восстановить",
"title_prompt": "Подсказка",
"title_error": "Ошибка",
"path_unknown": "Неизвестный путь",
@@ -1358,7 +1759,56 @@ window.AppI18n = {
"res_net_block": "Сеть заблокирована",
"res_drv_block": "Драйвер заблокирован",
"res_sys_repair": "Восстановление системы",
"res_cfg_update": "Обновление конфигурации"
"res_cfg_update": "Обновление конфигурации",
"manage_startup": "Диспетчер автозагрузки",
"manage_startup_desc": "Управление приложениями и службами автозагрузки",
"col_status": "Статус",
"status_enabled": "Включено",
"status_disabled": "Отключено",
"search_item": "Поиск элементов...",
"type_reg": "Реестр",
"type_srv": "Служба",
"type_tsk": "Задача",
"btn_view_item": "Просмотр элемента",
"btn_end": "Завершить процесс",
"btn_enable_item": "Включить элемент",
"btn_disable_item": "Отключить элемент",
"btn_delete_item": "Удалить элемент",
"btn_repair_item": "Восстановить элемент",
"btn_add_item": "Добавить элемент",
"btn_remove_item": "Удалить элемент",
"btn_add_file": "Добавить файл",
"btn_add_path": "Добавить путь",
"manage_netmon": "Монитор трафика",
"manage_netmon_desc": "Мониторинг сетевого трафика в реальном времени",
"col_down_speed": "Скорость скачивания",
"col_up_speed": "Скорость загрузки",
"col_conn_count": "Соединения",
"btn_extract_item": "Извлечь элемент",
"msg_extract_success": "Выбранные элементы успешно извлечены",
"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",
@@ -1409,6 +1859,7 @@ window.AppI18n = {
"scan_context_desc": "Omogoči funkcijo skeniranja z desnim klikom",
"theme_color": "Barvna tema",
"theme_color_desc": "Izberite barvno temo aplikacije",
"theme_system": "Sistemska tema",
"theme_white": "Svetel način",
"theme_red": "Rdeča tema",
"theme_yellow": "Rumena tema",
@@ -1427,12 +1878,8 @@ window.AppI18n = {
"update_check": "Preveri posodobitve",
"update_check_desc": "Preveri zadnje posodobitve",
"search_proc": "Išči procese...",
"btn_view": "Poglej",
"btn_end": "Končaj opravilo",
"manage_white": "Upravitelj seznama dovoljenih",
"search_file": "Išči datoteke...",
"btn_add": "Dodaj",
"btn_remove": "Odstrani",
"manage_quarantine": "Upravitelj karantene",
"btn_scan": "Skeniraj",
"search_window": "Išči okna...",
@@ -1457,7 +1904,6 @@ window.AppI18n = {
"msg_click_target": "Kliknite na ciljno okno v 5s...",
"msg_confirm_block": "Potrdi blokado",
"msg_repaired": "Izbrani predmeti popravljeni",
"btn_repair": "Popravi",
"title_prompt": "Namig",
"title_error": "Napaka",
"path_unknown": "Neznana pot",
@@ -1494,7 +1940,56 @@ window.AppI18n = {
"res_net_block": "Omrežje blokirano",
"res_drv_block": "Gonilnik blokiran",
"res_sys_repair": "Popravilo sistema",
"res_cfg_update": "Posodobitev konfiguracije"
"res_cfg_update": "Posodobitev konfiguracije",
"manage_startup": "Upravitelj zagona",
"manage_startup_desc": "Upravljanje zagon aplikacij in storitev",
"col_status": "Stanje",
"status_enabled": "Omogočeno",
"status_disabled": "Onemogočeno",
"search_item": "Išči elemente...",
"type_reg": "Register",
"type_srv": "Storitev",
"type_tsk": "Opravilo",
"btn_view_item": "Ogled elementa",
"btn_end": "Končaj proces",
"btn_enable_item": "Omogoči element",
"btn_disable_item": "Onemogoči element",
"btn_delete_item": "Izbriši element",
"btn_repair_item": "Popravi element",
"btn_add_item": "Dodaj element",
"btn_remove_item": "Odstrani element",
"btn_add_file": "Dodaj datoteko",
"btn_add_path": "Dodaj pot",
"manage_netmon": "Nadzor prometa",
"manage_netmon_desc": "Spremlja omrežni promet v realnem času",
"col_down_speed": "Hitrost prenosa",
"col_up_speed": "Hitrost nalaganja",
"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",
"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: {
+295 -96
View File
@@ -9,10 +9,13 @@ document.addEventListener('DOMContentLoaded', () => {
const appState = {
lang: "english_switch",
theme: "system_switch",
scanning: false,
firstLaunch: false,
taskmgrTimer: null,
taskmgrActive: false,
netmonTimer: null,
netmonActive: false,
virusMap: new Map(),
virusResults: [],
junkList: []
@@ -77,13 +80,15 @@ 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}],
pathOnly: [{key: 'path', label: 'col_path', flex: 1}],
junk: [{key: 'path', label: 'col_path', flex: 3}, {key: 'sizeStr', label: 'col_size', flex: 1}],
popup: [{key: 'exe', label: 'col_prog', flex: 1}, {key: 'class', label: 'col_class', flex: 1}, {key: 'title', label: 'col_title', flex: 2}],
repair: [{key: 'display', label: 'col_repair', flex: 1, isI18n: true}]
repair: [{key: 'display', label: 'col_repair', flex: 1, isI18n: true}, {key: 'path', label: 'col_path', flex: 3}],
netmon: [{key: 'name', label: 'col_name', flex: 1.5}, {key: 'pid', label: 'col_pid', flex: 0.5}, {key: 'downStr', label: 'col_down_speed', flex: 1}, {key: 'upStr', label: 'col_up_speed', flex: 1}, {key: 'connCount', label: 'col_conn_count', flex: 0.8}]
};
const buildCustomSelectElement = (select) => {
@@ -198,7 +203,12 @@ document.addEventListener('DOMContentLoaded', () => {
};
const applyTheme = (theme) => {
const themeVars = themes[theme];
appState.theme = theme;
const actualTheme = theme === "system_switch"
? (window.matchMedia('(prefers-color-scheme: dark)').matches ? "black_switch" : "white_switch")
: theme;
const themeVars = themes[actualTheme];
if (themeVars) {
for (const [key, value] of Object.entries(themeVars)) {
document.documentElement.style.setProperty(key, value.startsWith("rgba") ? value : `rgb(${value})`);
@@ -206,6 +216,10 @@ document.addEventListener('DOMContentLoaded', () => {
}
};
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
if (appState.theme === "system_switch") applyTheme("system_switch");
});
const renderDataGrid = (containerSelector, dataList, columns, valKey, defaultChecked = false) => {
const widget = document.querySelector(`${containerSelector} .manage-widget`) || document.querySelector(`${containerSelector} .virus-widget`);
const listUl = document.querySelector(`${containerSelector} .manage-list`) || document.querySelector(`${containerSelector} .virus-list`);
@@ -327,11 +341,25 @@ 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] ?? '';
if (state.sortKey === 'sizeStr' && 'size' in a && 'size' in b) return state.sortAsc ? a.size - b.size : b.size - a.size;
const rawKeyMap = { 'sizeStr': 'size', 'downStr': 'down', 'upStr': 'up' };
if (rawKeyMap[state.sortKey] && rawKeyMap[state.sortKey] in a && rawKeyMap[state.sortKey] in b) {
const rawKey = rawKeyMap[state.sortKey];
return state.sortAsc ? a[rawKey] - b[rawKey] : b[rawKey] - a[rawKey];
}
const numA = parseFloat(valA), numB = parseFloat(valB);
if (!isNaN(numA) && !isNaN(numB) && String(valA).trim() !== '' && String(valB).trim() !== '') return state.sortAsc ? numA - numB : numB - numA;
return state.sortAsc ? String(valA).localeCompare(String(valB)) : String(valB).localeCompare(String(valA));
@@ -490,18 +518,78 @@ 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 => {
const mapped = procs.map(p => ({ name: p.name, pid: p.pid, path: (p.path && p.path !== "None" && p.path !== "Unknown") ? p.path : "path_unknown" }));
renderDataGrid('#taskmgr_window', mapped, cols.process, 'pid', false);
if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 2000);
}).catch(() => { if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 2000); });
if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 1000);
}).catch(() => { if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 1000); });
} else {
if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 2000);
if (appState.taskmgrActive) appState.taskmgrTimer = setTimeout(fetchProcs, 1000);
}
};
const fetchNetmon = () => {
if (!appState.netmonActive || !window.pywebview) return;
const widget = document.querySelector('#netmon_window .manage-widget');
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 => {
const mapped = list.map(item => ({
name: item.name,
pid: item.pid,
downStr: formatSize(item.down) + '/s',
down: item.down,
upStr: formatSize(item.up) + '/s',
up: item.up,
connCount: item.conn,
path: (item.path && item.path !== "None" && item.path !== "Unknown") ? item.path : "path_unknown"
}));
renderDataGrid('#netmon_window', mapped, cols.netmon, 'pid', false);
if (appState.netmonActive) appState.netmonTimer = setTimeout(fetchNetmon, 1000);
}).catch(() => { if (appState.netmonActive) appState.netmonTimer = setTimeout(fetchNetmon, 1000); });
} else {
if (appState.netmonActive) appState.netmonTimer = setTimeout(fetchNetmon, 1000);
}
};
const fetchStartup = () => {
if (!window.pywebview) return;
const listUl = document.querySelector('#startup_window .manage-list');
if (listUl && !listUl.children.length) {
listUl.innerHTML = `<li class="manage-list-item" style="pointer-events: none; justify-content: center; height: 100%; border: none;"><div style="color: var(--text-secondary); padding: 40px; font-size: 15px;">${getMsg('msg_init')}</div></li>`;
}
window.pywebview.api.get_startup_list().then(items => {
const mapped = items.map(item => ({ id: item.id, type: item.type, name: item.name, status: item.status === 'enabled' ? 'status_enabled' : 'status_disabled', path: item.path || 'path_unknown' }));
renderDataGrid('#startup_window', mapped, cols.startup, 'id', false);
});
};
const fetchJunk = () => {
if (!window.pywebview) return;
const widget = document.querySelector('#junk_window .manage-widget');
if (widget?.gridState) widget.gridState.checkedSet.clear();
const listUl = document.querySelector('#junk_window .manage-list');
if (listUl && !listUl.children.length) {
listUl.innerHTML = `<li class="manage-list-item" style="pointer-events: none; justify-content: center; height: 100%; border: none;"><div style="color: var(--text-secondary); padding: 40px; font-size: 15px;">${getMsg('msg_init')}</div></li>`;
}
window.pywebview.api.scan_system_junk().then(list => {
appState.junkList = list.map(item => ({ path: item.path, sizeStr: formatSize(item.size), size: item.size }));
renderDataGrid('#junk_window', appState.junkList, cols.junk, 'path', true);
});
};
const fetchRepair = () => {
if (!window.pywebview) return;
const listUl = document.querySelector('#repair_window .manage-list');
if (listUl && !listUl.children.length) {
listUl.innerHTML = `<li class="manage-list-item" style="pointer-events: none; justify-content: center; height: 100%; border: none;"><div style="color: var(--text-secondary); padding: 40px; font-size: 15px;">${getMsg('msg_init')}</div></li>`;
}
window.pywebview.api.scan_system_repair().then(list => renderDataGrid('#repair_window', list, cols.repair, 'id', true));
};
const refreshConfigLists = () => {
if (!window.pywebview) return;
@@ -523,7 +611,7 @@ document.addEventListener('DOMContentLoaded', () => {
const input = document.querySelector('#junk_window .search-box input');
if (input) input.value = '';
} else if (oldActive.id === 'repair_window') {
renderDataGrid('#repair_window', [], cols.repair, 'value', true);
renderDataGrid('#repair_window', [], cols.repair, 'id', true);
}
}
@@ -535,6 +623,14 @@ document.addEventListener('DOMContentLoaded', () => {
if (appState.taskmgrActive) fetchProcs();
else if (appState.taskmgrTimer) { clearTimeout(appState.taskmgrTimer); appState.taskmgrTimer = null; }
appState.netmonActive = targetId === 'netmon_window';
if (appState.netmonActive) fetchNetmon();
else if (appState.netmonTimer) { clearTimeout(appState.netmonTimer); appState.netmonTimer = null; }
if (targetId === 'startup_window') fetchStartup();
if (targetId === 'junk_window') fetchJunk();
if (targetId === 'repair_window') fetchRepair();
if (['whitelist_window', 'quarantine_window', 'popup_window', 'custom_protect_window'].includes(targetId)) refreshConfigLists();
if (targetId === 'log_export_window' && window.pywebview) {
@@ -596,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';
@@ -683,103 +783,188 @@ document.addEventListener('DOMContentLoaded', () => {
});
});
document.querySelector('#junk_window .primary-btn')?.addEventListener('click', (e) => {
if (!window.pywebview) return;
e.target.setAttribute('data-i18n', 'msg_init'); e.target.textContent = getMsg('msg_init');
const widget = document.querySelector('#junk_window .manage-widget');
if (widget?.gridState) widget.gridState.checkedSet.clear();
window.pywebview.api.scan_system_junk().then(list => {
appState.junkList = list.map(item => ({ path: item.path, sizeStr: formatSize(item.size), size: item.size }));
renderDataGrid('#junk_window', appState.junkList, cols.junk, 'path', true);
e.target.setAttribute('data-i18n', 'btn_scan'); e.target.textContent = getMsg('btn_scan');
});
document.getElementById('taskmgr_select')?.addEventListener('change', (e) => {
const val = e.target.value;
const checkedCbs = document.querySelectorAll('#taskmgr_window .manage-list-item input[type="checkbox"]:checked');
if (checkedCbs.length === 0 || !window.pywebview) return updateCustomSelectUI('taskmgr_select', 'none');
if (val === 'view') {
checkedCbs.forEach(cb => {
if (cb.dataset.path && cb.dataset.path !== "path_unknown") window.pywebview.api.open_file_location(cb.dataset.path);
});
} else if (val === 'end') {
window.pywebview.api.show_confirm(getMsg("msg_end_proc_title"), getMsg("msg_end_proc_confirm")).then(res => {
if (res) Promise.all(Array.from(checkedCbs).map(cb => window.pywebview.api.kill_process(parseInt(cb.value)))).then(() => fetchProcs());
});
} else if (val === 'quarantine' || val === 'whitelist') {
const paths = Array.from(checkedCbs).map(cb => cb.dataset.path).filter(p => p && p !== "path_unknown");
if (paths.length > 0) {
const listKey = val === 'quarantine' ? 'quarantine' : 'white_list';
window.pywebview.api.manage_named_list(listKey, paths, 'add').then(() => {
Promise.all(Array.from(checkedCbs).map(cb => window.pywebview.api.kill_process(parseInt(cb.value)))).then(() => fetchProcs());
});
}
}
updateCustomSelectUI('taskmgr_select', 'none');
});
document.getElementById('netmon_select')?.addEventListener('change', (e) => {
const val = e.target.value;
const checkedCbs = document.querySelectorAll('#netmon_window .manage-list-item input[type="checkbox"]:checked');
if (checkedCbs.length === 0 || !window.pywebview) return updateCustomSelectUI('netmon_select', 'none');
if (val === 'view') {
checkedCbs.forEach(cb => {
if (cb.dataset.path && cb.dataset.path !== "path_unknown") window.pywebview.api.open_file_location(cb.dataset.path);
});
} else if (val === 'end') {
window.pywebview.api.show_confirm(getMsg("msg_end_proc_title"), getMsg("msg_end_proc_confirm")).then(res => {
if (res) Promise.all(Array.from(checkedCbs).map(cb => window.pywebview.api.kill_process(parseInt(cb.value)))).then(() => fetchNetmon());
});
}
updateCustomSelectUI('netmon_select', 'none');
});
document.querySelector('#junk_window .danger-btn')?.addEventListener('click', () => {
document.getElementById('startup_select')?.addEventListener('change', (e) => {
const val = e.target.value;
const checked = getCheckedValues('#startup_window');
if (checked.length > 0 && window.pywebview) {
if (val === 'enable' || val === 'disable') {
const widget = document.querySelector('#startup_window .manage-widget');
if (widget && widget.gridState && widget.gridState.lastDisplayData) {
widget.gridState.lastDisplayData.forEach(item => {
if (checked.includes(item.id)) item.status = val === 'enable' ? 'status_enabled' : 'status_disabled';
});
widget.renderData();
}
}
window.pywebview.api.manage_startup(checked, val).then(fetchStartup);
}
updateCustomSelectUI('startup_select', 'none');
});
document.getElementById('junk_select')?.addEventListener('change', (e) => {
const val = e.target.value;
const checked = getCheckedValues('#junk_window');
if (checked.length === 0 || !window.pywebview) return;
window.pywebview.api.clean_system_junk(checked).then(size => {
window.pywebview.api.show_alert(getMsg("title_prompt"), `${getMsg("msg_cleaned")}${formatSize(size)}`, "info");
appState.junkList = appState.junkList.filter(item => !checked.includes(item.path));
renderDataGrid('#junk_window', appState.junkList, cols.junk, 'path', true);
});
});
document.querySelector('#repair_window .primary-btn')?.addEventListener('click', () => {
if (!window.pywebview) return;
window.pywebview.api.scan_system_repair().then(list => renderDataGrid('#repair_window', list, cols.repair, 'value', true));
if (val === 'view') {
checked.forEach(path => window.pywebview.api.open_file_location(path));
} else if (val === 'delete') {
if (checked.length > 0) {
window.pywebview.api.clean_system_junk(checked).then(size => {
window.pywebview.api.show_alert(getMsg("title_prompt"), `${getMsg("msg_cleaned")}${formatSize(size)}`, "info");
appState.junkList = appState.junkList.filter(item => !checked.includes(item.path));
renderDataGrid('#junk_window', appState.junkList, cols.junk, 'path', true);
});
}
}
updateCustomSelectUI('junk_select', 'none');
});
document.querySelector('#repair_window .danger-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#repair_window');
if (checked.length === 0 || !window.pywebview) return;
window.pywebview.api.execute_system_repair(checked).then(() => {
window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_repaired"), "info");
renderDataGrid('#repair_window', [], cols.repair, 'value');
});
document.getElementById('repair_select')?.addEventListener('change', (e) => {
const val = e.target.value;
if (val === 'repair') {
const checked = getCheckedValues('#repair_window');
if (checked.length > 0 && window.pywebview) {
window.pywebview.api.execute_system_repair(checked).then(() => {
window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_repaired"), "info");
renderDataGrid('#repair_window', [], cols.repair, 'id', true);
});
}
}
updateCustomSelectUI('repair_select', 'none');
});
['whitelist_window', 'quarantine_window', 'popup_window', 'custom_protect_window'].forEach(id => {
const listKey = id === 'popup_window' ? 'block_list' : (id === 'custom_protect_window' ? 'custom_rule' : id.replace('_window', ''));
document.querySelector(`#${id} .modern-btn:not(.primary-btn)`)?.addEventListener('click', () => {
const checked = getCheckedValues(`#${id}`);
if (checked.length > 0 && window.pywebview) window.pywebview.api.remove_list_items(listKey, checked).then(refreshConfigLists);
});
if (id !== 'popup_window') {
document.querySelector(`#${id} .primary-btn`)?.addEventListener('click', () => {
window.pywebview?.api.select_files().then(files => {
const listKeyMap = {
'popup_window': 'block_list',
'custom_protect_window': 'custom_rule',
'whitelist_window': 'white_list',
'quarantine_window': 'quarantine'
};
const listKey = listKeyMap[id];
const selectId = id.replace('_window', '_select');
document.getElementById(selectId)?.addEventListener('change', (e) => {
const val = e.target.value;
if (!window.pywebview) return;
if (val === 'add') {
if (id === 'popup_window') {
const wrapper = document.getElementById(selectId).nextElementSibling;
const triggerText = wrapper.querySelector('.custom-select-text');
const originalI18n = triggerText.getAttribute('data-i18n');
const originalText = triggerText.textContent;
triggerText.setAttribute('data-i18n', 'msg_click_target');
triggerText.textContent = getMsg('msg_click_target');
window.pywebview.api.capture_popup_window().then(rule => {
triggerText.setAttribute('data-i18n', originalI18n || 'btn_select');
triggerText.textContent = originalText;
if (rule) window.pywebview.api.add_popup_rule(rule).then(ok => ok && refreshConfigLists());
});
} else {
window.pywebview.api.select_files().then(files => {
if (files?.length > 0) window.pywebview.api.manage_named_list(listKey, files, 'add').then(refreshConfigLists);
});
}
} else if (val === 'add_file') {
window.pywebview.api.select_files().then(files => {
if (files?.length > 0) window.pywebview.api.manage_named_list(listKey, files, 'add').then(refreshConfigLists);
});
});
}
});
document.querySelector('#popup_window .primary-btn')?.addEventListener('click', (e) => {
if (!window.pywebview) return;
e.target.setAttribute('data-i18n', 'msg_click_target'); e.target.textContent = getMsg('msg_click_target'); e.target.disabled = true;
window.pywebview.api.capture_popup_window().then(rule => {
e.target.setAttribute('data-i18n', 'btn_add'); e.target.textContent = getMsg('btn_add'); e.target.disabled = false;
if (rule) window.pywebview.api.add_popup_rule(rule).then(ok => ok && refreshConfigLists());
});
});
document.querySelector('#taskmgr_window .primary-btn')?.addEventListener('click', () => {
if (!window.pywebview) return;
document.querySelectorAll('#taskmgr_window .manage-list-item input[type="checkbox"]:checked').forEach(cb => {
if (cb.dataset.path && cb.dataset.path !== "path_unknown") window.pywebview.api.open_file_location(cb.dataset.path);
});
});
document.querySelector('#taskmgr_window .danger-btn')?.addEventListener('click', () => {
const cbs = document.querySelectorAll('#taskmgr_window .manage-list-item input[type="checkbox"]:checked');
if (cbs.length > 0 && window.pywebview) {
window.pywebview.api.show_confirm(getMsg("msg_end_proc_title"), getMsg("msg_end_proc_confirm")).then(res => {
if (res) Promise.all(Array.from(cbs).map(cb => window.pywebview.api.kill_process(parseInt(cb.value)))).then(() => {
window.pywebview.api.get_process_list().then(procs => renderDataGrid('#taskmgr_window', procs.map(p => ({ name: p.name, pid: p.pid, path: (p.path && p.path !== "None" && p.path !== "Unknown") ? p.path : "path_unknown" })), cols.process, 'pid', false));
} else if (val === 'add_path') {
window.pywebview.api.select_folder().then(folder => {
if (folder?.length > 0) window.pywebview.api.manage_named_list(listKey, folder, 'add').then(refreshConfigLists);
});
});
}
});
document.querySelector('#log_export_window .primary-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#log_export_window');
if (checked.length === 0) return window.pywebview?.api.show_alert(getMsg("title_prompt"), getMsg("msg_sel_log_export"), "warning");
window.pywebview?.api.export_logs(checked).then(res => res && window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_export_success"), "info"));
});
document.querySelector('#log_export_window .danger-btn')?.addEventListener('click', () => {
const checked = getCheckedValues('#log_export_window');
if (checked.length === 0 || !window.pywebview) return;
window.pywebview.api.show_confirm(getMsg("title_prompt"), getMsg("msg_del_log_confirm")).then(res => {
if (res) window.pywebview.api.clear_logs(checked).then(() => {
window.pywebview.api.get_logs().then(logs => renderDataGrid('#log_export_window', logs.reverse().map(log => ({ time_str: `[${log.time_str}]`, level: log.level, action: log.action, source: log.source || '-', id: log.id })), cols.log, 'id', true));
window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_log_deleted"), "info");
});
} else if (val === 'extract') {
const checked = getCheckedValues(`#${id}`);
if (checked.length > 0) {
window.pywebview.api.select_folder().then(folder => {
if (folder?.length > 0) {
window.pywebview.api.extract_list_items(checked, folder[0]).then(success => {
if (success) window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_extract_success"), "info");
});
}
});
}
} else if (val === 'remove') {
const checked = getCheckedValues(`#${id}`);
if (checked.length > 0) window.pywebview.api.remove_list_items(listKey, checked).then(refreshConfigLists);
} else if (val === 'quarantine' || val === 'whitelist') {
const checked = getCheckedValues(`#${id}`);
if (checked.length > 0) {
const targetKey = val === 'quarantine' ? 'quarantine' : 'white_list';
window.pywebview.api.manage_named_list(targetKey, checked, 'add').then(() => refreshConfigLists());
}
}
updateCustomSelectUI(selectId, 'none');
});
});
document.getElementById('log_export_select')?.addEventListener('change', (e) => {
const val = e.target.value;
const checked = getCheckedValues('#log_export_window');
if (val === 'export') {
if (checked.length === 0) {
window.pywebview?.api.show_alert(getMsg("title_prompt"), getMsg("msg_sel_log_export"), "warning");
} else {
window.pywebview?.api.export_logs(checked).then(res => res && window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_export_success"), "info"));
}
} else if (val === 'delete') {
if (checked.length > 0 && window.pywebview) {
window.pywebview.api.show_confirm(getMsg("title_prompt"), getMsg("msg_del_log_confirm")).then(res => {
if (res) window.pywebview.api.clear_logs(checked).then(() => {
window.pywebview.api.get_logs().then(logs => renderDataGrid('#log_export_window', logs.reverse().map(log => ({ time_str: `[${log.time_str}]`, level: log.level, action: log.action, source: log.source || '-', id: log.id })), cols.log, 'id', true));
window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_log_deleted"), "info");
});
});
}
}
updateCustomSelectUI('log_export_select', 'none');
});
document.getElementById('minimize_button')?.addEventListener('click', () => window.pywebview?.api.minimize());
document.getElementById('close_button')?.addEventListener('click', () => window.pywebview?.api.hide_window());
@@ -837,7 +1022,18 @@ document.addEventListener('DOMContentLoaded', () => {
}
};
bindSettingAction('key_product', async () => {
if (!window.pywebview) return;
try {
const cfg = await window.pywebview.api.get_config();
if (cfg && cfg.api_key && cfg.api_key.trim() !== "") {
window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_key_activated"), "info");
}
} catch (e) {}
});
bindSettingAction('web_official', () => window.pywebview?.api.open_website());
bindSettingAction('update_check', () => {
window.pywebview?.api.check_update().then(res => {
if (res.error) window.pywebview.api.show_alert(getMsg("title_error"), getMsg("msg_update_fail"), "error");
@@ -845,6 +1041,7 @@ document.addEventListener('DOMContentLoaded', () => {
else window.pywebview.api.show_alert(getMsg("title_prompt"), `${getMsg("msg_latest_version")} ${res.current}`, "info");
});
});
bindSettingAction('opt_reset', () => {
window.pywebview?.api.show_confirm(getMsg("title_prompt"), getMsg("opt_reset_desc")).then(res => {
if (res) window.pywebview.api.reset_config().then(() => window.pywebview.api.show_alert(getMsg("title_prompt"), getMsg("msg_reset_success"), "info"));
@@ -854,13 +1051,15 @@ document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('select.modern-select').forEach(buildCustomSelectElement);
renderDataGrid('#scan_window', appState.virusResults, cols.virus, 'path', true);
renderDataGrid('#taskmgr_window', [], cols.process, 'pid', false);
renderDataGrid('#startup_window', [], cols.startup, 'id', false);
renderDataGrid('#junk_window', [], cols.junk, 'path', true);
renderDataGrid('#repair_window', [], cols.repair, 'value', true);
renderDataGrid('#repair_window', [], cols.repair, 'id', true);
renderDataGrid('#whitelist_window', [], cols.pathOnly, 'path', false);
renderDataGrid('#quarantine_window', [], cols.pathOnly, 'path', false);
renderDataGrid('#custom_protect_window', [], cols.pathOnly, 'path', false);
renderDataGrid('#popup_window', [], cols.popup, 'value', false);
renderDataGrid('#log_export_window', [], cols.log, 'id', true);
renderDataGrid('#netmon_window', [], cols.netmon, 'pid', false);
window.addEventListener('pywebviewready', () => {
window.pywebview.api.get_config().then(cfg => {
@@ -906,7 +1105,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (res !== undefined && res !== null) toggle.checked = res;
} catch { toggle.checked = false; }
finally { toggle.disabled = false; }
await new Promise(r => setTimeout(r, 200));
await new Promise(r => setTimeout(r, 100));
}
}
}
+117 -16
View File
@@ -150,6 +150,12 @@
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="startup_window">
<div class="item-info">
<h2 data-i18n>啟動項管理</h2><p data-i18n>此選項可以管理各應用啟動項</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="junk_window">
<div class="item-info">
<h2 data-i18n>垃圾清理</h2><p data-i18n>此選項可以清理暫存垃圾檔案</p>
@@ -168,6 +174,12 @@
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="netmon_window">
<div class="item-info">
<h2 data-i18n>流量監控</h2><p data-i18n>此選項可以實時監控網路流量</p>
</div>
<button class="icon-action-btn"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg></button>
</div>
<div class="list-item" style="cursor: pointer;" data-target="whitelist_window">
<div class="item-info">
<h2 data-i18n>白名單</h2><p data-i18n>此選項可以查看並管理白名單</p>
@@ -214,6 +226,7 @@
<h2 data-i18n>顯色主題</h2><p data-i18n>此選項可以選擇主題顏色</p>
</div>
<select id="theme_select" class="modern-select">
<option value="system_switch" selected data-i18n>跟隨系統</option>
<option value="white_switch" data-i18n>白色主題</option>
<option value="red_switch" data-i18n>紅色主題</option>
<option value="yellow_switch" data-i18n>黃色主題</option>
@@ -314,8 +327,36 @@ Last updated: 2025-12-19
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋進程..." placeholder="搜尋進程...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>查看</button>
<button class="action-btn modern-btn danger-btn" data-i18n>結束</button>
<div class="action-group modern-action-group">
<select id="taskmgr_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="view" data-i18n="btn_view_item">查看項目</option>
<option value="end" data-i18n="btn_end">結束進程</option>
<option value="quarantine" data-i18n="act_quarantine">加入隔離區</option>
<option value="whitelist" data-i18n="act_white">加入白名單</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="startup_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>啟動項管理</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋項目..." placeholder="搜尋項目...">
</div>
<div class="action-group modern-action-group">
<select id="startup_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="enable" data-i18n="btn_enable_item">啟用項目</option>
<option value="disable" data-i18n="btn_disable_item">禁用項目</option>
<option value="delete" data-i18n="btn_delete_item">刪除項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
@@ -329,8 +370,16 @@ Last updated: 2025-12-19
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
<div class="action-group modern-action-group">
<select id="whitelist_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="add_file" data-i18n="btn_add_file">新增檔案</option>
<option value="add_path" data-i18n="btn_add_path">新增路徑</option>
<option value="extract" data-i18n="btn_extract_item">提取項目</option>
<option value="remove" data-i18n="btn_remove_item">移除項目</option>
<option value="quarantine" data-i18n="act_quarantine">加入隔離區</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
@@ -344,8 +393,16 @@ Last updated: 2025-12-19
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
<div class="action-group modern-action-group">
<select id="quarantine_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="add_file" data-i18n="btn_add_file">新增檔案</option>
<option value="add_path" data-i18n="btn_add_path">新增路徑</option>
<option value="extract" data-i18n="btn_extract_item">提取項目</option>
<option value="remove" data-i18n="btn_remove_item">移除項目</option>
<option value="whitelist" data-i18n="act_white">加入白名單</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
@@ -359,8 +416,13 @@ Last updated: 2025-12-19
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋檔案..." placeholder="搜尋檔案...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>掃描</button>
<button class="action-btn modern-btn danger-btn" data-i18n>刪除</button>
<div class="action-group modern-action-group">
<select id="junk_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="view" data-i18n="btn_view_item">查看項目</option>
<option value="delete" data-i18n="btn_delete_item">刪除項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
@@ -370,8 +432,12 @@ Last updated: 2025-12-19
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>系統修復</h2>
<div class="spacer"></div>
<button class="action-btn modern-btn primary-btn" data-i18n>掃描</button>
<button class="action-btn modern-btn danger-btn" data-i18n>修復</button>
<div class="action-group modern-action-group">
<select id="repair_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="repair" data-i18n="btn_repair_item">修復項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
@@ -385,8 +451,33 @@ Last updated: 2025-12-19
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋窗口..." placeholder="搜尋窗口...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
<div class="action-group modern-action-group">
<select id="popup_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="add" data-i18n="btn_add_item">新增項目</option>
<option value="remove" data-i18n="btn_remove_item">移除項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
<section id="netmon_window" class="page sub-page">
<div class="sub-header-toolbar">
<button class="icon-btn" data-target="tool_window"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg></button>
<h2 data-i18n>流量監控</h2>
<div class="spacer"></div>
<div class="search-box">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋進程..." placeholder="搜尋進程...">
</div>
<div class="action-group modern-action-group">
<select id="netmon_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="view" data-i18n="btn_view_item">查看項目</option>
<option value="end" data-i18n="btn_end">結束進程</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
@@ -400,8 +491,13 @@ Last updated: 2025-12-19
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋規則..." placeholder="搜尋規則...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>加入</button>
<button class="action-btn modern-btn" data-i18n>移除</button>
<div class="action-group modern-action-group">
<select id="custom_protect_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="add" data-i18n="btn_add_item">新增項目</option>
<option value="remove" data-i18n="btn_remove_item">移除項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
@@ -415,8 +511,13 @@ Last updated: 2025-12-19
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="text" data-i18n-placeholder="搜尋日誌..." placeholder="搜尋日誌...">
</div>
<button class="action-btn modern-btn primary-btn" data-i18n>導出</button>
<button class="action-btn modern-btn danger-btn" data-i18n>刪除</button>
<div class="action-group modern-action-group">
<select id="log_export_select" class="modern-select">
<option value="none" selected disabled data-i18n="btn_select">選擇</option>
<option value="export" data-i18n="btn_export">導出項目</option>
<option value="delete" data-i18n="btn_remove_item">移除項目</option>
</select>
</div>
</div>
<div class="manage-widget flex-grow"><ul class="manage-list"></ul></div>
</section>
+1350 -880
View File
File diff suppressed because it is too large. Load diff
+304 -116
View File
@@ -1,4 +1,4 @@
import os, re, yara, time, math, json, numpy, base64, datetime, requests
import os, re, yara, time, math, json, zlib, numpy, base64, requests, datetime
import ctypes, ctypes.wintypes, hashlib, pefile, threading, onnxruntime
####################################################################################################
@@ -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)
@@ -154,7 +162,24 @@ class rule_scanner:
rule_name = str(matches[0])
label = rule_name.split("_")[0]
level = rule_name.split("_")[-1]
return f"{label}:WinPE/Unknown.A!rb", level
return f"{label}:WinPE/Unknown.{level}!yr", level
return False, False
except Exception:
return False, False
def yara_mem_scan(self, pid):
try:
if not self.rules:
return False, False
matches = self.rules.match(pid=pid)
if matches:
rule_name = str(matches[0])
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
@@ -162,6 +187,20 @@ class rule_scanner:
####################################################################################################
class pe_scanner:
_STRING_PATTERN = re.compile(b'[\x20-\x7E]{5,}')
_BYTE_HIST_KEYS = [f'ByteHist_{i:02X}' for i in range(256)]
_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}
_C_LOG_C_TABLE = numpy.zeros(2049, dtype=numpy.float64)
_C_LOG_C_TABLE[1:] = numpy.arange(1, 2049) * numpy.log2(numpy.arange(1, 2049))
def __init__(self):
self.model = None
self.input_name = None
@@ -173,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
@@ -197,16 +241,105 @@ 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)
probs = counts[counts > 0] / len(arr)
return float(-numpy.sum(probs * numpy.log2(probs)))
counts = counts[counts > 0]
return float(numpy.log2(sz) - numpy.sum(counts * numpy.log2(counts)) / sz)
def _extract_strings(self, file_bytes):
res = {"StringCount": 0.0, "StringMeanLength": 0.0, "StringEntropy": 0.0}
res.update(dict.fromkeys(self._STRING_KEYS, 0.0))
matches = self._STRING_PATTERN.findall(file_bytes)
if not matches:
return res
combined_data = b''.join(matches)
count = len(matches)
total_len = len(combined_data)
res["StringCount"] = float(count)
res["StringMeanLength"] = float(total_len) / count
res["StringEntropy"] = self._calc_entropy(combined_data)
arr = numpy.frombuffer(combined_data, dtype=numpy.uint8)
counts = numpy.bincount(arr, minlength=127)
char_counts = counts[0x20:0x7F]
res.update(dict(zip(self._STRING_KEYS, char_counts.astype(numpy.float64) / total_len)))
return res
def _extract_histograms(self, file_bytes):
arr = numpy.frombuffer(file_bytes, dtype=numpy.uint8)
sz = len(arr)
res = {}
if sz == 0:
res.update(dict.fromkeys(self._BYTE_HIST_KEYS, 0.0))
res.update(dict.fromkeys(self._BYTE_ENT_KEYS, 0.0))
return res
byte_counts = numpy.bincount(arr, minlength=256)
byte_hist = byte_counts.astype(numpy.float64) / sz
res.update(dict(zip(self._BYTE_HIST_KEYS, byte_hist)))
ent_hist = numpy.zeros(256, dtype=numpy.float64)
window = 2048
step = 1024
if sz < window:
w_counts = byte_counts
p = w_counts[w_counts > 0] / float(sz)
entropy = float(-numpy.sum(p * numpy.log2(p)))
ent_bin = min(int(entropy * 2.0), 15)
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
for w_start in range(0, num_windows, batch_windows):
w_end = min(w_start + batch_windows, num_windows)
b_num = w_end - w_start
start_byte = w_start * step
end_byte = start_byte + b_num * step + step
b_arr = arr[start_byte:end_byte]
arr_2d = b_arr.reshape(b_num + 1, step)
offsets = (numpy.arange(b_num + 1, dtype=numpy.int32)[:, None] * 256)
flat_idx = (arr_2d.astype(numpy.int32) + offsets).ravel()
chunk_counts = numpy.bincount(flat_idx, minlength=(b_num + 1) * 256).reshape(b_num + 1, 256)
window_counts = chunk_counts[:-1] + chunk_counts[1:]
sum_c_log_c = numpy.sum(self._C_LOG_C_TABLE[window_counts], axis=1)
entropies = 11.0 - sum_c_log_c / 2048.0
ent_bins = (entropies * 2.0).astype(numpy.int32)
numpy.clip(ent_bins, 0, 15, out=ent_bins)
byte_bins = window_counts.reshape(b_num, 16, 16).sum(axis=2)
flat_ent_bins = (ent_bins[:, None] * 16 + numpy.arange(16)).ravel()
numpy.add.at(ent_hist, flat_ent_bins, byte_bins.ravel())
ent_sum = numpy.sum(ent_hist)
if ent_sum > 0:
ent_hist /= ent_sum
res.update(dict(zip(self._BYTE_ENT_KEYS, ent_hist)))
return res
def _extract_overlay_features(self, pe, fsize):
overlay_offset = pe.get_overlay_data_start_offset()
@@ -228,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
@@ -284,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)
@@ -303,15 +438,54 @@ 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):
res = {"HasSignature": 0.0, "SignatureCount": 0.0}
if not hasattr(pe, 'OPTIONAL_HEADER') or not hasattr(pe.OPTIONAL_HEADER, 'DATA_DIRECTORY'):
return res
sec_idx = 4
directories = pe.OPTIONAL_HEADER.DATA_DIRECTORY
if len(directories) <= sec_idx:
return res
sec_dir = directories[sec_idx]
if sec_dir.Size == 0 or sec_dir.VirtualAddress == 0:
return res
res["HasSignature"] = 1.0
offset = sec_dir.VirtualAddress
size = sec_dir.Size
if offset + size > fsize or offset < 0 or size < 0:
return res
count = 0
curr = offset
end = offset + size
while curr + 8 <= end:
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
if dw_length < 8:
break
count += 1
curr += (dw_length + 7) & ~7
res["SignatureCount"] = float(count)
return res
def extract_features(self, file_path):
pe = None
try:
@@ -327,6 +501,19 @@ class pe_scanner:
apis = set()
pe = pefile.PE(data=file_bytes, fast_load=True)
try:
pe.parse_rich_header()
pe.parse_data_directories(directories=[
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']
])
except Exception:
pass
base['TrustSigned'] = 1.0 if self.signer.sign_verify(file_path) else 0.0
base['FileEntropy'] = self._calc_entropy(file_bytes)
base['FileSize'] = float(fsize)
@@ -346,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'):
@@ -375,22 +561,22 @@ class pe_scanner:
if len(op.DATA_DIRECTORY) > exc_idx and op.DATA_DIRECTORY[exc_idx].Size > 0:
base['ExceptionCount'] = op.DATA_DIRECTORY[exc_idx].Size // 12 if base['Machine'] in (0x8664, 0xAA64) else 0.0
char_flags = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
for flag in char_flags:
base[f'Char_{flag:08X}_Count'] = 0.0
base[f'Char_{flag:08X}_MeanEntropy'] = 0.0
res_flags_count = dict.fromkeys(self._CHAR_COUNT_KEYS.values(), 0.0)
res_flags_ent = dict.fromkeys(self._CHAR_ENT_KEYS.values(), 0.0)
res_sec_hash = dict.fromkeys(self._SECTION_HASH_KEYS, 0.0)
if hasattr(pe, 'sections'):
base['SectionCount'] = len(pe.sections)
entropies = []
raw_sizes = []
v_sizes = []
exec_sec = 0
write_sec = 0
read_sec = 0
sec_exc = 0
exec_sec = write_sec = read_sec = sec_exc = 0
for section in pe.sections:
sec_name = section.Name.rstrip(b'\x00')
hash_val = zlib.crc32(sec_name) % 50
res_sec_hash[self._SECTION_HASH_KEYS[hash_val]] += 1.0
try:
s_data = section.get_data()
s_entropy = self._calc_entropy(s_data)
@@ -401,15 +587,19 @@ 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 char_flags:
for flag in self._CHAR_FLAGS:
if section.Characteristics & flag:
base[f'Char_{flag:08X}_Count'] += 1.0
base[f'Char_{flag:08X}_MeanEntropy'] += s_entropy
res_flags_count[self._CHAR_COUNT_KEYS[flag]] += 1.0
res_flags_ent[self._CHAR_ENT_KEYS[flag]] += s_entropy
base['SectionMaxEntropy'] = max(entropies) if entropies else 0.0
base['SectionMinEntropy'] = min(entropies) if entropies else 0.0
@@ -425,9 +615,14 @@ class pe_scanner:
base['ReadableSections'] = float(read_sec)
base['SectionException'] = float(sec_exc)
for flag in char_flags:
if base[f'Char_{flag:08X}_Count'] > 0:
base[f'Char_{flag:08X}_MeanEntropy'] = base[f'Char_{flag:08X}_MeanEntropy'] / base[f'Char_{flag:08X}_Count']
for flag in self._CHAR_FLAGS:
cnt_key = self._CHAR_COUNT_KEYS[flag]
if res_flags_count[cnt_key] > 0:
res_flags_ent[self._CHAR_ENT_KEYS[flag]] /= res_flags_count[cnt_key]
base.update(res_flags_count)
base.update(res_flags_ent)
base.update(res_sec_hash)
string_keys = ['FileDescription', 'FileVersion', 'ProductName', 'ProductVersion', 'CompanyName', 'LegalCopyright', 'Comments', 'InternalName', 'LegalTrademarks', 'SpecialBuild', 'PrivateBuild']
for key in string_keys:
@@ -438,26 +633,17 @@ 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
try:
pe.parse_data_directories(directories=[
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS']
])
except Exception:
pass
if hasattr(pe, 'DIRECTORY_ENTRY_TLS') and hasattr(pe.DIRECTORY_ENTRY_TLS, 'struct'):
if getattr(pe.DIRECTORY_ENTRY_TLS.struct, 'AddressOfCallBacks', 0) != 0:
base['HasTlsCallbacks'] = 1.0
@@ -473,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):
@@ -486,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
@@ -510,11 +699,14 @@ class pe_scanner:
else:
base['DebugCount'] = 0.0
base.update(self._extract_strings(file_bytes))
base.update(self._extract_histograms(file_bytes))
base.update(self._extract_overlay_features(pe, fsize))
base.update(self._extract_rich_header(pe))
base.update(self._extract_ep_anomalies(pe))
base.update(self._extract_advanced_resources(pe))
base.update(self._extract_load_config(pe))
base.update(self._extract_security_directory(pe, file_bytes, fsize))
for k in base:
base[k] = self._safe_float(base[k])
@@ -539,16 +731,31 @@ class pe_scanner:
base = raw_data.get('Base', {})
dlls = set(raw_data.get('DLLs', []))
apis = set(raw_data.get('APIs', []))
feat_map = {feat: i for i, feat in enumerate(self.feature_order)}
for i, feat in enumerate(self.feature_order):
if feat.startswith('Dll_'):
if feat[4:] in dlls:
vec[0, i] = 1.0
elif feat.startswith('Api_'):
if feat[4:] in apis:
vec[0, i] = 1.0
else:
vec[0, i] = base.get(feat, 0.0)
for k, v in base.items():
if k in feat_map:
vec[0, feat_map[k]] = v
for d in dlls:
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:
vec[0, feat_map[new_fn]] += 1.0
for a in apis:
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:
vec[0, feat_map[new_fn]] += 1.0
outputs = self.model.run(None, {self.input_name: vec})
result = outputs[1]
@@ -558,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])
@@ -577,69 +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=5242880, need_rescan=False):
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:
status_data = status_req.json()
current_status = status_data.get('status')
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
@@ -647,40 +838,34 @@ class cloud_scanner:
if file_size > 104857600:
return False, sha256
if file_size > chunk_size:
total_chunks = (file_size + chunk_size - 1) // chunk_size
upload_id = os.urandom(16).hex()
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
}
chunk_success = False
for _ in range(3):
r = self._request("POST", "/api/upload", files={'file': (os.path.basename(file_path), chunk_data)}, headers=headers)
if r:
chunk_success = True
break
time.sleep(1)
if not chunk_success:
return False, sha256
return True, sha256
total_chunks = max(1, (file_size + chunk_size - 1) // chunk_size)
upload_id = os.urandom(16).hex()
with open(file_path, 'rb') as f:
file_data = f.read()
for _ in range(3):
r = self._request("POST", "/api/upload", files={'file': (os.path.basename(file_path), file_data)})
if r:
return True, sha256
time.sleep(1)
return False, sha256
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}
chunk_success = False
for attempt in range(max_retries):
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:
resp = r.json()
if 'url' in resp:
sha256 = resp.get('url', '').split('/')[-1]
except Exception:
pass
chunk_success = True
break
time.sleep(2 ** attempt)
if not chunk_success:
return False, sha256
return True, sha256
except Exception:
return False, None
@@ -690,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', {})
@@ -718,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
@@ -725,7 +912,8 @@ class cloud_scanner:
sim_malicious_count += 1
if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count):
return f"General:WinPE/Unknown.{score}!cl"
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.5.0',
version='3.5.9.0',
company_name="PYAS Security",
file_description="PYAS Security Antivirus",
internal_name="PYAS",
+20 -15
View File
@@ -1,6 +1,7 @@
#include "DriverCommon.h"
DRIVER_DATA GlobalData;
static BOOLEAN g_ImageNotifyRegistered = FALSE;
static NTSTATUS InstanceSetup(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_SETUP_FLAGS Flags, DEVICE_TYPE VolumeDeviceType, FLT_FILESYSTEM_TYPE VolumeFilesystemType) {
UNREFERENCED_PARAMETER(FltObjects);
@@ -27,9 +28,7 @@ static VOID InstanceTeardownComplete(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTA
}
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
if (!(Flags & FLTFL_FILTER_UNLOAD_MANDATORY)) {
return STATUS_FLT_DO_NOT_DETACH;
}
UNREFERENCED_PARAMETER(Flags);
if (GlobalData.ServerPort) {
FltCloseCommunicationPort(GlobalData.ServerPort);
@@ -41,12 +40,14 @@ static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
GlobalData.FilterHandle = NULL;
}
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
if (g_ImageNotifyRegistered) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
g_ImageNotifyRegistered = FALSE;
}
UninitializeRegistryProtection();
UninitializeProcessProtection();
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
UnloadRules();
UninitializeRulesEngine();
@@ -61,12 +62,9 @@ static NTSTATUS PortMessage(PVOID PortCookie, PVOID InputBuffer, ULONG InputBuff
if (InputBuffer && InputBufferLength >= sizeof(PYAS_USER_MESSAGE)) {
PPYAS_USER_MESSAGE msg = (PPYAS_USER_MESSAGE)InputBuffer;
if (msg->Command == 1 || msg->Command == 2) {
msg->Path[MAX_PATH_LEN - 1] = L'\0';
UNICODE_STRING us;
RtlInitUnicodeString(&us, msg->Path);
if (us.Length > 0) {
if (msg->Command == 1) {
AddDynamicWhitelist(&us);
@@ -178,22 +176,29 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg
}
if (!NT_SUCCESS(status)) {
if (GlobalData.FilterHandle) {
FltUnregisterFilter(GlobalData.FilterHandle);
GlobalData.FilterHandle = NULL;
}
FltUnregisterFilter(GlobalData.FilterHandle);
GlobalData.FilterHandle = NULL;
UnloadRules();
UninitializeRulesEngine();
return status;
}
InitializeProcessProtection();
InitializeRegistryProtection(DriverObject);
PsSetLoadImageNotifyRoutine(ImageLoadNotify);
NTSTATUS imageNotifyStatus = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
if (NT_SUCCESS(imageNotifyStatus)) {
g_ImageNotifyRegistered = TRUE;
}
status = FltStartFiltering(GlobalData.FilterHandle);
if (!NT_SUCCESS(status)) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
if (g_ImageNotifyRegistered) {
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
g_ImageNotifyRegistered = FALSE;
}
UninitializeRegistryProtection();
UninitializeProcessProtection();
+10 -3
View File
@@ -189,8 +189,7 @@ static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE
}
NTSTATUS InitializeProcessProtection() {
static UNICODE_STRING Altitude = { 0 };
RtlInitUnicodeString(&Altitude, L"320000");
static UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"320000.PYAS.Ob");
ObCallbacks[0].ObjectType = PsProcessType;
ObCallbacks[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
@@ -208,7 +207,15 @@ NTSTATUS InitializeProcessProtection() {
ObRegistration.RegistrationContext = NULL;
ObRegistration.OperationRegistration = ObCallbacks;
return ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle);
NTSTATUS status = ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle);
if (!NT_SUCCESS(status) && (status == STATUS_FLT_INSTANCE_ALTITUDE_COLLISION || status == STATUS_OBJECT_NAME_COLLISION)) {
static UNICODE_STRING FallbackAltitude = RTL_CONSTANT_STRING(L"320000.PYAS.Ob.Fallback");
ObRegistration.Altitude = FallbackAltitude;
status = ObRegisterCallbacks(&ObRegistration, &ObRegistrationHandle);
}
return status;
}
VOID UninitializeProcessProtection() {
+9 -3
View File
@@ -235,9 +235,15 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
}
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject) {
UNICODE_STRING Altitude;
RtlInitUnicodeString(&Altitude, L"320000");
return CmRegisterCallbackEx(RegistryCallback, &Altitude, DriverObject, NULL, &Cookie, NULL);
static UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"320000.PYAS.Cm");
NTSTATUS status = CmRegisterCallbackEx(RegistryCallback, &Altitude, DriverObject, NULL, &Cookie, NULL);
if (!NT_SUCCESS(status)) {
static UNICODE_STRING FallbackAltitude = RTL_CONSTANT_STRING(L"320000.PYAS.Cm.Fallback");
status = CmRegisterCallbackEx(RegistryCallback, &FallbackAltitude, DriverObject, NULL, &Cookie, NULL);
}
return status;
}
VOID UninitializeRegistryProtection() {
+2 -2
View File
@@ -525,7 +525,7 @@ BOOLEAN IsProcessTrusted(HANDLE ProcessId) {
if (g_CacheInitialized) {
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
ULONG Hash = ((ULONG)(ULONG_PTR)ProcessId) & (TRUST_CACHE_SIZE - 1);
ULONG Hash = (((ULONG)(ULONG_PTR)ProcessId) >> 2) & (TRUST_CACHE_SIZE - 1);
if (TrustCache[Hash].Process == Process && TrustCache[Hash].ProcessCreateTime.QuadPart == createTime.QuadPart) {
LARGE_INTEGER Now;
@@ -580,7 +580,7 @@ update_cache:
if (g_CacheInitialized) {
KIRQL OldIrql;
KeAcquireSpinLock(&TrustCacheLock, &OldIrql);
ULONG Hash = ((ULONG)(ULONG_PTR)ProcessId) & (TRUST_CACHE_SIZE - 1);
ULONG Hash = (((ULONG)(ULONG_PTR)ProcessId) >> 2) & (TRUST_CACHE_SIZE - 1);
TrustCache[Hash].Process = Process;
TrustCache[Hash].ProcessCreateTime = createTime;
TrustCache[Hash].IsTrusted = isTrusted;
+231 -224
View File
@@ -1,226 +1,233 @@
{
"Rule_Registry_BlockList": [
"\\REGISTRY\\MACHINE\\BCD00000000",
"\\REGISTRY\\MACHINE\\BCD00000000\\*",
"*\\Classes\\.exe",
"*\\Classes\\.bat",
"*\\Classes\\.cmd",
"*\\Classes\\.com",
"*\\Classes\\exefile\\*",
"*\\Classes\\batfile\\*",
"*\\Classes\\cmdfile\\*",
"*\\Classes\\comfile\\*",
"*\\mouclass",
"*\\kbdclass",
"*\\DisableAntiSpyware",
"*\\DisableWindowsUpdateAccess",
"*\\EnableLUA",
"*\\ConsentPromptBehaviorAdmin",
"*\\NoControlPanel",
"*\\NoDrives",
"*\\NoFileMenu",
"*\\NoFind",
"*\\NoStartMenuPinnedList",
"*\\NoSetFolders",
"*\\NoSetFolderOptions",
"*\\NoViewOnDrive",
"*\\NoClose",
"*\\NoDesktop",
"*\\NoLogoff",
"*\\NoFolderOptions",
"*\\RestrictRun",
"*\\NoViewContextMenu",
"*\\HideClock",
"*\\NoStartMenuMyGames",
"*\\NoStartMenuMyMusic",
"*\\DisableCMD",
"*\\NoAddingComponents",
"*\\NoWinKeys",
"*\\NoStartMenuLogOff",
"*\\NoSimpleNetIDList",
"*\\NoLowDiskSpaceChecks",
"*\\DisableLockWorkstation",
"*\\Restrict_Run",
"*\\DisableTaskMgr",
"*\\DisableRegistryTools",
"*\\DisableChangePassword",
"*\\NoComponents",
"*\\NoStartMenuMorePrograms",
"*\\NoActiveDesktop",
"*\\NoSetActiveDesktop",
"*\\NoRecentDocsMenu",
"*\\NoWindowsUpdate",
"*\\NoChangeStartMenu",
"*\\NoFavoritesMenu",
"*\\NoRecentDocsHistory",
"*\\NoSetTaskbar",
"*\\NoSMHelp",
"*\\NoTrayContextMenu",
"*\\NoManageMyComputerVerb",
"*\\NoRealMode",
"*\\NoRun",
"*\\ClearRecentDocsOnExit",
"*\\NoActiveDesktopChanges",
"*\\NoStartMenuNetworkPlaces",
"*\\NoLockScreen",
"*\\HideFastUserSwitching",
"*\\ConsentPromptBehaviorUser",
"*\\EnableSecureUIAPaths",
"*\\NoSRM",
"*\\NoInteractiveServices",
"*\\ShutdownWithoutLogon",
"*\\AutoAdminLogon",
"*\\BootExecute",
"*\\SubSystems",
"*\\FirmwareBootDevice",
"*\\BootDriverFlags",
"*\\SystemStartOptions",
"*\\DisableAutomaticRestartOnFailure",
"*\\SystemBiosVersion",
"*\\NoTheme",
"*\\SystemSetupInProgress",
"*\\SetupType",
"*\\AutoReboot",
"*\\Userinit",
"*\\UIHost",
"*\\Debugger",
"*\\DefaultUserName",
"*\\DefaultPassword",
"*\\AltDefaultUserName",
"*\\AltDefaultPassword",
"*\\DisableCAD",
"*\\LegalNoticeCaption",
"*\\LegalNoticeText",
],
"Rule_Registry_TrustedList": [
],
"Rule_Process_TrustedPaths": [
"\\Device\\HarddiskVolume*\\Windows\\*",
"\\Device\\HarddiskVolume*\\Program Files\\*",
"\\Device\\HarddiskVolume*\\Program Files (x86)\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender Advanced Threat Protection\\*",
"*\\Sources\\SetupHost.exe",
"*\\Work\\*\\DismHost.exe",
"*\\Google\\Chrome\\Application\\chrome.exe",
"*\\Microsoft\\Edge\\Application\\msedge.exe",
"*\\Mozilla Firefox\\firefox.exe",
"*\\Thorium\\Application\\thorium.exe",
"*\\Steam\\steam.exe",
"*\\steam\\*\\steam.exe",
"*\\Battle.net\\*\\Battle.net.exe",
"*\\Epic Games\\Launcher\\Portal\\Binaries\\*\\EpicGamesLauncher.exe",
"*\\resources\\bin\\SDK\\DownloadSDKServer.exe",
"*\\ThunderEv.exe",
"*\\ThunderStart.exe",
"*\\KuGou.exe",
"*\\XLGame.exe",
"*\\XLLiveUD.exe",
"*\\hrkill-*.exe",
"*\\bin\\HipsDaemon.exe",
"*\\360Safe.exe",
"*\\bilibili\\bilibili.exe",
"*\\Kingsoft\\WPS Office\\ksolaunch.exe",
"*\\Kingsoft\\WPS Office\\*\\wps.exe",
"*\\PC4399_WPFLauncher.exe",
"*\\bin\\APlayer.exe",
"*\\Neverness To Everness\\NTELauncher.exe",
"*\\Application\\AndrowsLauncher.exe",
],
"Rule_Process_ExploitableBlacklist": [
"*\\cmd.exe",
"*\\powershell.exe",
"*\\pwsh.exe",
"*\\wscript.exe",
"*\\cscript.exe",
"*\\mshta.exe",
"*\\regsvr32.exe",
"*\\certutil.exe",
"*\\bitsadmin.exe",
"*\\wmic.exe",
"*\\hh.exe",
"*\\cmstp.exe",
"*\\installutil.exe",
"*\\regasm.exe",
"*\\regsvcs.exe",
"*\\msbuild.exe",
"*\\csc.exe",
"*\\vbc.exe",
"*\\jsc.exe",
"*\\bash.exe",
"*\\scrcons.exe",
"*\\schtasks.exe",
"*\\at.exe",
"*\\reg.exe",
"*\\sc.exe",
"*\\net.exe",
"*\\net1.exe",
"*\\whoami.exe",
"*\\ipconfig.exe",
"*\\taskkill.exe",
"*\\ProcessHacker.exe",
"*\\procmon.exe",
"*\\procexp.exe",
],
"Rule_File_ProtectedPaths": [
"*\\PYAS.exe",
"*\\Plugins\\Filter\\PYAS_Driver.sys",
"*\\Plugins\\Rules\\Rules_Driver_P1.json",
"*\\ProgramData\\PYAS\\*.json",
"*\\Engine\\Pattern\\*.onnx",
"*\\Engine\\Properties\\*.onnx",
"\\Device\\HarddiskVolume*\\bootmgr",
"\\Device\\HarddiskVolume*\\boot.ini",
"\\Device\\HarddiskVolume*\\BOOTNXT",
"\\Device\\HarddiskVolume*\\EFI\\*",
"\\Device\\HarddiskVolume*\\Boot\\*",
"\\Device\\HarddiskVolume*\\Recovery\\*",
"\\Device\\HarddiskVolume*\\System Volume Information\\*",
"\\Device\\HarddiskVolume*\\Windows\\System32\\config\\*",
"*\\CON",
"*\\CON\\*",
"*\\PRN",
"*\\PRN\\*",
"*\\AUX",
"*\\AUX\\*",
"*\\NUL",
"*\\NUL\\*",
"*\\COM?",
"*\\COM?\\*",
"*\\LPT?",
"*\\LPT?\\*",
"*\\OSDATA",
"*\\OSDATA\\*",
"*\\*<*",
"*\\*>*",
],
"Rule_File_ExceptionPaths": [
"*\\Users\\*\\steam\\*",
"*\\AppData\\*",
"*\\Temp\\*",
"*\\Local\\Temp\\*",
"*\\ventoy\\Boot\\*",
"*\\AdminRegionConfig.*",
"*\\nt_qq\\nt_*\\*",
"*\\cache\\*",
],
"Rule_File_RansomwareExtensions": [
".zip", ".7z", ".rar", ".tar", ".gz",
".ppt", ".pptx", ".wps", ".txt", ".rtf",
".pdf", ".xls", ".xlsx", ".doc", ".docx",
".jpg", ".jpeg", ".png", ".webp", ".gif",
".mp3", ".wav", ".aac", ".ogg", ".flac",
".mp4", ".avi", ".mov", ".wmv", ".mkv",
]
"Rule_Registry_BlockList": [
"\\REGISTRY\\MACHINE\\BCD00000000",
"\\REGISTRY\\MACHINE\\BCD00000000\\*",
"*\\Classes\\.exe",
"*\\Classes\\.bat",
"*\\Classes\\.cmd",
"*\\Classes\\.com",
"*\\Classes\\exefile\\*",
"*\\Classes\\batfile\\*",
"*\\Classes\\cmdfile\\*",
"*\\Classes\\comfile\\*",
"*\\mouclass",
"*\\kbdclass",
"*\\DisableAntiSpyware",
"*\\DisableWindowsUpdateAccess",
"*\\EnableLUA",
"*\\ConsentPromptBehaviorAdmin",
"*\\NoControlPanel",
"*\\NoDrives",
"*\\NoFileMenu",
"*\\NoFind",
"*\\NoStartMenuPinnedList",
"*\\NoSetFolders",
"*\\NoSetFolderOptions",
"*\\NoViewOnDrive",
"*\\NoClose",
"*\\NoDesktop",
"*\\NoLogoff",
"*\\NoFolderOptions",
"*\\RestrictRun",
"*\\NoViewContextMenu",
"*\\HideClock",
"*\\NoStartMenuMyGames",
"*\\NoStartMenuMyMusic",
"*\\DisableCMD",
"*\\NoAddingComponents",
"*\\NoWinKeys",
"*\\NoStartMenuLogOff",
"*\\NoSimpleNetIDList",
"*\\NoLowDiskSpaceChecks",
"*\\DisableLockWorkstation",
"*\\Restrict_Run",
"*\\DisableTaskMgr",
"*\\DisableRegistryTools",
"*\\DisableChangePassword",
"*\\NoComponents",
"*\\NoStartMenuMorePrograms",
"*\\NoActiveDesktop",
"*\\NoSetActiveDesktop",
"*\\NoRecentDocsMenu",
"*\\NoWindowsUpdate",
"*\\NoChangeStartMenu",
"*\\NoFavoritesMenu",
"*\\NoRecentDocsHistory",
"*\\NoSetTaskbar",
"*\\NoSMHelp",
"*\\NoTrayContextMenu",
"*\\NoManageMyComputerVerb",
"*\\NoRealMode",
"*\\NoRun",
"*\\ClearRecentDocsOnExit",
"*\\NoActiveDesktopChanges",
"*\\NoStartMenuNetworkPlaces",
"*\\NoLockScreen",
"*\\HideFastUserSwitching",
"*\\ConsentPromptBehaviorUser",
"*\\EnableSecureUIAPaths",
"*\\NoSRM",
"*\\NoInteractiveServices",
"*\\ShutdownWithoutLogon",
"*\\AutoAdminLogon",
"*\\BootExecute",
"*\\SubSystems",
"*\\FirmwareBootDevice",
"*\\BootDriverFlags",
"*\\SystemStartOptions",
"*\\DisableAutomaticRestartOnFailure",
"*\\SystemBiosVersion",
"*\\NoTheme",
"*\\SystemSetupInProgress",
"*\\SetupType",
"*\\AutoReboot",
"*\\Userinit",
"*\\UIHost",
"*\\Debugger",
"*\\DefaultUserName",
"*\\DefaultPassword",
"*\\AltDefaultUserName",
"*\\AltDefaultPassword",
"*\\DisableCAD",
"*\\LegalNoticeCaption",
"*\\LegalNoticeText",
],
"Rule_Registry_TrustedList": [
],
"Rule_Process_TrustedPaths": [
"\\Device\\HarddiskVolume*\\Windows\\*",
"\\Device\\HarddiskVolume*\\Program Files\\*",
"\\Device\\HarddiskVolume*\\Program Files (x86)\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender\\*",
"\\Device\\HarddiskVolume*\\ProgramData\\Microsoft\\Windows Defender Advanced Threat Protection\\*",
"*\\Sources\\SetupHost.exe",
"*\\Work\\*\\DismHost.exe",
"*\\Google\\Chrome\\Application\\chrome.exe",
"*\\Microsoft\\Edge\\Application\\msedge.exe",
"*\\Mozilla Firefox\\firefox.exe",
"*\\Thorium\\Application\\thorium.exe",
"*\\Steam\\steam.exe",
"*\\steam\\*\\steam.exe",
"*\\Battle.net\\*\\Battle.net.exe",
"*\\Epic Games\\Launcher\\Portal\\Binaries\\*\\EpicGamesLauncher.exe",
"*\\resources\\bin\\SDK\\DownloadSDKServer.exe",
"*\\ThunderEv.exe",
"*\\ThunderStart.exe",
"*\\KuGou.exe",
"*\\XLGame.exe",
"*\\XLLiveUD.exe",
"*\\hrkill-*.exe",
"*\\bin\\HipsDaemon.exe",
"*\\360Safe.exe",
"*\\bilibili\\bilibili.exe",
"*\\Kingsoft\\WPS Office\\ksolaunch.exe",
"*\\Kingsoft\\WPS Office\\*\\wps.exe",
"*\\Kingsoft\\WPS Office\\*\\*.exe",
"*\\PC4399_WPFLauncher.exe",
"*\\bin\\APlayer.exe",
"*\\Neverness To Everness\\NTELauncher.exe",
"*\\Application\\AndrowsLauncher.exe",
"*\\bin\\*\\codex.exe",
"*\\bin\\codex.exe",
],
"Rule_Process_ExploitableBlacklist": [
"*\\cmd.exe",
"*\\powershell.exe",
"*\\pwsh.exe",
"*\\wscript.exe",
"*\\cscript.exe",
"*\\mshta.exe",
"*\\regsvr32.exe",
"*\\certutil.exe",
"*\\bitsadmin.exe",
"*\\wmic.exe",
"*\\hh.exe",
"*\\cmstp.exe",
"*\\installutil.exe",
"*\\regasm.exe",
"*\\regsvcs.exe",
"*\\msbuild.exe",
"*\\csc.exe",
"*\\vbc.exe",
"*\\jsc.exe",
"*\\bash.exe",
"*\\scrcons.exe",
"*\\schtasks.exe",
"*\\at.exe",
"*\\reg.exe",
"*\\sc.exe",
"*\\net.exe",
"*\\net1.exe",
"*\\whoami.exe",
"*\\ipconfig.exe",
"*\\taskkill.exe",
"*\\ProcessHacker.exe",
"*\\procmon.exe",
"*\\procexp.exe",
],
"Rule_File_ProtectedPaths": [
"*\\PYAS.exe",
"*\\Plugins\\Filter\\PYAS_Driver.sys",
"*\\Plugins\\Rules\\Rules_Driver_P1.json",
"*\\ProgramData\\PYAS\\*.json",
"*\\Engine\\Pattern\\*.onnx",
"*\\Engine\\Properties\\*.onnx",
"\\Device\\HarddiskVolume*\\bootmgr",
"\\Device\\HarddiskVolume*\\boot.ini",
"\\Device\\HarddiskVolume*\\BOOTNXT",
"\\Device\\HarddiskVolume*\\EFI\\*",
"\\Device\\HarddiskVolume*\\Boot\\*",
"\\Device\\HarddiskVolume*\\Recovery\\*",
"\\Device\\HarddiskVolume*\\System Volume Information\\*",
"\\Device\\HarddiskVolume*\\Windows\\System32\\config\\*",
"*\\CON",
"*\\CON\\*",
"*\\PRN",
"*\\PRN\\*",
"*\\AUX",
"*\\AUX\\*",
"*\\NUL",
"*\\NUL\\*",
"*\\COM?",
"*\\COM?\\*",
"*\\LPT?",
"*\\LPT?\\*",
"*\\OSDATA",
"*\\OSDATA\\*",
"*\\*<*",
"*\\*>*",
],
"Rule_File_ExceptionPaths": [
"*\\Users\\*\\steam\\*",
"*\\AppData\\*",
"*\\Temp\\*",
"*\\Local\\Temp\\*",
"*\\ventoy\\Boot\\*",
"*\\AdminRegionConfig.*",
"*\\nt_qq\\nt_*\\*",
"*\\cache\\*",
"*\\Documents\\My Games\\*",
"*\\Documents\\Tencent Files\\*",
"*\\Documents\\WeChat Files\\*",
"*\\Documents\\WPS Cloud Files\\*"
],
"Rule_File_RansomwareExtensions": [
".zip", ".7z", ".rar", ".tar", ".gz",
".ppt", ".pptx", ".wps", ".txt", ".rtf",
".pdf", ".xls", ".xlsx", ".doc", ".docx",
".jpg", ".jpeg", ".png", ".webp", ".gif",
".mp3", ".wav", ".aac", ".ogg", ".flac",
".mp4", ".avi", ".mov", ".wmv", ".mkv",
]
}
+1 -1
View File
@@ -132,7 +132,7 @@ graph TD
The machine learning-based virus scanning engine training achieved extremely high accuracy.
<img width="1191" height="1323" alt="train" src="https://github.com/user-attachments/assets/2eea8426-1f82-44e3-9a38-f96f9257cf50" />
<img width="895" height="1097" alt="train" src="https://github.com/user-attachments/assets/fa7cf0e2-01a1-4234-9358-718c2b8a2c9a" />
## Support System