Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e69dac4e0 | ||
|
|
839599eed1 | ||
|
|
965b3d2863 | ||
|
|
64b206bc9e | ||
|
|
3b53e542da | ||
|
|
9974a0944a | ||
|
|
d40be1d740 | ||
|
|
14832d112e | ||
|
|
3b5f67f046 | ||
|
|
d1a074b2d3 | ||
|
|
03f3efb954 | ||
|
|
f82fa135c9 | ||
|
|
0e78b970ac | ||
|
|
cd2cf87b8f | ||
|
|
20d2f94146 | ||
|
|
06e0e6a01c | ||
|
|
901ee8e34a | ||
|
|
511a00cf84 | ||
|
|
30629a2363 | ||
|
|
7fc9aab048 | ||
|
|
3a468791c3 | ||
|
|
05a7103184 | ||
|
|
4ed8173c40 | ||
|
|
fb35bc372e | ||
|
|
11da9eb3bd | ||
|
|
5da8464bb4 | ||
|
|
f42524dcd5 | ||
|
|
3be437dc6e | ||
|
|
13291c36f5 | ||
|
|
948eb51ab8 | ||
|
|
0cef48771b | ||
|
|
a45b10b5c3 | ||
|
|
7627d4e5d7 | ||
|
|
5c88711094 | ||
|
|
a7fdd41530 | ||
|
|
c315b322cd | ||
|
|
181bfc5b44 | ||
|
|
d9ea2b522a | ||
|
|
dc498949c7 | ||
|
|
f804a5b7f0 | ||
|
|
c3ef1da373 | ||
|
|
b0a3482f25 | ||
|
|
018b497575 | ||
|
|
130b040de1 | ||
|
|
bc0a62224a | ||
|
|
77525fc7c5 | ||
|
|
a304a2f02d | ||
|
|
9456659544 | ||
|
|
3ec340e0da | ||
|
|
f1f35badcb | ||
|
|
045160c482 | ||
|
|
559c493997 | ||
|
|
936475f5bf | ||
|
|
e97f8eed54 | ||
|
|
eb1955779b | ||
|
|
2e5b4cc5a1 | ||
|
|
38b4f96437 | ||
|
|
173922d0ce | ||
|
|
99303a237e | ||
|
|
8f7baf2790 | ||
|
|
b673336b3f | ||
|
|
e37400a0f3 | ||
|
|
7b9474521d | ||
|
|
78c3f2c9b8 | ||
|
|
ad256abb7c | ||
|
|
86d160ae5f | ||
|
|
fde6afb39e | ||
|
|
7c3e78296a | ||
|
|
9cc06ff767 |
No files matched your search
@@ -1437,3 +1437,22 @@ rule General_52 {
|
||||
uint16(0) == 0x5a4d and filesize < 2000KB and
|
||||
8 of them
|
||||
}
|
||||
|
||||
rule General_53 {
|
||||
meta:
|
||||
description = "53"
|
||||
author = "PYAS Security"
|
||||
date = "2026-07-02"
|
||||
strings:
|
||||
$s1 = "VUZsQlUxO" wide
|
||||
$s2 = "VRaV04xY21s" wide
|
||||
$s3 = "MGVW" wide
|
||||
$s4 = "OUJkWFJvYjNKc" wide
|
||||
$s5 = "GVtRjBhVz" wide
|
||||
$s6 = "l1WDFabGNtbG" ascii
|
||||
$s7 = "1hV05o" fullword ascii
|
||||
$s8 = "ZEdsdmJnPT0=" ascii
|
||||
condition:
|
||||
uint16(0) == 0x5a4d and filesize > 20MB and
|
||||
7 of them
|
||||
}
|
||||
@@ -9,12 +9,15 @@ try:
|
||||
JSON_DUMPS = lambda x: orjson.dumps(x).decode('utf-8')
|
||||
JSON_LOADS = orjson.loads
|
||||
HAS_ORJSON = True
|
||||
|
||||
except ImportError:
|
||||
import json
|
||||
JSON_DUMPS = json.dumps
|
||||
JSON_LOADS = json.loads
|
||||
HAS_ORJSON = False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
JSONL_PATH = "pe_features.jsonl"
|
||||
BATCH_SIZE = 1000
|
||||
MAX_FILE_SIZE = 4 * 1024 * 1024 * 1024
|
||||
@@ -69,7 +72,8 @@ V2_GUID = GUID(0x00AAC56B, 0xCD44, 0x11D0, (ctypes.c_ubyte * 8)(0x8C, 0xC2, 0x00
|
||||
####################################################################################################
|
||||
|
||||
def verify_signature(file_path):
|
||||
if os.name != 'nt': return 0
|
||||
if os.name != 'nt':
|
||||
return 0
|
||||
try:
|
||||
wintrust = ctypes.windll.wintrust
|
||||
wintrust.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.wintypes.LPVOID]
|
||||
@@ -126,16 +130,20 @@ class FeatureExtractor:
|
||||
try:
|
||||
f = float(val)
|
||||
return f if not (math.isinf(f) or math.isnan(f)) else 0.0
|
||||
|
||||
except Exception:
|
||||
return 0.0
|
||||
|
||||
@staticmethod
|
||||
def _calc_entropy(data):
|
||||
if not data: return 0.0
|
||||
if not data:
|
||||
return 0.0
|
||||
|
||||
arr = np.frombuffer(data, dtype=np.uint8)
|
||||
sz = len(arr)
|
||||
counts = np.bincount(arr, minlength=256)
|
||||
counts = counts[counts > 0]
|
||||
|
||||
return float(np.log2(sz) - np.sum(counts * np.log2(counts)) / sz)
|
||||
|
||||
@classmethod
|
||||
@@ -189,6 +197,7 @@ class FeatureExtractor:
|
||||
byte_bins = w_counts.reshape(16, 16).sum(axis=1)
|
||||
idx_start = ent_bin * 16
|
||||
ent_hist[idx_start : idx_start+16] += byte_bins
|
||||
|
||||
else:
|
||||
num_windows = (sz - window) // step + 1
|
||||
batch_windows = 10000
|
||||
@@ -247,6 +256,7 @@ class FeatureExtractor:
|
||||
def _extract_rich_header(cls, pe):
|
||||
if not hasattr(pe, 'RICH_HEADER') or not pe.RICH_HEADER:
|
||||
return {"HasRichHeader": 0.0, "RichHeaderCount": 0.0}
|
||||
|
||||
return {
|
||||
"HasRichHeader": 1.0,
|
||||
"RichHeaderCount": float(len(pe.RICH_HEADER.values) // 2) if hasattr(pe.RICH_HEADER, 'values') else 0.0
|
||||
@@ -305,6 +315,7 @@ class FeatureExtractor:
|
||||
for entry_lang in entry_id.directory.entries:
|
||||
if hasattr(entry_lang, 'id'):
|
||||
langs.add(entry_lang.id)
|
||||
|
||||
if hasattr(entry_lang, 'data'):
|
||||
try:
|
||||
data = pe.get_data(entry_lang.data.struct.OffsetToData, entry_lang.data.struct.Size)
|
||||
@@ -328,10 +339,13 @@ class FeatureExtractor:
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_LOAD_CONFIG'):
|
||||
cfg["HasLoadConfig"] = 1.0
|
||||
struct = pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct
|
||||
|
||||
if getattr(struct, 'GuardCFFunctionTable', 0) != 0:
|
||||
cfg["HasCFG"] = 1.0
|
||||
|
||||
if getattr(struct, 'SEHandlerTable', 0) != 0:
|
||||
cfg["HasSEHTable"] = 1.0
|
||||
|
||||
return cfg
|
||||
|
||||
@classmethod
|
||||
@@ -364,6 +378,7 @@ class FeatureExtractor:
|
||||
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
|
||||
if dw_length < 8:
|
||||
break
|
||||
|
||||
count += 1
|
||||
curr += (dw_length + 7) & ~7
|
||||
|
||||
@@ -372,9 +387,10 @@ class FeatureExtractor:
|
||||
|
||||
@classmethod
|
||||
def extract(cls, file_bytes, file_path, fsize):
|
||||
if fsize == 0: return None
|
||||
base, dlls, apis, pe = {}, set(), set(), None
|
||||
if fsize == 0:
|
||||
return None
|
||||
|
||||
base, dlls, apis, pe = {}, set(), set(), None
|
||||
try:
|
||||
pe = pefile.PE(data=file_bytes, fast_load=True)
|
||||
|
||||
@@ -410,7 +426,6 @@ class FeatureExtractor:
|
||||
base['Is64Bit'] = 1.0 if base['Machine'] in (0x8664, 0xAA64, 0x0200) else 0.0
|
||||
base['IsExe'] = 1.0 if pe.is_exe() else 0.0
|
||||
base['IsDll'] = 1.0 if pe.is_dll() else 0.0
|
||||
|
||||
base['ExceptionCount'] = 0.0
|
||||
|
||||
if hasattr(pe, 'OPTIONAL_HEADER'):
|
||||
@@ -452,10 +467,14 @@ class FeatureExtractor:
|
||||
raw_sizes.append(section.SizeOfRawData)
|
||||
v_sizes.append(section.Misc_VirtualSize)
|
||||
|
||||
if section.Characteristics & 0x20000000: exec_sec += 1
|
||||
if section.Characteristics & 0x80000000: write_sec += 1
|
||||
if section.Characteristics & 0x40000000: read_sec += 1
|
||||
if section.SizeOfRawData + section.PointerToRawData > fsize: sec_exc = 1
|
||||
if section.Characteristics & 0x20000000:
|
||||
exec_sec += 1
|
||||
if section.Characteristics & 0x80000000:
|
||||
write_sec += 1
|
||||
if section.Characteristics & 0x40000000:
|
||||
read_sec += 1
|
||||
if section.SizeOfRawData + section.PointerToRawData > fsize:
|
||||
sec_exc = 1
|
||||
|
||||
for flag in cls._CHAR_FLAGS:
|
||||
if section.Characteristics & flag:
|
||||
@@ -495,11 +514,13 @@ class FeatureExtractor:
|
||||
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
|
||||
for st in getattr(fileinfo, 'StringTable', []):
|
||||
for key, val in st.entries.items():
|
||||
|
||||
try:
|
||||
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
|
||||
if k in string_keys:
|
||||
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
|
||||
base[f'{k}Length'] = float(len(v))
|
||||
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
@@ -519,13 +540,19 @@ class FeatureExtractor:
|
||||
func_count = 0
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
if getattr(entry, 'dll', None):
|
||||
try: dlls.add(entry.dll.decode('ascii', 'ignore').lower())
|
||||
except Exception: pass
|
||||
try:
|
||||
dlls.add(entry.dll.decode('ascii', 'ignore').lower())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for imp in getattr(entry, 'imports', []):
|
||||
func_count += 1
|
||||
if getattr(imp, 'name', None):
|
||||
try: apis.add(imp.name.decode('ascii', 'ignore'))
|
||||
except Exception: pass
|
||||
try:
|
||||
apis.add(imp.name.decode('ascii', 'ignore'))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
base['ImportFunctionCount'] = float(func_count)
|
||||
|
||||
base['ExportCount'] = float(len(pe.DIRECTORY_ENTRY_EXPORT.symbols)) if (hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols')) else 0.0
|
||||
@@ -550,10 +577,13 @@ class FeatureExtractor:
|
||||
base.update(cls._extract_load_config(pe))
|
||||
base.update(cls._extract_security_directory(pe, file_bytes, fsize))
|
||||
|
||||
for k in base: base[k] = cls._safe_float(base[k])
|
||||
for k in base:
|
||||
base[k] = cls._safe_float(base[k])
|
||||
|
||||
return {"Base": base, "DLLs": list(dlls), "APIs": list(apis)}
|
||||
|
||||
except Exception: return None
|
||||
except Exception:
|
||||
return None
|
||||
finally:
|
||||
if pe: pe.close()
|
||||
|
||||
@@ -570,6 +600,7 @@ def load_existing_hashes():
|
||||
match = pattern.search(line)
|
||||
if match:
|
||||
hashes.add(match.group(1).decode('ascii'))
|
||||
|
||||
return hashes
|
||||
|
||||
GLOBAL_HASHES = set()
|
||||
@@ -636,6 +667,7 @@ def scan_and_save(path, label, existing_hashes):
|
||||
skipped += 1
|
||||
elif status == 'success':
|
||||
sha256, json_str = data
|
||||
|
||||
if sha256 not in existing_hashes:
|
||||
existing_hashes.add(sha256)
|
||||
batch_buffer.append(json_str)
|
||||
@@ -660,10 +692,12 @@ def scan_and_save(path, label, existing_hashes):
|
||||
print("\n\n[-] User aborted. Force terminating all child processes...")
|
||||
pool.terminate()
|
||||
pool.join()
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n[-] Critical error: {e}")
|
||||
pool.terminate()
|
||||
pool.join()
|
||||
|
||||
finally:
|
||||
if batch_buffer:
|
||||
try:
|
||||
@@ -688,15 +722,20 @@ if __name__ == "__main__":
|
||||
while True:
|
||||
print("-" * 57)
|
||||
target_path = input("\n[*] Enter path (file or directory): ").strip().strip('"').strip("'")
|
||||
if target_path.lower() in ['exit', 'q']: break
|
||||
if target_path.lower() in ['exit', 'q']:
|
||||
break
|
||||
|
||||
if not target_path or not os.path.exists(target_path):
|
||||
print("[-] Invalid path.")
|
||||
continue
|
||||
|
||||
while True:
|
||||
label_input = input("[*] Enter label (0=Safe, 1=Malware): ").strip()
|
||||
if label_input.lower() in ['exit', 'q']: sys.exit()
|
||||
if label_input in ['0', '1']: break
|
||||
if label_input.lower() in ['exit', 'q']:
|
||||
sys.exit()
|
||||
if label_input in ['0', '1']:
|
||||
break
|
||||
|
||||
print("[-] Please enter 0 or 1.")
|
||||
|
||||
scan_and_save(target_path, int(label_input), existing_hashes)
|
||||
|
||||
+149
-49
@@ -5,7 +5,7 @@ import onnxruntime as ort
|
||||
|
||||
####################################################################################################
|
||||
|
||||
MODEL_FILE = "Pefile_General_S1.onnx"
|
||||
MODEL_FILE = "Pefile_General_T1.onnx"
|
||||
FEATURE_FILE = "features.json"
|
||||
MAX_FILE_SIZE = 4 * 1024 * 1024 * 1024
|
||||
|
||||
@@ -116,16 +116,20 @@ class FeatureExtractor:
|
||||
try:
|
||||
f = float(val)
|
||||
return f if not (math.isinf(f) or math.isnan(f)) else 0.0
|
||||
|
||||
except Exception:
|
||||
return 0.0
|
||||
|
||||
@staticmethod
|
||||
def _calc_entropy(data):
|
||||
if not data: return 0.0
|
||||
if not data:
|
||||
return 0.0
|
||||
|
||||
arr = np.frombuffer(data, dtype=np.uint8)
|
||||
sz = len(arr)
|
||||
counts = np.bincount(arr, minlength=256)
|
||||
counts = counts[counts > 0]
|
||||
|
||||
return float(np.log2(sz) - np.sum(counts * np.log2(counts)) / sz)
|
||||
|
||||
@classmethod
|
||||
@@ -295,6 +299,7 @@ class FeatureExtractor:
|
||||
for entry_lang in entry_id.directory.entries:
|
||||
if hasattr(entry_lang, 'id'):
|
||||
langs.add(entry_lang.id)
|
||||
|
||||
if hasattr(entry_lang, 'data'):
|
||||
try:
|
||||
data = pe.get_data(entry_lang.data.struct.OffsetToData, entry_lang.data.struct.Size)
|
||||
@@ -318,10 +323,13 @@ class FeatureExtractor:
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_LOAD_CONFIG'):
|
||||
cfg["HasLoadConfig"] = 1.0
|
||||
struct = pe.DIRECTORY_ENTRY_LOAD_CONFIG.struct
|
||||
|
||||
if getattr(struct, 'GuardCFFunctionTable', 0) != 0:
|
||||
cfg["HasCFG"] = 1.0
|
||||
|
||||
if getattr(struct, 'SEHandlerTable', 0) != 0:
|
||||
cfg["HasSEHTable"] = 1.0
|
||||
|
||||
return cfg
|
||||
|
||||
@classmethod
|
||||
@@ -354,6 +362,7 @@ class FeatureExtractor:
|
||||
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
|
||||
if dw_length < 8:
|
||||
break
|
||||
|
||||
count += 1
|
||||
curr += (dw_length + 7) & ~7
|
||||
|
||||
@@ -406,7 +415,6 @@ class FeatureExtractor:
|
||||
base['Is64Bit'] = 1.0 if base['Machine'] in (0x8664, 0xAA64, 0x0200) else 0.0
|
||||
base['IsExe'] = 1.0 if pe.is_exe() else 0.0
|
||||
base['IsDll'] = 1.0 if pe.is_dll() else 0.0
|
||||
|
||||
base['ExceptionCount'] = 0.0
|
||||
|
||||
if hasattr(pe, 'OPTIONAL_HEADER'):
|
||||
@@ -448,10 +456,14 @@ class FeatureExtractor:
|
||||
raw_sizes.append(section.SizeOfRawData)
|
||||
v_sizes.append(section.Misc_VirtualSize)
|
||||
|
||||
if section.Characteristics & 0x20000000: exec_sec += 1
|
||||
if section.Characteristics & 0x80000000: write_sec += 1
|
||||
if section.Characteristics & 0x40000000: read_sec += 1
|
||||
if section.SizeOfRawData + section.PointerToRawData > fsize: sec_exc = 1
|
||||
if section.Characteristics & 0x20000000:
|
||||
exec_sec += 1
|
||||
if section.Characteristics & 0x80000000:
|
||||
write_sec += 1
|
||||
if section.Characteristics & 0x40000000:
|
||||
read_sec += 1
|
||||
if section.SizeOfRawData + section.PointerToRawData > fsize:
|
||||
sec_exc = 1
|
||||
|
||||
for flag in cls._CHAR_FLAGS:
|
||||
if section.Characteristics & flag:
|
||||
@@ -491,11 +503,13 @@ class FeatureExtractor:
|
||||
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
|
||||
for st in getattr(fileinfo, 'StringTable', []):
|
||||
for key, val in st.entries.items():
|
||||
|
||||
try:
|
||||
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
|
||||
if k in string_keys:
|
||||
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
|
||||
base[f'{k}Length'] = float(len(v))
|
||||
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
@@ -515,13 +529,19 @@ class FeatureExtractor:
|
||||
func_count = 0
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
if getattr(entry, 'dll', None):
|
||||
try: dlls.add(entry.dll.decode('ascii', 'ignore').lower())
|
||||
except Exception: pass
|
||||
try:
|
||||
dlls.add(entry.dll.decode('ascii', 'ignore').lower())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for imp in getattr(entry, 'imports', []):
|
||||
func_count += 1
|
||||
if getattr(imp, 'name', None):
|
||||
try: apis.add(imp.name.decode('ascii', 'ignore'))
|
||||
except Exception: pass
|
||||
try:
|
||||
apis.add(imp.name.decode('ascii', 'ignore'))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
base['ImportFunctionCount'] = float(func_count)
|
||||
|
||||
base['ExportCount'] = float(len(pe.DIRECTORY_ENTRY_EXPORT.symbols)) if (hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols')) else 0.0
|
||||
@@ -546,12 +566,16 @@ class FeatureExtractor:
|
||||
base.update(cls._extract_load_config(pe))
|
||||
base.update(cls._extract_security_directory(pe, file_bytes, fsize))
|
||||
|
||||
for k in base: base[k] = cls._safe_float(base[k])
|
||||
for k in base:
|
||||
base[k] = cls._safe_float(base[k])
|
||||
|
||||
return {"Base": base, "DLLs": list(dlls), "APIs": list(apis)}
|
||||
|
||||
except Exception: return None
|
||||
except Exception:
|
||||
return None
|
||||
finally:
|
||||
if pe: pe.close()
|
||||
if pe:
|
||||
pe.close()
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -564,16 +588,48 @@ class ModelPredictor:
|
||||
expected_dim = self.sess.get_inputs()[0].shape[1]
|
||||
current_dim = len(self.model_features)
|
||||
if current_dim != expected_dim:
|
||||
raise ValueError(f"Dimension mismatch! Model expects {expected_dim} features, but feature file lists {current_dim}.")
|
||||
raise ValueError(f"Dimension error: Model expects {expected_dim}, but feature file has {current_dim}")
|
||||
|
||||
self.feat_map = {feat: i for i, feat in enumerate(self.model_features)}
|
||||
|
||||
self.dll_hash_dim = 512
|
||||
self.api_hash_dim = 4096
|
||||
self.dll_hash_pad = 3
|
||||
self.api_hash_pad = 4
|
||||
self._parse_hash_dims()
|
||||
|
||||
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 _parse_hash_dims(self):
|
||||
max_dll = -1
|
||||
max_api = -1
|
||||
|
||||
for feat in self.model_features:
|
||||
if feat.startswith("DllHash_"):
|
||||
try:
|
||||
val_str = feat.split("_")[1]
|
||||
max_dll = max(max_dll, int(val_str))
|
||||
self.dll_hash_pad = len(val_str)
|
||||
except Exception:
|
||||
pass
|
||||
elif feat.startswith("ApiHash_"):
|
||||
try:
|
||||
val_str = feat.split("_")[1]
|
||||
max_api = max(max_api, int(val_str))
|
||||
self.api_hash_pad = len(val_str)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if max_dll >= 0:
|
||||
self.dll_hash_dim = max_dll + 1
|
||||
if max_api >= 0:
|
||||
self.api_hash_dim = max_api + 1
|
||||
|
||||
def predict(self, raw_data):
|
||||
vec = np.zeros((1, len(self.model_features)), dtype=np.float32)
|
||||
base = raw_data.get('Base', {})
|
||||
@@ -585,14 +641,14 @@ class ModelPredictor:
|
||||
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}"
|
||||
h = zlib.crc32(d.encode('utf-8', 'ignore')) % self.dll_hash_dim
|
||||
feat_name = f"DllHash_{h:0{self.dll_hash_pad}d}"
|
||||
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}"
|
||||
h = zlib.crc32(a.encode('utf-8', 'ignore')) % self.api_hash_dim
|
||||
feat_name = f"ApiHash_{h:0{self.api_hash_pad}d}"
|
||||
if feat_name in self.feat_map:
|
||||
vec[0, self.feat_map[feat_name]] += 1.0
|
||||
|
||||
@@ -604,9 +660,11 @@ class ModelPredictor:
|
||||
prob_dict = result[0]
|
||||
if hasattr(prob_dict, 'get'):
|
||||
return float(prob_dict.get(1, prob_dict.get('1', 0.0)))
|
||||
|
||||
elif isinstance(result, np.ndarray):
|
||||
if result.ndim == 2 and result.shape[1] > 1:
|
||||
return float(result[0][1])
|
||||
|
||||
return 0.0
|
||||
|
||||
####################################################################################################
|
||||
@@ -615,6 +673,7 @@ def scan_target(target, predictor):
|
||||
files = []
|
||||
if os.path.isfile(target):
|
||||
files.append(target)
|
||||
|
||||
elif os.path.isdir(target):
|
||||
for r, _, fs in os.walk(target):
|
||||
for f in fs:
|
||||
@@ -624,46 +683,83 @@ def scan_target(target, predictor):
|
||||
print("[-] No valid PE files found.")
|
||||
return
|
||||
|
||||
print(f"\n[*] Scanning {len(files)} files...\n")
|
||||
print(f"{'RESULT':<10} | {'PROB':<8} | {'FILE'}")
|
||||
print("-" * 80)
|
||||
log_buffer = []
|
||||
|
||||
def _log(message):
|
||||
print(message)
|
||||
log_buffer.append(message)
|
||||
|
||||
_log(f"[*] Scanning {len(files)} files...\n")
|
||||
_log(f"{'RESULT':<10} | {'PROB':<8} | {'FILE'}")
|
||||
_log("-" * 80)
|
||||
|
||||
safe_count = 0
|
||||
mal_count = 0
|
||||
unsupport_count = 0
|
||||
error_count = 0
|
||||
|
||||
for fpath in files:
|
||||
try:
|
||||
data = FeatureExtractor.extract(fpath)
|
||||
if not data:
|
||||
print(f"{'ERROR':<10} | {'----':<8} | {fpath}")
|
||||
continue
|
||||
|
||||
prob = predictor.predict(data)
|
||||
is_malware = prob > 0.5
|
||||
|
||||
label = "MALWARE" if is_malware else "SAFE"
|
||||
if is_malware:
|
||||
mal_count += 1
|
||||
else:
|
||||
safe_count += 1
|
||||
try:
|
||||
for fpath in files:
|
||||
try:
|
||||
data = FeatureExtractor.extract(fpath)
|
||||
if not data:
|
||||
_log(f"{'UNSUPPORT':<10} | {'-':<8} | {fpath}")
|
||||
unsupport_count += 1
|
||||
continue
|
||||
|
||||
prob = predictor.predict(data)
|
||||
is_malware = prob > 0.5
|
||||
|
||||
label = "MALWARE" if is_malware else "SAFE"
|
||||
if is_malware:
|
||||
mal_count += 1
|
||||
else:
|
||||
safe_count += 1
|
||||
|
||||
print(f"{label:<10} | {prob:.4f} | {fpath}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"{'FAIL':<10} | {'----':<8} | {fpath} ({str(e)})")
|
||||
_log(f"{label:<10} | {prob:.4f} | {fpath}")
|
||||
|
||||
except Exception as e:
|
||||
_log(f"{'FAIL':<10} | {'-':<8} | {fpath} ({str(e)})")
|
||||
error_count += 1
|
||||
|
||||
except KeyboardInterrupt:
|
||||
_log("\n[-] Scan aborted by user (Ctrl+C).\n")
|
||||
|
||||
print("-" * 80)
|
||||
print(f"[*] Summary: Safe={safe_count}, Malware={mal_count}, Total={len(files)}")
|
||||
_log("-" * 80)
|
||||
_log(f"\n[*] Summary: Safe={safe_count}, Malware={mal_count}, Unsupport={unsupport_count}, Error={error_count}, Total={len(files)}")
|
||||
|
||||
log_filename = datetime.datetime.now().strftime("%Y%m%d_%H%M%S.log")
|
||||
try:
|
||||
with open(log_filename, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(log_buffer) + "\n")
|
||||
print(f"[*] Log saved successfully: {log_filename}")
|
||||
except Exception as e:
|
||||
print(f"[-] Failed to save log: {e}")
|
||||
|
||||
####################################################################################################
|
||||
|
||||
if __name__ == "__main__":
|
||||
base_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
custom_model = MODEL_FILE
|
||||
targets = []
|
||||
|
||||
args = sys.argv[1:]
|
||||
i = 0
|
||||
while i < len(args):
|
||||
if args[i] in ("-m", "-model", "--model") and i + 1 < len(args):
|
||||
custom_model = args[i+1]
|
||||
i += 2
|
||||
else:
|
||||
targets.append(args[i])
|
||||
i += 1
|
||||
|
||||
print("\n-------------------------- PE Malware Predictor v4.0 --------------------------\n")
|
||||
print(f"[*] © 2020-2026 87owo (PYAS Security)")
|
||||
print(f"[*] Loading model features and weights...")
|
||||
print(f"[*] Official Website: https://github.com/87owo/PYAS")
|
||||
print(f"[*] Loading model: {custom_model}")
|
||||
|
||||
base_dir = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(os.path.abspath(__file__))
|
||||
model_path = os.path.join(base_dir, MODEL_FILE)
|
||||
model_path = os.path.join(base_dir, custom_model) if not os.path.isabs(custom_model) else custom_model
|
||||
feature_path = os.path.join(base_dir, FEATURE_FILE)
|
||||
|
||||
try:
|
||||
@@ -672,8 +768,8 @@ if __name__ == "__main__":
|
||||
print(f"[-] Critical Error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
for path in sys.argv[1:]:
|
||||
if targets:
|
||||
for path in targets:
|
||||
target = path.strip('"').strip("'")
|
||||
if os.path.exists(target):
|
||||
print()
|
||||
@@ -687,11 +783,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:
|
||||
|
||||
@@ -12,6 +12,7 @@ from sklearn.metrics import accuracy_score, roc_auc_score
|
||||
try:
|
||||
import orjson
|
||||
JSON_LOADS = orjson.loads
|
||||
|
||||
except ImportError:
|
||||
JSON_LOADS = json.loads
|
||||
|
||||
@@ -21,11 +22,11 @@ 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
|
||||
DLL_HASH_DIM = 512
|
||||
API_HASH_DIM = 4096
|
||||
|
||||
EXCLUDE_FEATURES = {
|
||||
'label', 'filehash',
|
||||
@@ -56,12 +57,14 @@ def analyze_schema(jsonl_path):
|
||||
with open(jsonl_path, 'rb') as f:
|
||||
for line in f:
|
||||
num_lines += 1
|
||||
|
||||
if not base_keys:
|
||||
try:
|
||||
data = JSON_LOADS(line)
|
||||
base_data = data.get('Base')
|
||||
if base_data:
|
||||
base_keys.update(base_data.keys())
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -76,6 +79,7 @@ def analyze_schema(jsonl_path):
|
||||
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
|
||||
print(f"[*] Stage 2: Building sparse feature matrix dynamically via Multiprocessing...")
|
||||
final_features = base_schema + dll_schema + api_schema
|
||||
|
||||
num_features = len(final_features)
|
||||
offset_dll = len(base_schema)
|
||||
offset_api = offset_dll + DLL_HASH_DIM
|
||||
@@ -118,6 +122,7 @@ def _chunk_worker(args):
|
||||
if f.read(1) != b'\n':
|
||||
f.seek(start_byte)
|
||||
f.readline()
|
||||
|
||||
else:
|
||||
f.seek(start_byte)
|
||||
else:
|
||||
@@ -181,9 +186,7 @@ def _calculate_dynamic_lr(current_iter: int) -> float:
|
||||
def train_process(X, y, feature_names):
|
||||
print(f"[*] Dataset shape: {X.shape}")
|
||||
|
||||
X_train, X_test, y_train, y_test = train_test_split(
|
||||
X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y
|
||||
)
|
||||
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y)
|
||||
|
||||
n_pos = (y_train == 1).sum()
|
||||
n_neg = (y_train == 0).sum()
|
||||
@@ -300,6 +303,7 @@ if __name__ == "__main__":
|
||||
try:
|
||||
model, X_test, y_test = train_process(X, y, final_features)
|
||||
evaluate_and_save(model, X_test, y_test)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n[-] Training aborted.")
|
||||
except Exception as e:
|
||||
|
||||
@@ -12,6 +12,7 @@ from sklearn.metrics import accuracy_score, roc_auc_score
|
||||
try:
|
||||
import orjson
|
||||
JSON_LOADS = orjson.loads
|
||||
|
||||
except ImportError:
|
||||
JSON_LOADS = json.loads
|
||||
|
||||
@@ -21,11 +22,11 @@ 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
|
||||
DLL_HASH_DIM = 512
|
||||
API_HASH_DIM = 4096
|
||||
|
||||
EXCLUDE_FEATURES = {
|
||||
'label', 'filehash',
|
||||
@@ -56,12 +57,14 @@ def analyze_schema(jsonl_path):
|
||||
with open(jsonl_path, 'rb') as f:
|
||||
for line in f:
|
||||
num_lines += 1
|
||||
|
||||
if not base_keys:
|
||||
try:
|
||||
data = JSON_LOADS(line)
|
||||
base_data = data.get('Base')
|
||||
if base_data:
|
||||
base_keys.update(base_data.keys())
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -76,6 +79,7 @@ def analyze_schema(jsonl_path):
|
||||
def load_data_efficiently(jsonl_path, base_schema, dll_schema, api_schema, num_lines):
|
||||
print(f"[*] Stage 2: Building sparse feature matrix dynamically via Multiprocessing...")
|
||||
final_features = base_schema + dll_schema + api_schema
|
||||
|
||||
num_features = len(final_features)
|
||||
offset_dll = len(base_schema)
|
||||
offset_api = offset_dll + DLL_HASH_DIM
|
||||
@@ -118,6 +122,7 @@ def _chunk_worker(args):
|
||||
if f.read(1) != b'\n':
|
||||
f.seek(start_byte)
|
||||
f.readline()
|
||||
|
||||
else:
|
||||
f.seek(start_byte)
|
||||
else:
|
||||
@@ -181,9 +186,7 @@ def _calculate_dynamic_lr(current_iter: int) -> float:
|
||||
def train_process(X, y, feature_names):
|
||||
print(f"[*] Dataset shape: {X.shape}")
|
||||
|
||||
X_train, X_test, y_train, y_test = train_test_split(
|
||||
X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y
|
||||
)
|
||||
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=TEST_SIZE, random_state=RANDOM_SEED, stratify=y)
|
||||
|
||||
n_pos = (y_train == 1).sum()
|
||||
n_neg = (y_train == 0).sum()
|
||||
@@ -208,7 +211,7 @@ def train_process(X, y, feature_names):
|
||||
params,
|
||||
train_data,
|
||||
valid_sets=[valid_data],
|
||||
num_boost_round=500,
|
||||
num_boost_round=350,
|
||||
callbacks=[
|
||||
lgb.log_evaluation(period=50),
|
||||
lgb.reset_parameter(learning_rate=_calculate_dynamic_lr)
|
||||
@@ -300,6 +303,7 @@ if __name__ == "__main__":
|
||||
try:
|
||||
model, X_test, y_test = train_process(X, y, final_features)
|
||||
evaluate_and_save(model, X_test, y_test)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n[-] Training aborted.")
|
||||
except Exception as e:
|
||||
|
||||
+533
-82
@@ -68,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": "發現新版本",
|
||||
@@ -98,7 +94,6 @@ window.AppI18n = {
|
||||
"msg_click_target": "請在5秒內點擊目標視窗...",
|
||||
"msg_confirm_block": "確認攔截",
|
||||
"msg_repaired": "已修復選取項目",
|
||||
"btn_repair": "修復",
|
||||
"title_prompt": "提示",
|
||||
"title_error": "錯誤",
|
||||
"path_unknown": "未知路徑",
|
||||
@@ -141,12 +136,58 @@ window.AppI18n = {
|
||||
"col_status": "狀態",
|
||||
"status_enabled": "已啟用",
|
||||
"status_disabled": "已禁用",
|
||||
"btn_enable": "啟用",
|
||||
"btn_disable": "禁用",
|
||||
"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": "清理垃圾",
|
||||
"protect_suspend": "進程暫停",
|
||||
"protect_suspend_desc": "此選項可以暫停進程等待掃描",
|
||||
"protect_load": "加載防護",
|
||||
"protect_load_desc": "此選項可以攔截惡意進程加載",
|
||||
"opt_autostart": "開機啟動",
|
||||
"opt_autostart_desc": "此選項可以設定開機自動執行",
|
||||
"opt_ext_filter": "副檔名過濾",
|
||||
"opt_ext_filter_desc": "此選項僅掃描指定檔案副檔名",
|
||||
},
|
||||
"simplified_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -216,18 +257,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": "发现新版本",
|
||||
@@ -246,7 +283,6 @@ window.AppI18n = {
|
||||
"msg_click_target": "请在5秒内点击目标窗口...",
|
||||
"msg_confirm_block": "确认拦截",
|
||||
"msg_repaired": "已修复选取项目",
|
||||
"btn_repair": "修复",
|
||||
"title_prompt": "提示",
|
||||
"title_error": "错误",
|
||||
"path_unknown": "未知路径",
|
||||
@@ -289,12 +325,58 @@ window.AppI18n = {
|
||||
"col_status": "状态",
|
||||
"status_enabled": "已启用",
|
||||
"status_disabled": "已禁用",
|
||||
"btn_enable": "启用",
|
||||
"btn_disable": "禁用",
|
||||
"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": "清理垃圾",
|
||||
"protect_suspend": "进程暂停",
|
||||
"protect_suspend_desc": "此选项可以暂停进程等待扫描",
|
||||
"protect_load": "加载防护",
|
||||
"protect_load_desc": "此选项可以拦截恶意进程加载",
|
||||
"opt_autostart": "开机启动",
|
||||
"opt_autostart_desc": "此选项可以设定开机自动运行",
|
||||
"opt_ext_filter": "后缀名过滤",
|
||||
"opt_ext_filter_desc": "此选项仅扫描指定文件后缀名",
|
||||
},
|
||||
"english_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -364,18 +446,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",
|
||||
@@ -394,7 +472,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",
|
||||
@@ -437,12 +514,58 @@ window.AppI18n = {
|
||||
"col_status": "Status",
|
||||
"status_enabled": "Enabled",
|
||||
"status_disabled": "Disabled",
|
||||
"btn_enable": "Enable",
|
||||
"btn_disable": "Disable",
|
||||
"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",
|
||||
"protect_suspend": "Process Suspend",
|
||||
"protect_suspend_desc": "Suspend processes while waiting for scanning",
|
||||
"protect_load": "Load Protection",
|
||||
"protect_load_desc": "Intercept malicious process loading",
|
||||
"opt_autostart": "Auto Start",
|
||||
"opt_autostart_desc": "Run automatically at system startup",
|
||||
"opt_ext_filter": "Suffix Filter",
|
||||
"opt_ext_filter_desc": "This option only scans specified file extensions",
|
||||
},
|
||||
"japanese_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -512,12 +635,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": "ウィンドウを検索...",
|
||||
@@ -542,7 +661,6 @@ window.AppI18n = {
|
||||
"msg_click_target": "5秒以内にターゲットウィンドウをクリックしてください...",
|
||||
"msg_confirm_block": "ブロックの確認",
|
||||
"msg_repaired": "選択された項目が修復されました",
|
||||
"btn_repair": "修復",
|
||||
"title_prompt": "プロンプト",
|
||||
"title_error": "エラー",
|
||||
"path_unknown": "不明なパス",
|
||||
@@ -585,12 +703,58 @@ window.AppI18n = {
|
||||
"col_status": "ステータス",
|
||||
"status_enabled": "有効",
|
||||
"status_disabled": "無効",
|
||||
"btn_enable": "有効化",
|
||||
"btn_disable": "無効化",
|
||||
"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": "ジャンククリーンアップ",
|
||||
"protect_suspend": "プロセスの一時停止",
|
||||
"protect_suspend_desc": "スキャン待ちの間プロセスを一時停止します",
|
||||
"protect_load": "ロード保護",
|
||||
"protect_load_desc": "悪意のあるプロセスの読み込みをインターセプトします",
|
||||
"opt_autostart": "自動起動",
|
||||
"opt_autostart_desc": "システム起動時に自動的に実行します",
|
||||
"opt_ext_filter": "拡張子フィルタ",
|
||||
"opt_ext_filter_desc": "このオプションは指定された拡張子のファイルのみをスキャンします",
|
||||
},
|
||||
"korean_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -660,12 +824,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": "창 검색...",
|
||||
@@ -690,7 +850,6 @@ window.AppI18n = {
|
||||
"msg_click_target": "5초 이내에 대상 창을 클릭하십시오...",
|
||||
"msg_confirm_block": "차단 확인",
|
||||
"msg_repaired": "선택한 항목이 복구되었습니다",
|
||||
"btn_repair": "복구",
|
||||
"title_prompt": "프롬프트",
|
||||
"title_error": "오류",
|
||||
"path_unknown": "알 수 없는 경로",
|
||||
@@ -733,12 +892,58 @@ window.AppI18n = {
|
||||
"col_status": "상태",
|
||||
"status_enabled": "사용",
|
||||
"status_disabled": "사용 안 함",
|
||||
"btn_enable": "사용",
|
||||
"btn_disable": "사용 안 함",
|
||||
"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": "정크 정리",
|
||||
"protect_suspend": "프로세스 일시 중지",
|
||||
"protect_suspend_desc": "스캔을 기다리는 동안 프로세스를 일시 중지합니다",
|
||||
"protect_load": "로드 보호",
|
||||
"protect_load_desc": "악성 프로세스 로딩을 차단합니다",
|
||||
"opt_autostart": "자동 시작",
|
||||
"opt_autostart_desc": "시스템 시작 시 자동으로 실행합니다",
|
||||
"opt_ext_filter": "확장자 필터",
|
||||
"opt_ext_filter_desc": "이 옵션은 지정된 파일 확장자만 스캔합니다",
|
||||
},
|
||||
"french_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -808,12 +1013,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...",
|
||||
@@ -838,7 +1039,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",
|
||||
@@ -881,12 +1081,58 @@ window.AppI18n = {
|
||||
"col_status": "Statut",
|
||||
"status_enabled": "Activé",
|
||||
"status_disabled": "Désactivé",
|
||||
"btn_enable": "Activer",
|
||||
"btn_disable": "Désactiver",
|
||||
"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",
|
||||
"protect_suspend": "Suspendre processus",
|
||||
"protect_suspend_desc": "Suspend les processus en attente d'analyse",
|
||||
"protect_load": "Protection de chargement",
|
||||
"protect_load_desc": "Intercepte le chargement de processus malveillants",
|
||||
"opt_autostart": "Démarrage auto",
|
||||
"opt_autostart_desc": "Exécuter automatiquement au démarrage",
|
||||
"opt_ext_filter": "Filtre d'extension",
|
||||
"opt_ext_filter_desc": "Cette option analyse uniquement les extensions de fichiers spécifiées",
|
||||
},
|
||||
"spanish_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -894,7 +1140,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",
|
||||
@@ -956,12 +1202,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...",
|
||||
@@ -986,7 +1228,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",
|
||||
@@ -1029,12 +1270,58 @@ window.AppI18n = {
|
||||
"col_status": "Estado",
|
||||
"status_enabled": "Habilitado",
|
||||
"status_disabled": "Deshabilitado",
|
||||
"btn_enable": "Habilitar",
|
||||
"btn_disable": "Deshabilitar",
|
||||
"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",
|
||||
"protect_suspend": "Suspender procesos",
|
||||
"protect_suspend_desc": "Suspender procesos mientras espera el escaneo",
|
||||
"protect_load": "Protección de carga",
|
||||
"protect_load_desc": "Interceptar la carga de procesos maliciosos",
|
||||
"opt_autostart": "Inicio automático",
|
||||
"opt_autostart_desc": "Ejecutar automáticamente al inicio",
|
||||
"opt_ext_filter": "Filtro de extensión",
|
||||
"opt_ext_filter_desc": "Esta opción solo escanea extensiones de archivo especificadas",
|
||||
},
|
||||
"hindi_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -1104,12 +1391,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": "विंडोज़ खोजें...",
|
||||
@@ -1134,7 +1417,6 @@ window.AppI18n = {
|
||||
"msg_click_target": "5 सेकंड के भीतर लक्ष्य विंडो पर क्लिक करें...",
|
||||
"msg_confirm_block": "ब्लॉक की पुष्टि करें",
|
||||
"msg_repaired": "चयनित आइटम मरम्मत किए गए",
|
||||
"btn_repair": "मरम्मत",
|
||||
"title_prompt": "सुझाव",
|
||||
"title_error": "त्रुटि",
|
||||
"path_unknown": "अज्ञात पथ",
|
||||
@@ -1177,12 +1459,58 @@ window.AppI18n = {
|
||||
"col_status": "स्थिति",
|
||||
"status_enabled": "सक्षम",
|
||||
"status_disabled": "अक्षम",
|
||||
"btn_enable": "सक्षम करें",
|
||||
"btn_disable": "अक्षम करें",
|
||||
"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": "कचरा साफ़ करें",
|
||||
"protect_suspend": "प्रक्रिया रोकें",
|
||||
"protect_suspend_desc": "स्कैन की प्रतीक्षा करते समय प्रक्रियाओं को रोकें",
|
||||
"protect_load": "लोड सुरक्षा",
|
||||
"protect_load_desc": "दुर्भावनापूर्ण प्रक्रिया लोडिंग को रोकें",
|
||||
"opt_autostart": "स्वतः प्रारंभ",
|
||||
"opt_autostart_desc": "सिस्टम स्टार्टअप पर स्वचालित रूप से चलाएं",
|
||||
"opt_ext_filter": "एक्सटेंशन फ़िल्टर",
|
||||
"opt_ext_filter_desc": "यह विकल्प केवल निर्दिष्ट फ़ाइल एक्सटेंशन को स्कैन करता है",
|
||||
},
|
||||
"arabic_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -1252,12 +1580,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": "البحث عن النوافذ...",
|
||||
@@ -1282,7 +1606,6 @@ window.AppI18n = {
|
||||
"msg_click_target": "انقر على النافذة المستهدفة خلال 5 ثوانٍ...",
|
||||
"msg_confirm_block": "تأكيد الحظر",
|
||||
"msg_repaired": "تم إصلاح العناصر المحددة",
|
||||
"btn_repair": "إصلاح",
|
||||
"title_prompt": "تلميح",
|
||||
"title_error": "خطأ",
|
||||
"path_unknown": "مسار غير معروف",
|
||||
@@ -1325,12 +1648,58 @@ window.AppI18n = {
|
||||
"col_status": "الحالة",
|
||||
"status_enabled": "مُمكّن",
|
||||
"status_disabled": "مُعطّل",
|
||||
"btn_enable": "تمكين",
|
||||
"btn_disable": "تعطيل",
|
||||
"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": "تنظيف المخلفات",
|
||||
"protect_suspend": "تعليق العمليات",
|
||||
"protect_suspend_desc": "تعليق العمليات أثناء انتظار الفحص",
|
||||
"protect_load": "حماية التحميل",
|
||||
"protect_load_desc": "اعتراض تحميل العمليات الخبيثة",
|
||||
"opt_autostart": "بدء تلقائي",
|
||||
"opt_autostart_desc": "التشغيل تلقائيًا عند بدء التشغيل",
|
||||
"opt_ext_filter": "تصفية الامتداد",
|
||||
"opt_ext_filter_desc": "يقوم هذا الخيار بفحص امتدادات الملفات المحددة فقط",
|
||||
},
|
||||
"russian_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -1380,7 +1749,7 @@ window.AppI18n = {
|
||||
"scan_context": "Сканирование из контекстного меню",
|
||||
"scan_context_desc": "Включить функцию сканирования по правому клику",
|
||||
"theme_color": "Цветовая тема",
|
||||
"theme_color_desc": "Выберите цветовую тему приложения",
|
||||
"theme_color_desc": "Выберите цветовой тему приложения",
|
||||
"theme_system": "Системная тема",
|
||||
"theme_white": "Светлый режим",
|
||||
"theme_red": "Красная тема",
|
||||
@@ -1400,12 +1769,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": "Поиск окон...",
|
||||
@@ -1430,7 +1795,6 @@ window.AppI18n = {
|
||||
"msg_click_target": "Нажмите на целевое окно в течение 5 сек...",
|
||||
"msg_confirm_block": "Подтвердить блокировку",
|
||||
"msg_repaired": "Выбранные элементы восстановлены",
|
||||
"btn_repair": "Восстановить",
|
||||
"title_prompt": "Подсказка",
|
||||
"title_error": "Ошибка",
|
||||
"path_unknown": "Неизвестный путь",
|
||||
@@ -1473,12 +1837,58 @@ window.AppI18n = {
|
||||
"col_status": "Статус",
|
||||
"status_enabled": "Включено",
|
||||
"status_disabled": "Отключено",
|
||||
"btn_enable": "Включить",
|
||||
"btn_disable": "Отключить",
|
||||
"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": "Очистка мусора",
|
||||
"protect_suspend": "Приостановка процессов",
|
||||
"protect_suspend_desc": "Приостановка процессов в ожидании сканирования",
|
||||
"protect_load": "Защита загрузки",
|
||||
"protect_load_desc": "Перехват загрузки вредоносных процессов",
|
||||
"opt_autostart": "Автозапуск",
|
||||
"opt_autostart_desc": "Автоматический запуск при старте системы",
|
||||
"opt_ext_filter": "Фильтр расширений",
|
||||
"opt_ext_filter_desc": "Этот параметр сканирует только указанные расширения файлов",
|
||||
},
|
||||
"slovenian_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -1548,12 +1958,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...",
|
||||
@@ -1578,7 +1984,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",
|
||||
@@ -1621,12 +2026,58 @@ window.AppI18n = {
|
||||
"col_status": "Stanje",
|
||||
"status_enabled": "Omogočeno",
|
||||
"status_disabled": "Onemogočeno",
|
||||
"btn_enable": "Omogoči",
|
||||
"btn_disable": "Onemogoči",
|
||||
"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",
|
||||
"protect_suspend": "Začasna ustavitev",
|
||||
"protect_suspend_desc": "Zaustavite procese med čakanjem na skeniranje",
|
||||
"protect_load": "Zaščita nalaganja",
|
||||
"protect_load_desc": "Prestrezi nalaganje zlonamernih procesov",
|
||||
"opt_autostart": "Samodejni zagon",
|
||||
"opt_autostart_desc": "Samodejni zagon ob zagonu sistema",
|
||||
"opt_ext_filter": "Filter končnic",
|
||||
"opt_ext_filter_desc": "Ta možnost skenira samo določene končnice datotek",
|
||||
}
|
||||
},
|
||||
langMap: {
|
||||
|
||||
+296
-143
@@ -14,6 +14,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
firstLaunch: false,
|
||||
taskmgrTimer: null,
|
||||
taskmgrActive: false,
|
||||
netmonTimer: null,
|
||||
netmonActive: false,
|
||||
virusMap: new Map(),
|
||||
virusResults: [],
|
||||
junkList: []
|
||||
@@ -78,14 +80,16 @@ 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}],
|
||||
fileList: [{key: 'time_str', label: 'col_date', flex: 1.5}, {key: 'path', label: 'col_path', flex: 5}],
|
||||
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}, {key: 'path', label: 'col_path', flex: 3}]
|
||||
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) => {
|
||||
@@ -338,11 +342,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));
|
||||
@@ -429,23 +447,23 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
};
|
||||
|
||||
const checkVirusListEmpty = () => {
|
||||
updateCustomSelectUI('scan_method_select', 'none');
|
||||
if (appState.virusResults.length === 0) {
|
||||
appState.virusMap.clear();
|
||||
changeScanSelectMode('scan');
|
||||
const title = document.querySelector('#scan_window .section-title');
|
||||
const text = document.getElementById('progress_text');
|
||||
if (title) {
|
||||
title.setAttribute('data-i18n', 'scan_virus');
|
||||
title.textContent = getMsg('scan_virus');
|
||||
updateCustomSelectUI('scan_method_select', 'none');
|
||||
if (appState.virusResults.length === 0) {
|
||||
appState.virusMap.clear();
|
||||
changeScanSelectMode('scan');
|
||||
const title = document.querySelector('#scan_window .section-title');
|
||||
const text = document.getElementById('progress_text');
|
||||
if (title) {
|
||||
title.setAttribute('data-i18n', 'scan_virus');
|
||||
title.textContent = getMsg('scan_virus');
|
||||
}
|
||||
if (text) {
|
||||
text.setAttribute('data-i18n', 'scan_virus_desc');
|
||||
text.removeAttribute('data-dynamic-msg');
|
||||
text.textContent = getMsg('scan_virus_desc');
|
||||
}
|
||||
}
|
||||
if (text) {
|
||||
text.setAttribute('data-i18n', 'scan_virus_desc');
|
||||
text.removeAttribute('data-dynamic-msg');
|
||||
text.textContent = getMsg('scan_virus_desc');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const triggerScan = (method) => {
|
||||
if (!window.pywebview) return;
|
||||
@@ -501,16 +519,41 @@ 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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -526,40 +569,45 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
};
|
||||
|
||||
document.querySelector('#startup_window .primary-btn')?.addEventListener('click', () => {
|
||||
const checked = getCheckedValues('#startup_window');
|
||||
if (checked.length > 0 && window.pywebview) {
|
||||
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 = 'status_enabled';
|
||||
});
|
||||
widget.renderData();
|
||||
}
|
||||
window.pywebview.api.manage_startup(checked, 'enable').then(fetchStartup);
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
||||
document.querySelector('#startup_window .danger-btn')?.addEventListener('click', () => {
|
||||
const checked = getCheckedValues('#startup_window');
|
||||
if (checked.length > 0 && window.pywebview) {
|
||||
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 = 'status_disabled';
|
||||
});
|
||||
widget.renderData();
|
||||
}
|
||||
window.pywebview.api.manage_startup(checked, 'disable').then(fetchStartup);
|
||||
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;
|
||||
window.pywebview.api.get_config().then(cfg => {
|
||||
renderDataGrid('#whitelist_window', (cfg.white_list || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
|
||||
renderDataGrid('#quarantine_window', (cfg.quarantine || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
|
||||
renderDataGrid('#custom_protect_window', (cfg.custom_rule || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
|
||||
const formatTime = (ts) => {
|
||||
if (!ts) return '-';
|
||||
const d = new Date(ts * 1000);
|
||||
const pad = n => n.toString().padStart(2, '0');
|
||||
return `[${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}]`;
|
||||
};
|
||||
const mapList = (arr) => (arr || []).map(i => ({
|
||||
path: i.file || (typeof i === 'string' ? i : ''),
|
||||
time_str: formatTime(i.time)
|
||||
}));
|
||||
renderDataGrid('#whitelist_window', mapList(cfg.white_list), cols.fileList, 'path');
|
||||
renderDataGrid('#quarantine_window', mapList(cfg.quarantine), cols.fileList, 'path');
|
||||
renderDataGrid('#custom_protect_window', mapList(cfg.custom_rule), cols.fileList, 'path');
|
||||
const popupRules = (cfg.block_list || []).map(item => ({ exe: item.exe || '*', class: item.class || '*', title: item.title || '*', value: item.exe || item.title }));
|
||||
renderDataGrid('#popup_window', popupRules, cols.popup, 'value');
|
||||
});
|
||||
@@ -586,7 +634,13 @@ 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();
|
||||
|
||||
@@ -649,13 +703,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';
|
||||
@@ -723,7 +781,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
|
||||
toggle.addEventListener('change', async (e) => {
|
||||
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
|
||||
const switchMap = ["process_switch", "suspend_switch", "load_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "sensitive_switch", "extension_switch", "cloud_switch", "suffix_switch", "autostart_switch", "context_switch"];
|
||||
const key = switchMap[index];
|
||||
if (window.pywebview && key) {
|
||||
toggle.disabled = true;
|
||||
@@ -736,41 +794,97 @@ 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, 'id', 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, 'id', true);
|
||||
});
|
||||
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 => {
|
||||
@@ -781,65 +895,91 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
'quarantine_window': 'quarantine'
|
||||
};
|
||||
const listKey = listKeyMap[id];
|
||||
const selectId = id.replace('_window', '_select');
|
||||
|
||||
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 => {
|
||||
if (files?.length > 0) window.pywebview.api.manage_named_list(listKey, files, 'add').then(refreshConfigLists);
|
||||
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 {
|
||||
const fTypes = id === 'custom_protect_window' ? ['JSON Files (*.json)'] : null;
|
||||
const currentListKey = id === 'custom_protect_window' ? 'custom_rule' : listKey;
|
||||
window.pywebview.api.select_files(fTypes).then(files => {
|
||||
if (files?.length > 0) window.pywebview.api.manage_named_list(currentListKey, files, 'add').then(refreshConfigLists);
|
||||
});
|
||||
}
|
||||
} else if (val === 'add_file') {
|
||||
const fTypes = id === 'custom_protect_window' ? ['JSON Files (*.json)'] : null;
|
||||
const currentListKey = id === 'custom_protect_window' ? 'custom_rule' : listKey;
|
||||
window.pywebview.api.select_files(fTypes).then(files => {
|
||||
if (files?.length > 0) window.pywebview.api.manage_named_list(currentListKey, 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());
|
||||
|
||||
@@ -897,7 +1037,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");
|
||||
@@ -905,6 +1056,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"));
|
||||
@@ -917,11 +1069,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
renderDataGrid('#startup_window', [], cols.startup, 'id', false);
|
||||
renderDataGrid('#junk_window', [], cols.junk, 'path', 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('#whitelist_window', [], cols.fileList, 'path', false);
|
||||
renderDataGrid('#quarantine_window', [], cols.fileList, 'path', false);
|
||||
renderDataGrid('#custom_protect_window', [], cols.fileList, '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 => {
|
||||
@@ -933,7 +1086,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
updateCustomSelectUI('theme_select', theme);
|
||||
updateCustomSelectUI('lang_select', lang);
|
||||
|
||||
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
|
||||
const switchMap = ["process_switch", "suspend_switch", "load_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "sensitive_switch", "extension_switch", "cloud_switch", "suffix_switch", "autostart_switch", "context_switch"];
|
||||
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
|
||||
if (appState.firstLaunch) {
|
||||
toggle.checked = false;
|
||||
@@ -953,8 +1106,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
window.onEngineReady = async () => {
|
||||
if (!appState.firstLaunch) return;
|
||||
await window.pywebview.api.update_config("first_launch", false);
|
||||
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
|
||||
const seq = ["cloud_switch", "process_switch", "document_switch", "system_switch", "network_switch", "driver_switch"];
|
||||
const switchMap = ["process_switch", "suspend_switch", "load_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "sensitive_switch", "extension_switch", "cloud_switch", "suffix_switch", "autostart_switch", "context_switch"];
|
||||
const seq = ["cloud_switch", "autostart_switch", "suffix_switch", "process_switch", "suspend_switch", "load_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "context_switch"];
|
||||
|
||||
for (const key of seq) {
|
||||
const index = switchMap.indexOf(key);
|
||||
@@ -967,7 +1120,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+176
-43
@@ -109,6 +109,18 @@
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>進程暫停</h2><p data-i18n>此選項可以暫停進程等待掃描</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>加載防護</h2><p data-i18n>此選項可以攔截惡意進程加載</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>檔案防護</h2><p data-i18n>此選項可以隔離惡意檔案建立</p>
|
||||
@@ -123,13 +135,13 @@
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>驅動防護</h2><p data-i18n>此選項可以實時攔截惡意行為</p>
|
||||
<h2 data-i18n>網路防護</h2><p data-i18n>此選項可以攔截惡意網路連接</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>網路防護</h2><p data-i18n>此選項可以攔截惡意網路連接</p>
|
||||
<h2 data-i18n>驅動防護</h2><p data-i18n>此選項可以實時攔截惡意行為</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
@@ -174,6 +186,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>
|
||||
@@ -209,6 +227,18 @@
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>副檔名過濾</h2><p data-i18n>此選項僅掃描指定檔案副檔名</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>開機啟動</h2><p data-i18n>此選項可以設定開機自動執行</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>選單掃描</h2><p data-i18n>此選項可以啟用右鍵掃描功能</p>
|
||||
@@ -278,34 +308,64 @@
|
||||
</div>
|
||||
<div class="license-widget">
|
||||
<textarea class="license-text" readonly>87owo Publicly Available Source Code License Agreement
|
||||
Last updated: 2025-12-19
|
||||
|
||||
1. License and Ownership
|
||||
- This software and its source code are source available for public view.
|
||||
- Access or execution of certain features may require an activation key.
|
||||
- 87owo reserves all legal rights, no intellectual property is transferred.
|
||||
- All project archives, source code, and binaries remain fully proprietary.
|
||||
Last Updated: 2026-06-09
|
||||
IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE LICENSE APPROVED BY THE OPEN SOURCE INITIATIVE (OSI). BY DOWNLOADING, ACCESSING, OR USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THESE TERMS.
|
||||
|
||||
2. Use and Prohibitions
|
||||
- You may view, download, and modify the code for local and personal use.
|
||||
- Redistribution of any original or modified code requires prior consent.
|
||||
- Commercial hosting or SaaS deployment is prohibited without permission.
|
||||
- Reverse engineering, license removal, or illegal use is strictly barred.
|
||||
1. Definitions and Interpretation
|
||||
|
||||
3. Data and Privacy
|
||||
- This project may collect operational data for optimization and analysis.
|
||||
- All collected data is strictly anonymized to ensure the user's privacy.
|
||||
- Transmission of information is encrypted to prevent unauthorized access.
|
||||
- 1.1. Author: Refers to 87owo (PYAS Security).
|
||||
- 1.2. Software: Refers to the source code, binaries, project archives, assets, and documentation associated with this repository or distribution.
|
||||
- 1.3. Personal Local Use: Means viewing, downloading, executing, and modifying the Software strictly on local hardware owned or directly controlled by an individual for private, non-commercial, non-production, and non-distribution purposes.
|
||||
- 1.4. Enterprise Use: Includes enterprise internal deployment, multi-user institutional use, or utilization within any corporate environment, which is strictly excluded from Personal Local Use.
|
||||
|
||||
4. Updates and Terms
|
||||
- New releases may include critical patches, enhancements, or UI changes.
|
||||
- Continued use of these services constitutes acceptance of updated terms.
|
||||
- The author reserves the right to modify these license terms at any time.
|
||||
2. Intellectual Property and Ownership
|
||||
|
||||
5. Warranty and Liability
|
||||
- All services and assets are provided "as is" without any form of warranty.
|
||||
- 87owo is not liable for any direct or indirect damages from software use.
|
||||
- Users assume all risks regarding performance, data, and system integrity.
|
||||
- 2.1. Retention of Rights: The Author reserves all legal rights, titles, and interests in and to the Software, including but not limited to all copyrights, trademarks, patents, and trade secrets.
|
||||
- 2.2. No Transfer: No intellectual property rights or ownership stakes are transferred under this Agreement. All project components remain fully proprietary.
|
||||
- 2.3. Activation Keys: Access to or execution of certain premium features, modules, or binary distributions within the Software may require a valid, authorized activation key issued solely by the Author.
|
||||
|
||||
3. Grant of License and Permitted Use
|
||||
|
||||
Subject to your strict compliance with all terms of this Agreement, the Author hereby grants you a limited, non-exclusive, world-wide, royalty-free, revocable, and non-transferable license to:
|
||||
- 3.1. Code Inspection: View and download the source code from official authorized repositories.
|
||||
- 3.2. Local Modification: Modify the source code strictly for the purposes of Personal Local Use.
|
||||
- 3.3. Local Execution: Execute the Software locally for personal evaluation and private deployment.
|
||||
|
||||
4. Prohibitions and Restrictions
|
||||
|
||||
Except as explicitly granted in Section 3, you shall not, and shall not permit any third party to:
|
||||
- 4.1. Unauthorized Redistribution: Copy, redistribute, sublicense, republish, mirror, or publicly disclose any original or modified versions of the source code, binaries, or assets.
|
||||
- 4.2. Commercial and SaaS Deployment: Host, deploy, embed, or utilize the Software for commercial purposes, including but not limited to Software-as-a-Service (SaaS), commercial web hosting, paid remote execution, cloud infrastructure integration, or enterprise internal utility.
|
||||
- 4.3. Reverse Engineering: Reverse engineer, decompile, decrypt, or disassemble any binary components of the Software, except and only to the extent that such restriction is explicitly prohibited by applicable mandatory laws.
|
||||
- 4.4. Notice Preservation: Remove, obscure, deface, or alter any copyright notices, trademarks, hyperlinked terms, or license files included within the Software distribution.
|
||||
|
||||
5. Prior Authorization and Application Channels
|
||||
|
||||
- 5.1. Authorization Requirement: Any activity exceeding the scope of Personal Local Use (including but not limited to public redistribution, independent forks not targeting upstream contributions, commercial deployment, or enterprise use) requires prior explicit written consent from the Author.
|
||||
- 5.2. Official Application Channels: Users seeking such authorization must submit a formal request through one of the following official channels:
|
||||
- Channel A (GitHub Fork): Initiating a standard Fork on the official GitHub repository for collaborative and upstream contributions.
|
||||
- Channel B (GitHub Issues): Submitting a public issue utilizing the designated authorization request template.
|
||||
- Channel C (Official Email): Sending a detailed inquiry to the official organizational email address associated with the PYAS Security project.
|
||||
- 5.3. Request Content: Any application must explicitly state the specific use case, intended target audience, scope of deployment, and whether any commercial monetization is involved.
|
||||
|
||||
6. Updates, Amendments, and Scope of Terms
|
||||
|
||||
- 6.1. New Releases: The Author may deliver updates, critical security patches, feature enhancements, or user interface modifications. Continued use of the Software constitutes acceptance of the latest distributed version.
|
||||
- 6.2. Non-Retroactivity: The Author reserves the right to amend this License Agreement at any time. Amendments apply strictly to subsequent versions or new downloads. Copies lawfully acquired prior to such modification remain governed by the exact version of the License under which they were originally obtained.
|
||||
|
||||
7. Telemetry, Data, and Privacy
|
||||
|
||||
- 7.1. Operational Data Collection: The Software may collect anonymized technical, operational, and usage data strictly for security analysis, performance optimization, and bug reporting.
|
||||
- 7.2. Cloud Sample Submission: To enhance threat detection and malware analysis, the Software may automatically or manually transmit suspicious executable files and software samples to the Author's cloud infrastructure for deep analysis.
|
||||
- 7.3. Scope of Collected Extensions: Sample collection targets potential executable and system binary formats, which explicitly include, but are not limited to, files with the following extensions: `.exe`, `.dll`, `.sys`, `.ocx`, `.scr`, `.efi`, `.acm`, `.ax`, `.cpl`, `.drv`, `.com`, `.mui`, `.pyd`, `.wfx`, `.api`, `.awx`, `.rll`, and `.winmd`.
|
||||
- 7.4. Public Disclosure of Threat Intelligence: You explicitly acknowledge and agree that the metadata, behavioral reports, and threat intelligence data generated from the cloud analysis of submitted samples may be publicly disclosed, published, or shared with the cybersecurity community for collaborative defense purposes.
|
||||
- 7.5. Opt-Out Mechanism: Cloud-based telemetry and sample submission are fully optional. Users who do not wish to participate in sample collection or cloud-assisted analysis may disable the corresponding cloud services at any time via the toggle switches provided within the Software’s settings interface.
|
||||
|
||||
8. Warranty Disclaimer and Limitation of Liability
|
||||
|
||||
- 8.1. Disclaimer of Warranty: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE AND ALL RELATED ASSETS ARE PROVIDED ON AN "AS IS" AND "AS AVAILABLE" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
- 8.2. Limitation of Liability: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL THE AUTHOR, COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE. USERS ASSUME ALL RISKS REGARDING PERFORMANCE, DATA INTEGRITY, AND SYSTEM STABILITY.
|
||||
|
||||
© 2020-2026 87owo (PYAS Security). All rights reserved.</textarea>
|
||||
</div>
|
||||
@@ -321,8 +381,15 @@ 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>
|
||||
@@ -336,8 +403,14 @@ 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="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>
|
||||
@@ -351,8 +424,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>
|
||||
@@ -366,8 +447,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>
|
||||
@@ -381,8 +470,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>
|
||||
@@ -392,8 +486,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>
|
||||
@@ -407,8 +505,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>
|
||||
@@ -422,8 +545,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>
|
||||
@@ -437,8 +565,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>
|
||||
|
||||
+53
-23
@@ -1,31 +1,61 @@
|
||||
87owo Publicly Available Source Code License Agreement
|
||||
Last updated: 2025-12-19
|
||||
|
||||
1. License and Ownership
|
||||
- This software and its source code are source available for public view.
|
||||
- Access or execution of certain features may require an activation key.
|
||||
- 87owo reserves all legal rights, no intellectual property is transferred.
|
||||
- All project archives, source code, and binaries remain fully proprietary.
|
||||
Last Updated: 2026-06-09
|
||||
IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE LICENSE APPROVED BY THE OPEN SOURCE INITIATIVE (OSI). BY DOWNLOADING, ACCESSING, OR USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THESE TERMS.
|
||||
|
||||
2. Use and Prohibitions
|
||||
- You may view, download, and modify the code for local and personal use.
|
||||
- Redistribution of any original or modified code requires prior consent.
|
||||
- Commercial hosting or SaaS deployment is prohibited without permission.
|
||||
- Reverse engineering, license removal, or illegal use is strictly barred.
|
||||
1. Definitions and Interpretation
|
||||
|
||||
3. Data and Privacy
|
||||
- This project may collect operational data for optimization and analysis.
|
||||
- All collected data is strictly anonymized to ensure the user's privacy.
|
||||
- Transmission of information is encrypted to prevent unauthorized access.
|
||||
- 1.1. Author: Refers to 87owo (PYAS Security).
|
||||
- 1.2. Software: Refers to the source code, binaries, project archives, assets, and documentation associated with this repository or distribution.
|
||||
- 1.3. Personal Local Use: Means viewing, downloading, executing, and modifying the Software strictly on local hardware owned or directly controlled by an individual for private, non-commercial, non-production, and non-distribution purposes.
|
||||
- 1.4. Enterprise Use: Includes enterprise internal deployment, multi-user institutional use, or utilization within any corporate environment, which is strictly excluded from Personal Local Use.
|
||||
|
||||
4. Updates and Terms
|
||||
- New releases may include critical patches, enhancements, or UI changes.
|
||||
- Continued use of these services constitutes acceptance of updated terms.
|
||||
- The author reserves the right to modify these license terms at any time.
|
||||
2. Intellectual Property and Ownership
|
||||
|
||||
5. Warranty and Liability
|
||||
- All services and assets are provided "as is" without any form of warranty.
|
||||
- 87owo is not liable for any direct or indirect damages from software use.
|
||||
- Users assume all risks regarding performance, data, and system integrity.
|
||||
- 2.1. Retention of Rights: The Author reserves all legal rights, titles, and interests in and to the Software, including but not limited to all copyrights, trademarks, patents, and trade secrets.
|
||||
- 2.2. No Transfer: No intellectual property rights or ownership stakes are transferred under this Agreement. All project components remain fully proprietary.
|
||||
- 2.3. Activation Keys: Access to or execution of certain premium features, modules, or binary distributions within the Software may require a valid, authorized activation key issued solely by the Author.
|
||||
|
||||
3. Grant of License and Permitted Use
|
||||
|
||||
Subject to your strict compliance with all terms of this Agreement, the Author hereby grants you a limited, non-exclusive, world-wide, royalty-free, revocable, and non-transferable license to:
|
||||
- 3.1. Code Inspection: View and download the source code from official authorized repositories.
|
||||
- 3.2. Local Modification: Modify the source code strictly for the purposes of Personal Local Use.
|
||||
- 3.3. Local Execution: Execute the Software locally for personal evaluation and private deployment.
|
||||
|
||||
4. Prohibitions and Restrictions
|
||||
|
||||
Except as explicitly granted in Section 3, you shall not, and shall not permit any third party to:
|
||||
- 4.1. Unauthorized Redistribution: Copy, redistribute, sublicense, republish, mirror, or publicly disclose any original or modified versions of the source code, binaries, or assets.
|
||||
- 4.2. Commercial and SaaS Deployment: Host, deploy, embed, or utilize the Software for commercial purposes, including but not limited to Software-as-a-Service (SaaS), commercial web hosting, paid remote execution, cloud infrastructure integration, or enterprise internal utility.
|
||||
- 4.3. Reverse Engineering: Reverse engineer, decompile, decrypt, or disassemble any binary components of the Software, except and only to the extent that such restriction is explicitly prohibited by applicable mandatory laws.
|
||||
- 4.4. Notice Preservation: Remove, obscure, deface, or alter any copyright notices, trademarks, hyperlinked terms, or license files included within the Software distribution.
|
||||
|
||||
5. Prior Authorization and Application Channels
|
||||
|
||||
- 5.1. Authorization Requirement: Any activity exceeding the scope of Personal Local Use (including but not limited to public redistribution, independent forks not targeting upstream contributions, commercial deployment, or enterprise use) requires prior explicit written consent from the Author.
|
||||
- 5.2. Official Application Channels: Users seeking such authorization must submit a formal request through one of the following official channels:
|
||||
- Channel A (GitHub Fork): Initiating a standard Fork on the official GitHub repository for collaborative and upstream contributions.
|
||||
- Channel B (GitHub Issues): Submitting a public issue utilizing the designated authorization request template.
|
||||
- Channel C (Official Email): Sending a detailed inquiry to the official organizational email address associated with the PYAS Security project.
|
||||
- 5.3. Request Content: Any application must explicitly state the specific use case, intended target audience, scope of deployment, and whether any commercial monetization is involved.
|
||||
|
||||
6. Updates, Amendments, and Scope of Terms
|
||||
|
||||
- 6.1. New Releases: The Author may deliver updates, critical security patches, feature enhancements, or user interface modifications. Continued use of the Software constitutes acceptance of the latest distributed version.
|
||||
- 6.2. Non-Retroactivity: The Author reserves the right to amend this License Agreement at any time. Amendments apply strictly to subsequent versions or new downloads. Copies lawfully acquired prior to such modification remain governed by the exact version of the License under which they were originally obtained.
|
||||
|
||||
7. Telemetry, Data, and Privacy
|
||||
|
||||
- 7.1. Operational Data Collection: The Software may collect anonymized technical, operational, and usage data strictly for security analysis, performance optimization, and bug reporting.
|
||||
- 7.2. Cloud Sample Submission: To enhance threat detection and malware analysis, the Software may automatically or manually transmit suspicious executable files and software samples to the Author's cloud infrastructure for deep analysis.
|
||||
- 7.3. Scope of Collected Extensions: Sample collection targets potential executable and system binary formats, which explicitly include, but are not limited to, files with the following extensions: `.exe`, `.dll`, `.sys`, `.ocx`, `.scr`, `.efi`, `.acm`, `.ax`, `.cpl`, `.drv`, `.com`, `.mui`, `.pyd`, `.wfx`, `.api`, `.awx`, `.rll`, and `.winmd`.
|
||||
- 7.4. Public Disclosure of Threat Intelligence: You explicitly acknowledge and agree that the metadata, behavioral reports, and threat intelligence data generated from the cloud analysis of submitted samples may be publicly disclosed, published, or shared with the cybersecurity community for collaborative defense purposes.
|
||||
- 7.5. Opt-Out Mechanism: Cloud-based telemetry and sample submission are fully optional. Users who do not wish to participate in sample collection or cloud-assisted analysis may disable the corresponding cloud services at any time via the toggle switches provided within the Software’s settings interface.
|
||||
|
||||
8. Warranty Disclaimer and Limitation of Liability
|
||||
|
||||
- 8.1. Disclaimer of Warranty: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE AND ALL RELATED ASSETS ARE PROVIDED ON AN "AS IS" AND "AS AVAILABLE" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
- 8.2. Limitation of Liability: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL THE AUTHOR, COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE. USERS ASSUME ALL RISKS REGARDING PERFORMANCE, DATA INTEGRITY, AND SYSTEM STABILITY.
|
||||
|
||||
© 2020-2026 87owo (PYAS Security). All rights reserved.
|
||||
+144
-72
@@ -1,5 +1,6 @@
|
||||
import os, re, yara, time, math, json, numpy, base64, datetime, requests, zlib
|
||||
import ctypes, ctypes.wintypes, hashlib, pefile, threading, onnxruntime
|
||||
import os, re, yara, time, math, json, zlib, mmap, numpy
|
||||
import pefile, threading, onnxruntime, datetime, requests
|
||||
import ctypes, ctypes.wintypes
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -57,6 +58,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 +77,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 +95,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 +111,19 @@ class rule_scanner:
|
||||
yara_files = {}
|
||||
for root, _, files in os.walk(path):
|
||||
for file in files:
|
||||
|
||||
full_path = os.path.join(root, file)
|
||||
if callback:
|
||||
callback(full_path)
|
||||
|
||||
ext = os.path.splitext(file)[1].lower()
|
||||
if ext in ('.yara', '.yar'):
|
||||
namespace = os.path.relpath(full_path, path).replace(os.sep, '_')
|
||||
yara_files[namespace] = full_path
|
||||
|
||||
elif ext in ('.yc', '.yrc'):
|
||||
self.load_compiled_rule(full_path)
|
||||
|
||||
elif ext in ('.ip', '.txt'):
|
||||
self.load_network_list(full_path)
|
||||
|
||||
@@ -155,6 +164,7 @@ class rule_scanner:
|
||||
label = rule_name.split("_")[0]
|
||||
level = rule_name.split("_")[-1]
|
||||
return f"{label}:WinPE/Unknown.{level}!yr", level
|
||||
|
||||
return False, False
|
||||
except Exception:
|
||||
return False, False
|
||||
@@ -170,6 +180,7 @@ class rule_scanner:
|
||||
label = rule_name.split("_")[0]
|
||||
level = rule_name.split("_")[-1]
|
||||
return f"{label}:WinPE/Unknown.{level}!ym", level
|
||||
|
||||
return False, False
|
||||
except Exception:
|
||||
return False, False
|
||||
@@ -183,6 +194,7 @@ class pe_scanner:
|
||||
_BYTE_ENT_KEYS = [f'ByteEnt_{i:02X}' for i in range(256)]
|
||||
_STRING_KEYS = [f'StringHist_{i:02d}' for i in range(95)]
|
||||
_SECTION_HASH_KEYS = [f'SectionHash_{i:02d}' for i in range(50)]
|
||||
|
||||
_CHAR_FLAGS = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
|
||||
_CHAR_COUNT_KEYS = {flag: f'Char_{flag:08X}_Count' for flag in _CHAR_FLAGS}
|
||||
_CHAR_ENT_KEYS = {flag: f'Char_{flag:08X}_MeanEntropy' for flag in _CHAR_FLAGS}
|
||||
@@ -194,6 +206,10 @@ class pe_scanner:
|
||||
self.model = None
|
||||
self.input_name = None
|
||||
self.feature_order = []
|
||||
self.dll_hash_dim = 512
|
||||
self.api_hash_dim = 4096
|
||||
self.dll_hash_pad = 3
|
||||
self.api_hash_pad = 4
|
||||
self.signer = sign_scanner()
|
||||
self.signer.init_windll(["wintrust"])
|
||||
|
||||
@@ -201,29 +217,61 @@ 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)
|
||||
|
||||
self._parse_hash_dims()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _parse_hash_dims(self):
|
||||
max_dll = -1
|
||||
max_api = -1
|
||||
|
||||
for feat in self.feature_order:
|
||||
if feat.startswith("DllHash_"):
|
||||
try:
|
||||
val_str = feat.split("_")[1]
|
||||
max_dll = max(max_dll, int(val_str))
|
||||
self.dll_hash_pad = len(val_str)
|
||||
except Exception:
|
||||
pass
|
||||
elif feat.startswith("ApiHash_"):
|
||||
try:
|
||||
val_str = feat.split("_")[1]
|
||||
max_api = max(max_api, int(val_str))
|
||||
self.api_hash_pad = len(val_str)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if max_dll >= 0:
|
||||
self.dll_hash_dim = max_dll + 1
|
||||
if max_api >= 0:
|
||||
self.api_hash_dim = max_api + 1
|
||||
|
||||
def _safe_float(self, val):
|
||||
try:
|
||||
f = float(val)
|
||||
if math.isinf(f) or math.isnan(f):
|
||||
return 0.0
|
||||
|
||||
return f
|
||||
except Exception:
|
||||
return 0.0
|
||||
@@ -231,33 +279,43 @@ class pe_scanner:
|
||||
def _calc_entropy(self, data):
|
||||
if not data:
|
||||
return 0.0
|
||||
|
||||
arr = numpy.frombuffer(data, dtype=numpy.uint8)
|
||||
sz = len(arr)
|
||||
counts = numpy.bincount(arr, minlength=256)
|
||||
counts = counts[counts > 0]
|
||||
|
||||
return float(numpy.log2(sz) - numpy.sum(counts * numpy.log2(counts)) / sz)
|
||||
|
||||
def _extract_strings(self, file_bytes):
|
||||
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
|
||||
total_len = 0
|
||||
count = 0
|
||||
global_counts = numpy.zeros(256, dtype=numpy.int64)
|
||||
mv = memoryview(file_bytes)
|
||||
|
||||
combined_data = b''.join(matches)
|
||||
count = len(matches)
|
||||
total_len = len(combined_data)
|
||||
for m in self._STRING_PATTERN.finditer(file_bytes):
|
||||
count += 1
|
||||
start, end = m.span()
|
||||
total_len += (end - start)
|
||||
arr = numpy.frombuffer(mv[start:end], dtype=numpy.uint8)
|
||||
global_counts += numpy.bincount(arr, minlength=256)
|
||||
|
||||
if count == 0 or total_len == 0:
|
||||
return res
|
||||
|
||||
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]
|
||||
counts_nonzero = global_counts[global_counts > 0]
|
||||
if len(counts_nonzero) > 0:
|
||||
res["StringEntropy"] = float(numpy.log2(total_len) - numpy.sum(counts_nonzero * numpy.log2(counts_nonzero)) / total_len)
|
||||
|
||||
valid_chars = global_counts[0x20:0x7F]
|
||||
res.update(dict(zip(self._STRING_KEYS, valid_chars.astype(numpy.float64) / total_len)))
|
||||
|
||||
res.update(dict(zip(self._STRING_KEYS, char_counts.astype(numpy.float64) / total_len)))
|
||||
return res
|
||||
|
||||
def _extract_histograms(self, file_bytes):
|
||||
@@ -286,6 +344,7 @@ class pe_scanner:
|
||||
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
|
||||
@@ -342,6 +401,7 @@ class pe_scanner:
|
||||
def _extract_rich_header(self, pe):
|
||||
if not hasattr(pe, 'RICH_HEADER') or not pe.RICH_HEADER:
|
||||
return {"HasRichHeader": 0.0, "RichHeaderCount": 0.0}
|
||||
|
||||
return {
|
||||
"HasRichHeader": 1.0,
|
||||
"RichHeaderCount": float(len(pe.RICH_HEADER.values) // 2) if hasattr(pe.RICH_HEADER, 'values') else 0.0
|
||||
@@ -398,6 +458,7 @@ class pe_scanner:
|
||||
for entry_lang in entry_id.directory.entries:
|
||||
if hasattr(entry_lang, 'id'):
|
||||
langs.add(entry_lang.id)
|
||||
|
||||
if hasattr(entry_lang, 'data'):
|
||||
try:
|
||||
data = pe.get_data(entry_lang.data.struct.OffsetToData, entry_lang.data.struct.Size)
|
||||
@@ -417,13 +478,17 @@ 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):
|
||||
@@ -463,18 +528,21 @@ class pe_scanner:
|
||||
|
||||
def extract_features(self, file_path):
|
||||
pe = None
|
||||
mm = None
|
||||
f = None
|
||||
|
||||
try:
|
||||
fsize = os.path.getsize(file_path)
|
||||
if fsize == 0 or fsize > 536870912:
|
||||
return None
|
||||
|
||||
with open(file_path, "rb") as f:
|
||||
file_bytes = f.read()
|
||||
f = open(file_path, "rb")
|
||||
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
|
||||
|
||||
base = {}
|
||||
dlls = set()
|
||||
apis = set()
|
||||
pe = pefile.PE(data=file_bytes, fast_load=True)
|
||||
pe = pefile.PE(data=mm, fast_load=True)
|
||||
|
||||
try:
|
||||
pe.parse_rich_header()
|
||||
@@ -490,7 +558,7 @@ class pe_scanner:
|
||||
pass
|
||||
|
||||
base['TrustSigned'] = 1.0 if self.signer.sign_verify(file_path) else 0.0
|
||||
base['FileEntropy'] = self._calc_entropy(file_bytes)
|
||||
base['FileEntropy'] = self._calc_entropy(mm)
|
||||
base['FileSize'] = float(fsize)
|
||||
|
||||
fh = pe.FILE_HEADER
|
||||
@@ -508,7 +576,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'):
|
||||
@@ -556,6 +623,7 @@ class pe_scanner:
|
||||
try:
|
||||
s_data = section.get_data()
|
||||
s_entropy = self._calc_entropy(s_data)
|
||||
|
||||
except Exception:
|
||||
s_entropy = 0.0
|
||||
|
||||
@@ -563,10 +631,14 @@ class pe_scanner:
|
||||
raw_sizes.append(section.SizeOfRawData)
|
||||
v_sizes.append(section.Misc_VirtualSize)
|
||||
|
||||
if section.Characteristics & 0x20000000: exec_sec += 1
|
||||
if section.Characteristics & 0x80000000: write_sec += 1
|
||||
if section.Characteristics & 0x40000000: read_sec += 1
|
||||
if section.SizeOfRawData + section.PointerToRawData > fsize: sec_exc = 1
|
||||
if section.Characteristics & 0x20000000:
|
||||
exec_sec += 1
|
||||
if section.Characteristics & 0x80000000:
|
||||
write_sec += 1
|
||||
if section.Characteristics & 0x40000000:
|
||||
read_sec += 1
|
||||
if section.SizeOfRawData + section.PointerToRawData > fsize:
|
||||
sec_exc = 1
|
||||
|
||||
for flag in self._CHAR_FLAGS:
|
||||
if section.Characteristics & flag:
|
||||
@@ -603,6 +675,7 @@ class pe_scanner:
|
||||
if hasattr(pe, 'FileInfo'):
|
||||
for fileinfo_list in pe.FileInfo:
|
||||
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():
|
||||
@@ -611,6 +684,7 @@ class pe_scanner:
|
||||
if k in string_keys:
|
||||
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
|
||||
base[f'{k}Length'] = float(len(v))
|
||||
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
@@ -629,12 +703,14 @@ class pe_scanner:
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
|
||||
base['ImportCount'] = float(len(pe.DIRECTORY_ENTRY_IMPORT))
|
||||
func_count = 0
|
||||
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
if getattr(entry, 'dll', None):
|
||||
try:
|
||||
dlls.add(entry.dll.decode('ascii', 'ignore').lower())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for imp in getattr(entry, 'imports', []):
|
||||
func_count += 1
|
||||
if getattr(imp, 'name', None):
|
||||
@@ -642,6 +718,7 @@ class pe_scanner:
|
||||
apis.add(imp.name.decode('ascii', 'ignore'))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
base['ImportFunctionCount'] = float(func_count)
|
||||
else:
|
||||
base['ImportCount'] = 0.0
|
||||
@@ -657,6 +734,7 @@ class pe_scanner:
|
||||
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'] = float(icon_count)
|
||||
else:
|
||||
base['IconCount'] = 0.0
|
||||
@@ -666,14 +744,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_strings(mm))
|
||||
base.update(self._extract_histograms(mm))
|
||||
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))
|
||||
base.update(self._extract_security_directory(pe, mm, fsize))
|
||||
|
||||
for k in base:
|
||||
base[k] = self._safe_float(base[k])
|
||||
@@ -685,10 +763,15 @@ class pe_scanner:
|
||||
finally:
|
||||
if pe:
|
||||
pe.close()
|
||||
if mm:
|
||||
mm.close()
|
||||
if f:
|
||||
f.close()
|
||||
|
||||
def pe_scan(self, file_path, enhanced_mode=False):
|
||||
if not self.model or not self.feature_order:
|
||||
return False, False
|
||||
|
||||
try:
|
||||
raw_data = self.extract_features(file_path)
|
||||
if not raw_data:
|
||||
@@ -708,8 +791,9 @@ class pe_scanner:
|
||||
old_fn = f"Dll_{d}"
|
||||
if old_fn in feat_map:
|
||||
vec[0, feat_map[old_fn]] = 1.0
|
||||
h = zlib.crc32(d.encode('utf-8', 'ignore')) % 256
|
||||
new_fn = f"DllHash_{h:03d}"
|
||||
|
||||
h = zlib.crc32(d.encode('utf-8', 'ignore')) % self.dll_hash_dim
|
||||
new_fn = f"DllHash_{h:0{self.dll_hash_pad}d}"
|
||||
if new_fn in feat_map:
|
||||
vec[0, feat_map[new_fn]] += 1.0
|
||||
|
||||
@@ -717,12 +801,15 @@ class pe_scanner:
|
||||
old_fn = f"Api_{a}"
|
||||
if old_fn in feat_map:
|
||||
vec[0, feat_map[old_fn]] = 1.0
|
||||
h = zlib.crc32(a.encode('utf-8', 'ignore')) % 1024
|
||||
new_fn = f"ApiHash_{h:04d}"
|
||||
|
||||
h = zlib.crc32(a.encode('utf-8', 'ignore')) % self.api_hash_dim
|
||||
new_fn = f"ApiHash_{h:0{self.api_hash_pad}d}"
|
||||
if new_fn in feat_map:
|
||||
vec[0, feat_map[new_fn]] += 1.0
|
||||
|
||||
outputs = self.model.run(None, {self.input_name: vec})
|
||||
|
||||
del raw_data, vec
|
||||
result = outputs[1]
|
||||
prob = 0.0
|
||||
|
||||
@@ -730,12 +817,12 @@ 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])
|
||||
|
||||
score = int(prob * 100)
|
||||
|
||||
if score >= 80:
|
||||
return f"General:WinPE/Malware.{score}!ml", score
|
||||
elif enhanced_mode and score >= 50:
|
||||
@@ -749,67 +836,52 @@ class pe_scanner:
|
||||
|
||||
class cloud_scanner:
|
||||
def __init__(self):
|
||||
self.session = None
|
||||
self.api_host = None
|
||||
self.api_key = None
|
||||
self.timeout = 30
|
||||
self.lock = threading.RLock()
|
||||
self.local = threading.local()
|
||||
|
||||
def _init_session(self, api_host, api_key):
|
||||
with self.lock:
|
||||
if self.session is None or self.api_host != api_host or self.api_key != api_key:
|
||||
self.api_host = api_host.rstrip('/')
|
||||
self.api_key = api_key
|
||||
self.session = requests.Session()
|
||||
self.session.headers.update({"X-API-Key": api_key, "User-Agent": "PYAS-Engine/1.1"})
|
||||
def _get_session(self, api_host, api_key):
|
||||
if not hasattr(self.local, 'session') or getattr(self.local, 'host', None) != api_host:
|
||||
self.local.session = requests.Session()
|
||||
self.local.session.headers.update({"X-API-Key": api_key, "User-Agent": "PYAS-Engine/1.1"})
|
||||
self.local.host = api_host
|
||||
|
||||
def _request(self, method, endpoint, **kwargs):
|
||||
with self.lock:
|
||||
current_session = self.session
|
||||
current_host = self.api_host
|
||||
|
||||
if not current_session:
|
||||
return None
|
||||
|
||||
return self.local.session
|
||||
|
||||
def _request(self, method, endpoint, api_host, api_key, **kwargs):
|
||||
session = self._get_session(api_host, api_key)
|
||||
try:
|
||||
r = current_session.request(method, f"{current_host}{endpoint}", timeout=self.timeout, **kwargs)
|
||||
r = session.request(method, f"{api_host}{endpoint}", timeout=self.timeout, **kwargs)
|
||||
if r.status_code == 200:
|
||||
return r
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def calc_hash(self, file_path, block_size=65536):
|
||||
try:
|
||||
h = hashlib.sha256()
|
||||
with open(file_path, "rb") as f:
|
||||
for chunk in iter(lambda: f.read(block_size), b""):
|
||||
h.update(chunk)
|
||||
return h.hexdigest()
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def rescan(self, sha256, api_host, api_key):
|
||||
self._init_session(api_host, api_key)
|
||||
r = self._request("POST", f"/api/rescan/{sha256}")
|
||||
r = self._request("POST", f"/api/rescan/{sha256}", api_host, api_key)
|
||||
return r is not None and r.json().get('status') == 'success'
|
||||
|
||||
def upload_file(self, file_path, api_host, api_key, chunk_size=4194304, need_rescan=False, max_retries=3):
|
||||
def upload_file(self, file_path, api_host, api_key, chunk_size=4194304, need_rescan=False, max_retries=3, file_hash=None):
|
||||
try:
|
||||
self._init_session(api_host, api_key)
|
||||
sha256 = self.calc_hash(file_path)
|
||||
sha256 = file_hash
|
||||
if not sha256:
|
||||
return False, None
|
||||
|
||||
status_req = self._request("GET", f"/api/processing_status/{sha256}")
|
||||
status_req = self._request("GET", f"/api/processing_status/{sha256}", api_host, api_key)
|
||||
if status_req:
|
||||
current_status = status_req.json().get('status')
|
||||
|
||||
if current_status == 'done':
|
||||
if need_rescan:
|
||||
self.rescan(sha256, api_host, api_key)
|
||||
return True, sha256
|
||||
|
||||
elif current_status in ('queued', 'processing'):
|
||||
return True, sha256
|
||||
|
||||
@@ -823,15 +895,11 @@ class cloud_scanner:
|
||||
with open(file_path, 'rb') as f:
|
||||
for i in range(total_chunks):
|
||||
chunk_data = f.read(chunk_size)
|
||||
headers = {
|
||||
"X-Chunk-Index": str(i),
|
||||
"X-Total-Chunks": str(total_chunks),
|
||||
"X-Upload-ID": upload_id
|
||||
}
|
||||
headers = {"X-Chunk-Index": str(i), "X-Total-Chunks": str(total_chunks), "X-Upload-ID": upload_id}
|
||||
|
||||
chunk_success = False
|
||||
for attempt in range(max_retries):
|
||||
r = self._request("POST", "/api/upload", files={'file': (os.path.basename(file_path), chunk_data)}, headers=headers)
|
||||
r = self._request("POST", "/api/upload", api_host, api_key, files={'file': (os.path.basename(file_path), chunk_data)}, headers=headers)
|
||||
if r:
|
||||
if i == total_chunks - 1:
|
||||
try:
|
||||
@@ -840,6 +908,7 @@ class cloud_scanner:
|
||||
sha256 = resp.get('url', '').split('/')[-1]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
chunk_success = True
|
||||
break
|
||||
time.sleep(2 ** attempt)
|
||||
@@ -857,24 +926,25 @@ class cloud_scanner:
|
||||
try:
|
||||
if not sha256:
|
||||
return False
|
||||
self._init_session(api_host, api_key)
|
||||
|
||||
is_done = False
|
||||
for _ in range(max_retries):
|
||||
r = self._request("GET", f"/api/processing_status/{sha256}")
|
||||
r = self._request("GET", f"/api/processing_status/{sha256}", api_host, api_key)
|
||||
if r:
|
||||
st = r.json().get('status', 'error')
|
||||
if st == 'done':
|
||||
is_done = True
|
||||
break
|
||||
|
||||
if st in ['error', 'failed']:
|
||||
return False
|
||||
|
||||
time.sleep(interval)
|
||||
|
||||
if not is_done:
|
||||
return False
|
||||
|
||||
r = self._request("GET", f"/api/report/{sha256}")
|
||||
r = self._request("GET", f"/api/report/{sha256}", api_host, api_key)
|
||||
if r:
|
||||
data = r.json().get('data', {})
|
||||
metadata = data.get('metadata', {})
|
||||
@@ -885,6 +955,7 @@ class cloud_scanner:
|
||||
is_malicious = 'General' in label
|
||||
sim_malicious_count = 0
|
||||
valid_sim_count = 0
|
||||
|
||||
for s in sims:
|
||||
if s.get('similarity', 0) > 80:
|
||||
valid_sim_count += 1
|
||||
@@ -893,6 +964,7 @@ class cloud_scanner:
|
||||
|
||||
if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count):
|
||||
return f"General:WinPE/Malware.{score}!cl"
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
#define AppId "{{a7d7bac3-93b8-4630-8308-c7a56bf7fdf4}"
|
||||
#define AppName "PYAS"
|
||||
#define AppVersion "3.6.2.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;
|
||||
+1774
File diff suppressed because it is too large.
Load diff
+509
@@ -0,0 +1,509 @@
|
||||
import os, gc, json, time, stat, threading
|
||||
import ctypes, ctypes.wintypes
|
||||
|
||||
from PYAS_Tools import MEMORY_BASIC_INFORMATION
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class ScannerMixin:
|
||||
def init_engine_thread(self):
|
||||
try:
|
||||
self.start_daemon_thread(self.backup_mbr)
|
||||
self.start_daemon_thread(self.relock_file)
|
||||
self.start_daemon_thread(self.init_whitelist)
|
||||
self.start_daemon_thread(self.popup_intercept_thread)
|
||||
|
||||
def log_callback(x):
|
||||
self.write_log("INFO", "Load Engine", source=os.path.basename(x))
|
||||
|
||||
self.heuristic.load_path(self.path_heuristic, callback=log_callback)
|
||||
self.properties.load_path(self.path_properties, callback=log_callback)
|
||||
self.write_log("INFO", "System", detail="Engine Initialization Complete")
|
||||
|
||||
if "-scan" in self.args_pyas:
|
||||
try:
|
||||
idx = self.args_pyas.index("-scan")
|
||||
self.trigger_context_scan(self.args_pyas[idx+1])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
with self.lock_config:
|
||||
first_launch = self.pyas_config.get("first_launch", True)
|
||||
driver_enabled = self.pyas_config.get("driver_switch", False)
|
||||
context_enabled = self.pyas_config.get("context_switch", True)
|
||||
autostart_enabled = self.pyas_config.get("autostart_switch", True)
|
||||
|
||||
self.register_context_menu(context_enabled)
|
||||
self.manage_autostart(autostart_enabled)
|
||||
|
||||
if not first_launch:
|
||||
with self.lock_config:
|
||||
if self.pyas_config.get("process_switch"):
|
||||
self.start_daemon_thread(self.protect_proc_thread)
|
||||
if self.pyas_config.get("document_switch"):
|
||||
self.start_daemon_thread(self.protect_file_thread)
|
||||
if self.pyas_config.get("system_switch"):
|
||||
self.start_daemon_thread(self.protect_system_thread)
|
||||
if self.pyas_config.get("network_switch"):
|
||||
self.start_daemon_thread(self.protect_net_thread)
|
||||
|
||||
if driver_enabled:
|
||||
if self.install_system_driver() and self.start_driver_listener(wait_ready=True):
|
||||
pass
|
||||
else:
|
||||
self.stop_system_driver()
|
||||
with self.lock_config:
|
||||
self.pyas_config["driver_switch"] = False
|
||||
self.save_config()
|
||||
|
||||
self.write_log("WARN", "System", detail="Driver Protection Failed to Start", success=False)
|
||||
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "init_engine_thread", detail=str(e), success=False)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def yield_files(self, targets):
|
||||
if isinstance(targets, str):
|
||||
if os.path.isdir(targets):
|
||||
for root, dirs, files in os.walk(targets):
|
||||
dirs[:] = [d for d in dirs if not self._is_reparse_point(os.path.join(root, d))]
|
||||
for f in files:
|
||||
yield self.norm_path(os.path.join(root, f)), False
|
||||
|
||||
elif os.path.isfile(targets):
|
||||
yield self.norm_path(targets), True
|
||||
|
||||
elif isinstance(targets, (list, tuple, set)):
|
||||
for t in targets:
|
||||
yield from self.yield_files(t)
|
||||
|
||||
def _is_reparse_point(self, path):
|
||||
try:
|
||||
if os.path.islink(path):
|
||||
return True
|
||||
if hasattr(os.path, 'isjunction') and os.path.isjunction(path):
|
||||
return True
|
||||
|
||||
st = os.lstat(path)
|
||||
return bool(getattr(st, 'st_file_attributes', 0) & 0x400)
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def scan_engine(self, file_path):
|
||||
with self.lock_config:
|
||||
ext_switch = self.pyas_config.get("extension_switch", False)
|
||||
sen_switch = self.pyas_config.get("sensitive_switch", False)
|
||||
|
||||
try:
|
||||
pe_label, _ = self.properties.pe_scan(file_path, enhanced_mode=sen_switch)
|
||||
if pe_label:
|
||||
return pe_label
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
if ext_switch:
|
||||
yara_label, _ = self.heuristic.yara_scan(file_path)
|
||||
if yara_label:
|
||||
return yara_label
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def safe_scan_engine(self, file_path):
|
||||
norm_path = self.norm_path(file_path)
|
||||
if not norm_path:
|
||||
return False
|
||||
|
||||
file_hash = self.calc_file_hash(norm_path)
|
||||
cache_key = file_hash if file_hash else os.path.normcase(norm_path)
|
||||
|
||||
with self.lock_file_ops:
|
||||
if cache_key in self.hash_cache:
|
||||
return self.hash_cache[cache_key]
|
||||
|
||||
if cache_key in self.scan_events:
|
||||
event = self.scan_events[cache_key]
|
||||
needs_scan = False
|
||||
else:
|
||||
event = threading.Event()
|
||||
self.scan_events[cache_key] = event
|
||||
needs_scan = True
|
||||
|
||||
if not needs_scan:
|
||||
event.wait(timeout=60)
|
||||
with self.lock_file_ops:
|
||||
return self.hash_cache.get(cache_key, False)
|
||||
|
||||
try:
|
||||
result = self.scan_engine(norm_path)
|
||||
with self.lock_file_ops:
|
||||
if len(self.hash_cache) > 10000:
|
||||
for k in list(self.hash_cache.keys())[:1000]:
|
||||
del self.hash_cache[k]
|
||||
|
||||
self.hash_cache[cache_key] = result
|
||||
return result
|
||||
|
||||
finally:
|
||||
with self.lock_file_ops:
|
||||
if cache_key in self.scan_events:
|
||||
self.scan_events[cache_key].set()
|
||||
del self.scan_events[cache_key]
|
||||
|
||||
gc.collect()
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def start_scan(self, targets):
|
||||
with self.lock_virus:
|
||||
if getattr(self, 'scan_running', False):
|
||||
return
|
||||
|
||||
self.scan_running = True
|
||||
self.scan_finished = False
|
||||
self.virus_results = []
|
||||
self.scan_count = 0
|
||||
self.scan_start = time.time()
|
||||
|
||||
self.scan_pool.submit(self.scan_worker, targets)
|
||||
|
||||
def scan_worker(self, targets):
|
||||
last_update = 0.0
|
||||
try:
|
||||
for file_path, is_explicit in self.yield_files(targets):
|
||||
with self.lock_virus:
|
||||
if not self.scan_running:
|
||||
break
|
||||
|
||||
norm_path = self.norm_path(file_path)
|
||||
if not norm_path:
|
||||
continue
|
||||
|
||||
current_time = time.time()
|
||||
if current_time - last_update >= 0.05:
|
||||
if self._window:
|
||||
js_cmd = f"if(window.updateScanProgress) window.updateScanProgress({json.dumps(norm_path.replace(os.sep, '/'))});"
|
||||
self.ui_queue.put(js_cmd)
|
||||
|
||||
last_update = current_time
|
||||
|
||||
was_locked = False
|
||||
try:
|
||||
with self.lock_file_ops:
|
||||
if norm_path in self.virus_lock:
|
||||
self.lock_file(norm_path, False)
|
||||
was_locked = True
|
||||
|
||||
if self.is_in_whitelist(norm_path):
|
||||
continue
|
||||
|
||||
with self.lock_virus:
|
||||
self.scan_count += 1
|
||||
|
||||
with self.lock_config:
|
||||
ext_filter = self.pyas_config.get("suffix_switch", True)
|
||||
suffix = self.pyas_config.get("suffix", [])
|
||||
|
||||
if not is_explicit and ext_filter and os.path.splitext(norm_path)[-1].lower() not in suffix:
|
||||
continue
|
||||
|
||||
result = self.safe_scan_engine(norm_path)
|
||||
if result:
|
||||
with self.lock_virus:
|
||||
self.virus_results.append(norm_path)
|
||||
|
||||
if self._window:
|
||||
js_cmd = f"if(window.addVirusResult) window.addVirusResult({json.dumps(result)}, {json.dumps(norm_path.replace(os.sep, '/'))});"
|
||||
self.ui_queue.put(js_cmd)
|
||||
|
||||
self.write_log("SCAN", "Virus Detected", source=norm_path, file_hash=self.calc_file_hash(norm_path))
|
||||
|
||||
self.cloud_check(norm_path)
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "Scan Engine", source=norm_path, detail=str(e), success=False)
|
||||
|
||||
finally:
|
||||
if was_locked:
|
||||
self.lock_file(norm_path, True)
|
||||
|
||||
finally:
|
||||
with self.lock_virus:
|
||||
self.scan_running = False
|
||||
self.scan_finished = True
|
||||
count, scanned, elapsed = len(self.virus_results), self.scan_count, int(time.time() - self.scan_start)
|
||||
|
||||
messages = {
|
||||
"traditional_switch": f"發現 {count} 個病毒,掃描 {scanned} 個檔案,耗時 {elapsed} 秒",
|
||||
"simplified_switch": f"发现 {count} 个病毒,扫描 {scanned} 个文件,耗时 {elapsed} 秒",
|
||||
"english_switch": f"Found {count} viruses, scanned {scanned} files, time {elapsed}s",
|
||||
"japanese_switch": f"{count} 個のウイルスを発見し、{scanned} 個のファイルをスキャンしました(所要時間 {elapsed} 秒)",
|
||||
"korean_switch": f"바이러스 {count}개 발견, 파일 {scanned}개 검사, 소요 시간 {elapsed}초",
|
||||
"french_switch": f"{count} virus trouvés, {scanned} fichiers analysés, temps {elapsed}s",
|
||||
"spanish_switch": f"Se encontraron {count} virus, {scanned} archivos escaneados, tiempo {elapsed}s",
|
||||
"hindi_switch": f"{count} वायरस मिले, {scanned} फ़ाइलें स्कैन की गईं, समय {elapsed}s",
|
||||
"arabic_switch": f"تم العثور على {count} فيروسات، تم فحص {scanned} ملفات، الوقت {elapsed} ثانية",
|
||||
"russian_switch": f"Найдено {count} вирусов, проверено {scanned} файлов, время {elapsed}с",
|
||||
"slovenian_switch": f"Najdenih {count} virusov, skeniranih {scanned} datotek, čas {elapsed}s"
|
||||
}
|
||||
if self._window:
|
||||
js_cmd = f"if(window.finishScan) window.finishScan({json.dumps(messages)}, {count});"
|
||||
self.ui_queue.put(js_cmd)
|
||||
|
||||
self.write_log("INFO", "Scan Completed", detail=f"Found {count} viruses, scanned {scanned} files, time {elapsed}s")
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def stop_scan(self):
|
||||
with self.lock_virus: self.scan_running = False
|
||||
|
||||
def trigger_scan(self, method):
|
||||
messages = {
|
||||
"traditional_switch": "已取消掃描",
|
||||
"simplified_switch": "已取消扫描", "english_switch":
|
||||
"Scan cancelled", "japanese_switch": "スキャンがキャンセルされました",
|
||||
"korean_switch": "스캔이 취소되었습니다",
|
||||
"french_switch": "Analyse annulée",
|
||||
"spanish_switch": "Escaneo cancelado",
|
||||
"hindi_switch": "स्कैन रद्द कर दिया गया",
|
||||
"arabic_switch": "تم إلغاء الفحص",
|
||||
"russian_switch": "Сканирование отменено",
|
||||
"slovenian_switch": "Skeniranje preklicano"
|
||||
}
|
||||
targets = []
|
||||
|
||||
if method == "smart":
|
||||
for folder in ["Desktop", "Downloads", "AppData"]:
|
||||
fp = os.path.join(self.path_user, folder)
|
||||
if os.path.exists(fp):
|
||||
targets.append(fp)
|
||||
|
||||
if os.path.exists(self.path_config):
|
||||
targets.append(self.path_config)
|
||||
|
||||
buf = ctypes.create_unicode_buffer(1024)
|
||||
max_address = 0x7FFFFFFFFFFF if ctypes.sizeof(ctypes.c_void_p) == 8 else 0x7FFFFFFF
|
||||
system_dir = self.path_system.lower()
|
||||
|
||||
for proc in self.get_process_list():
|
||||
if proc["path"] and proc["path"] != "None":
|
||||
targets.append(proc["path"])
|
||||
|
||||
pid = proc["pid"]
|
||||
if pid <= 4:
|
||||
continue
|
||||
|
||||
h_process = self.kernel32.OpenProcess(0x1000, False, pid)
|
||||
if h_process:
|
||||
try:
|
||||
address = 0
|
||||
mbi = MEMORY_BASIC_INFORMATION()
|
||||
while address < max_address and self.kernel32.VirtualQueryEx(h_process, ctypes.c_void_p(address), ctypes.byref(mbi), ctypes.sizeof(mbi)):
|
||||
if mbi.State == 0x1000 and mbi.Type == 0x1000000:
|
||||
if self.psapi.GetMappedFileNameW(h_process, ctypes.c_void_p(address), buf, 1024):
|
||||
raw_path = buf.value
|
||||
if raw_path.startswith("\\"):
|
||||
raw_path = self.device_path_to_drive(raw_path)
|
||||
|
||||
file_path = self.norm_path(raw_path)
|
||||
if file_path and not file_path.lower().startswith(system_dir):
|
||||
targets.append(file_path)
|
||||
|
||||
if mbi.RegionSize == 0:
|
||||
break
|
||||
address += mbi.RegionSize
|
||||
finally:
|
||||
self.kernel32.CloseHandle(h_process)
|
||||
|
||||
self.start_scan(list(set(targets)))
|
||||
|
||||
elif method == "file":
|
||||
targets = self.select_files()
|
||||
if targets:
|
||||
self.start_scan(targets)
|
||||
|
||||
elif self._window:
|
||||
self._window.evaluate_js(f"if(window.finishScan) window.finishScan({json.dumps(messages)}, 0);")
|
||||
|
||||
elif method == "path":
|
||||
targets = self.select_folder()
|
||||
if targets:
|
||||
self.start_scan(targets)
|
||||
|
||||
elif self._window:
|
||||
self._window.evaluate_js(f"if(window.finishScan) window.finishScan({json.dumps(messages)}, 0);")
|
||||
|
||||
elif method == "full":
|
||||
targets = [f"{chr(d)}:/" for d in range(65, 91) if os.path.exists(f"{chr(d)}:/")]
|
||||
self.start_scan(targets)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def solve_scan(self, file_paths):
|
||||
deleted_paths = []
|
||||
proc_map = {}
|
||||
for proc in self.get_process_list():
|
||||
if proc["path"] and proc["path"] != "None":
|
||||
|
||||
norm_p = self.norm_path(proc["path"], must_exist=False)
|
||||
if norm_p:
|
||||
proc_map.setdefault(os.path.normcase(norm_p), []).append(proc["pid"])
|
||||
|
||||
last_update = 0.0
|
||||
with self.lock_virus:
|
||||
deleted_set = set()
|
||||
for raw_path in file_paths:
|
||||
|
||||
path = self.norm_path(raw_path, must_exist=False)
|
||||
if not path:
|
||||
continue
|
||||
|
||||
current_time = time.time()
|
||||
if current_time - last_update >= 0.05:
|
||||
if self._window:
|
||||
self._window.evaluate_js(f"if(window.updateDeleteProgress) window.updateDeleteProgress({json.dumps(path.replace(os.sep, '/'))});")
|
||||
|
||||
last_update = current_time
|
||||
|
||||
try:
|
||||
if path in self.virus_lock:
|
||||
self.lock_file(path, False)
|
||||
|
||||
path_key = os.path.normcase(path)
|
||||
if path_key in proc_map:
|
||||
for pid in proc_map[path_key]:
|
||||
self.kill_process(pid)
|
||||
|
||||
try:
|
||||
os.chmod(path, stat.S_IWRITE)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
delete_success = False
|
||||
for _ in range(5):
|
||||
try:
|
||||
os.remove(path)
|
||||
delete_success = True
|
||||
break
|
||||
|
||||
except Exception:
|
||||
time.sleep(0.1)
|
||||
|
||||
if not delete_success:
|
||||
os.remove(path)
|
||||
|
||||
self.remove_list_items("quarantine", [path])
|
||||
deleted_paths.append(raw_path)
|
||||
deleted_set.add(path)
|
||||
self.write_log("INFO", "Virus Delete", source=path, file_hash=self.calc_file_hash(path), operate=True, success=True)
|
||||
|
||||
except Exception as e:
|
||||
self.lock_file(path, True)
|
||||
self.write_log("SCAN", "Virus Delete", source=path, file_hash=self.calc_file_hash(path), detail=str(e), operate=True, success=False)
|
||||
|
||||
if deleted_set:
|
||||
self.virus_results = [p for p in self.virus_results if p not in deleted_set]
|
||||
|
||||
remaining = len(self.virus_results)
|
||||
|
||||
messages = {
|
||||
"traditional_switch": f"剩餘 {remaining} 個病毒,已刪除 {len(deleted_paths)} 個檔案。",
|
||||
"simplified_switch": f"剩余 {remaining} 个病毒,已删除 {len(deleted_paths)} 个文件。",
|
||||
"english_switch": f"Remaining {remaining} viruses, deleted {len(deleted_paths)} files.",
|
||||
"japanese_switch": f"残りのウイルス {remaining} 個、削除されたファイル {len(deleted_paths)} 個。",
|
||||
"korean_switch": f"남은 바이러스 {remaining}개, 삭제된 파일 {len(deleted_paths)}개.",
|
||||
"french_switch": f"Virus restants : {remaining}, fichiers supprimés : {len(deleted_paths)}.",
|
||||
"spanish_switch": f"Virus restantes: {remaining}, archivos eliminados: {len(deleted_paths)}.",
|
||||
"hindi_switch": f"शेष वायरस {remaining}, हटाए गए फ़ाइलें {len(deleted_paths)}.",
|
||||
"arabic_switch": f"الفيروسات المتبقية {remaining}، تم حذف {len(deleted_paths)} ملفات.",
|
||||
"russian_switch": f"Осталось вирусов: {remaining}, удалено файлов: {len(deleted_paths)}.",
|
||||
"slovenian_switch": f"Preostalih virusov: {remaining}, izbrisanih datotek: {len(deleted_paths)}."
|
||||
}
|
||||
if self._window:
|
||||
self._window.evaluate_js(f"if(window.finishScan) window.finishScan({json.dumps(messages)}, {remaining});")
|
||||
|
||||
return deleted_paths
|
||||
|
||||
def remove_virus_result(self, paths):
|
||||
with self.lock_virus:
|
||||
norm_paths = set(self.norm_path(p, must_exist=False) for p in paths)
|
||||
self.virus_results = [p for p in self.virus_results if p not in norm_paths]
|
||||
return len(self.virus_results)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def cloud_check(self, file_path):
|
||||
norm_path = self.norm_path(file_path)
|
||||
if not norm_path:
|
||||
return
|
||||
|
||||
cache_key = os.path.normcase(norm_path)
|
||||
with self.lock_file_ops:
|
||||
if cache_key in self.cloud_pending:
|
||||
return
|
||||
|
||||
self.cloud_pending.add(cache_key)
|
||||
|
||||
self.cloud_queue.put(norm_path)
|
||||
|
||||
def cloud_worker(self):
|
||||
while True:
|
||||
try:
|
||||
file_path = self.cloud_queue.get()
|
||||
cache_key = os.path.normcase(file_path)
|
||||
|
||||
try:
|
||||
self.perform_cloud_scan(file_path)
|
||||
finally:
|
||||
with self.lock_file_ops:
|
||||
self.cloud_pending.discard(cache_key)
|
||||
|
||||
self.cloud_queue.task_done()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def perform_cloud_scan(self, file_path):
|
||||
was_locked = False
|
||||
try:
|
||||
with self.lock_config:
|
||||
if not self.pyas_config.get("cloud_switch", True):
|
||||
return False
|
||||
|
||||
api_host, api_key, max_size = self.pyas_config.get("api_host"), self.pyas_config.get("api_key"), self.pyas_config.get("size", 256 * 1024 * 1024)
|
||||
|
||||
if not os.path.exists(file_path) or not os.path.isfile(file_path):
|
||||
return False
|
||||
|
||||
with self.lock_file_ops:
|
||||
if file_path in self.virus_lock:
|
||||
self.lock_file(file_path, False)
|
||||
was_locked = True
|
||||
|
||||
if os.path.getsize(file_path) > max_size:
|
||||
if was_locked:
|
||||
self.lock_file(file_path, True)
|
||||
return False
|
||||
|
||||
file_hash = self.calc_file_hash(file_path)
|
||||
success, sha256 = self.cloud.upload_file(file_path, api_host, api_key, file_hash=file_hash)
|
||||
if was_locked:
|
||||
self.lock_file(file_path, True)
|
||||
was_locked = False
|
||||
|
||||
if not success:
|
||||
self.write_log("WARN", "Cloud API", source=file_path, detail="Failed", success=False)
|
||||
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "perform_cloud_scan", detail=str(e), success=False)
|
||||
|
||||
finally:
|
||||
if was_locked:
|
||||
try:
|
||||
self.lock_file(file_path, True)
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
+1106
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
|
||||
|
||||
pyinstaller_versionfile.create_versionfile(
|
||||
output_file="versionfile.txt",
|
||||
version='3.5.7.0',
|
||||
version='3.6.2.0',
|
||||
company_name="PYAS Security",
|
||||
file_description="PYAS Security Antivirus",
|
||||
internal_name="PYAS",
|
||||
|
||||
+348
-170
@@ -1,171 +1,349 @@
|
||||
#pragma once
|
||||
#include <fltKernel.h>
|
||||
#include <ntddk.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <ntddscsi.h>
|
||||
#include <ntstrsafe.h>
|
||||
|
||||
constexpr auto PYAS_PORT_NAME = L"\\PYAS_Output_Pipe";
|
||||
constexpr auto MAX_PATH_LEN = 1024;
|
||||
constexpr auto PYAS_POOL_TAG = 'SAYP';
|
||||
|
||||
#ifndef PROCESS_TERMINATE
|
||||
#define PROCESS_TERMINATE (0x0001)
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_THREAD
|
||||
#define PROCESS_CREATE_THREAD (0x0002)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_OPERATION
|
||||
#define PROCESS_VM_OPERATION (0x0008)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_READ
|
||||
#define PROCESS_VM_READ (0x0010)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_WRITE
|
||||
#define PROCESS_VM_WRITE (0x0020)
|
||||
#endif
|
||||
#ifndef PROCESS_DUP_HANDLE
|
||||
#define PROCESS_DUP_HANDLE (0x0040)
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_PROCESS
|
||||
#define PROCESS_CREATE_PROCESS (0x0080)
|
||||
#endif
|
||||
#ifndef PROCESS_SET_INFORMATION
|
||||
#define PROCESS_SET_INFORMATION (0x0200)
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_INFORMATION
|
||||
#define PROCESS_QUERY_INFORMATION (0x0400)
|
||||
#endif
|
||||
#ifndef PROCESS_SUSPEND_RESUME
|
||||
#define PROCESS_SUSPEND_RESUME (0x0800)
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
|
||||
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
|
||||
#endif
|
||||
#ifndef PROCESS_SET_QUOTA
|
||||
#define PROCESS_SET_QUOTA (0x0100)
|
||||
#endif
|
||||
#ifndef THREAD_TERMINATE
|
||||
#define THREAD_TERMINATE (0x0001)
|
||||
#endif
|
||||
#ifndef THREAD_SUSPEND_RESUME
|
||||
#define THREAD_SUSPEND_RESUME (0x0002)
|
||||
#endif
|
||||
#ifndef THREAD_SET_CONTEXT
|
||||
#define THREAD_SET_CONTEXT (0x0010)
|
||||
#endif
|
||||
#ifndef THREAD_SET_INFORMATION
|
||||
#define THREAD_SET_INFORMATION (0x0020)
|
||||
#endif
|
||||
#ifndef THREAD_SET_THREAD_TOKEN
|
||||
#define THREAD_SET_THREAD_TOKEN (0x0080)
|
||||
#endif
|
||||
#ifndef POOL_FLAG_NON_PAGED
|
||||
#define POOL_FLAG_NON_PAGED 0x0000000000000040UI64
|
||||
#endif
|
||||
|
||||
#ifndef FSCTL_MANAGE_BYPASS_IO
|
||||
#define FSCTL_MANAGE_BYPASS_IO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 188, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
typedef enum _FS_BPIO_OPERATIONS {
|
||||
FS_BPIO_OP_ENABLE = 1,
|
||||
FS_BPIO_OP_DISABLE = 2,
|
||||
FS_BPIO_OP_QUERY = 3,
|
||||
FS_BPIO_OP_VOLUME_STACK_PAUSE = 4,
|
||||
FS_BPIO_OP_VOLUME_STACK_RESUME = 5,
|
||||
FS_BPIO_OP_STREAM_PAUSE = 6,
|
||||
FS_BPIO_OP_STREAM_RESUME = 7,
|
||||
FS_BPIO_OP_GET_INFO = 8
|
||||
} FS_BPIO_OPERATIONS;
|
||||
|
||||
typedef struct _FS_BPIO_INPUT {
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
ULONG InFlags;
|
||||
ULONGLONG Reserved1;
|
||||
ULONGLONG Reserved2;
|
||||
} FS_BPIO_INPUT, * PFS_BPIO_INPUT;
|
||||
|
||||
typedef struct _FS_BPIO_OUTPUT {
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
ULONG OutFlags;
|
||||
ULONGLONG Reserved1;
|
||||
ULONGLONG Reserved2;
|
||||
NTSTATUS Status;
|
||||
} FS_BPIO_OUTPUT, * PFS_BPIO_OUTPUT;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct _PYAS_MESSAGE {
|
||||
ULONG MessageCode;
|
||||
ULONG ProcessId;
|
||||
WCHAR Path[MAX_PATH_LEN];
|
||||
} PYAS_MESSAGE, * PPYAS_MESSAGE;
|
||||
|
||||
typedef struct _PYAS_USER_MESSAGE {
|
||||
ULONG Command;
|
||||
WCHAR Path[MAX_PATH_LEN];
|
||||
} PYAS_USER_MESSAGE, * PPYAS_USER_MESSAGE;
|
||||
|
||||
typedef struct _DRIVER_DATA {
|
||||
PDRIVER_OBJECT DriverObject;
|
||||
PFLT_FILTER FilterHandle;
|
||||
PFLT_PORT ServerPort;
|
||||
PFLT_PORT ClientPort;
|
||||
PEPROCESS PyasProcess;
|
||||
ULONG PyasPid;
|
||||
LARGE_INTEGER Cookie;
|
||||
KSPIN_LOCK PortMutex;
|
||||
KSPIN_LOCK TrackerMutex;
|
||||
EX_RUNDOWN_REF PortRundown;
|
||||
} DRIVER_DATA, * PDRIVER_DATA;
|
||||
|
||||
typedef struct _RULE_NODE {
|
||||
struct _RULE_NODE* Next;
|
||||
UNICODE_STRING Pattern;
|
||||
} RULE_NODE, * PRULE_NODE;
|
||||
|
||||
extern DRIVER_DATA GlobalData;
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
|
||||
NTSTATUS InitializeProcessProtection();
|
||||
VOID UninitializeProcessProtection();
|
||||
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject);
|
||||
VOID UninitializeRegistryProtection();
|
||||
|
||||
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath);
|
||||
VOID UnloadRules();
|
||||
|
||||
VOID InitializeRulesEngine();
|
||||
VOID UninitializeRulesEngine();
|
||||
|
||||
VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr);
|
||||
VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr);
|
||||
|
||||
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo);
|
||||
|
||||
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName);
|
||||
|
||||
BOOLEAN IsProcessTrusted(HANDLE ProcessId);
|
||||
BOOLEAN IsTargetProtected(HANDLE ProcessId);
|
||||
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes);
|
||||
|
||||
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName);
|
||||
BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName);
|
||||
BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName);
|
||||
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite);
|
||||
|
||||
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize);
|
||||
|
||||
FORCEINLINE PVOID PyasAllocate(SIZE_T Size) {
|
||||
return ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, PYAS_POOL_TAG);
|
||||
}
|
||||
|
||||
FORCEINLINE VOID PyasFree(PVOID Ptr) {
|
||||
if (Ptr) ExFreePoolWithTag(Ptr, PYAS_POOL_TAG);
|
||||
#pragma once
|
||||
#include <fltKernel.h>
|
||||
#include <ntddk.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <ntddscsi.h>
|
||||
#include <ntstrsafe.h>
|
||||
|
||||
constexpr auto PYAS_PORT_NAME = L"\\PYAS_Output_Pipe";
|
||||
constexpr auto MAX_PATH_LEN = 1024;
|
||||
constexpr auto PYAS_POOL_TAG = 'SAYP';
|
||||
constexpr auto PYAS_OB_ALTITUDE = L"320000.4101";
|
||||
|
||||
#ifndef PROCESS_TERMINATE
|
||||
#define PROCESS_TERMINATE (0x0001)
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_THREAD
|
||||
#define PROCESS_CREATE_THREAD (0x0002)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_OPERATION
|
||||
#define PROCESS_VM_OPERATION (0x0008)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_READ
|
||||
#define PROCESS_VM_READ (0x0010)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_WRITE
|
||||
#define PROCESS_VM_WRITE (0x0020)
|
||||
#endif
|
||||
#ifndef PROCESS_DUP_HANDLE
|
||||
#define PROCESS_DUP_HANDLE (0x0040)
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_PROCESS
|
||||
#define PROCESS_CREATE_PROCESS (0x0080)
|
||||
#endif
|
||||
#ifndef PROCESS_SET_INFORMATION
|
||||
#define PROCESS_SET_INFORMATION (0x0200)
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_INFORMATION
|
||||
#define PROCESS_QUERY_INFORMATION (0x0400)
|
||||
#endif
|
||||
#ifndef PROCESS_SUSPEND_RESUME
|
||||
#define PROCESS_SUSPEND_RESUME (0x0800)
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
|
||||
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
|
||||
#endif
|
||||
#ifndef PROCESS_SET_QUOTA
|
||||
#define PROCESS_SET_QUOTA (0x0100)
|
||||
#endif
|
||||
#ifndef THREAD_TERMINATE
|
||||
#define THREAD_TERMINATE (0x0001)
|
||||
#endif
|
||||
#ifndef THREAD_SUSPEND_RESUME
|
||||
#define THREAD_SUSPEND_RESUME (0x0002)
|
||||
#endif
|
||||
#ifndef THREAD_SET_CONTEXT
|
||||
#define THREAD_SET_CONTEXT (0x0010)
|
||||
#endif
|
||||
#ifndef THREAD_SET_INFORMATION
|
||||
#define THREAD_SET_INFORMATION (0x0020)
|
||||
#endif
|
||||
#ifndef THREAD_SET_THREAD_TOKEN
|
||||
#define THREAD_SET_THREAD_TOKEN (0x0080)
|
||||
#endif
|
||||
#ifndef THREAD_IMPERSONATE
|
||||
#define THREAD_IMPERSONATE (0x0100)
|
||||
#endif
|
||||
#ifndef THREAD_DIRECT_IMPERSONATION
|
||||
#define THREAD_DIRECT_IMPERSONATION (0x0200)
|
||||
#endif
|
||||
#ifndef POOL_FLAG_NON_PAGED
|
||||
#define POOL_FLAG_NON_PAGED 0x0000000000000040UI64
|
||||
#endif
|
||||
|
||||
#ifndef FSCTL_MANAGE_BYPASS_IO
|
||||
#define FSCTL_MANAGE_BYPASS_IO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 188, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
typedef enum _FS_BPIO_OPERATIONS {
|
||||
FS_BPIO_OP_ENABLE = 1,
|
||||
FS_BPIO_OP_DISABLE = 2,
|
||||
FS_BPIO_OP_QUERY = 3,
|
||||
FS_BPIO_OP_VOLUME_STACK_PAUSE = 4,
|
||||
FS_BPIO_OP_VOLUME_STACK_RESUME = 5,
|
||||
FS_BPIO_OP_STREAM_PAUSE = 6,
|
||||
FS_BPIO_OP_STREAM_RESUME = 7,
|
||||
FS_BPIO_OP_GET_INFO = 8
|
||||
} FS_BPIO_OPERATIONS;
|
||||
|
||||
typedef struct _FS_BPIO_INPUT {
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
ULONG InFlags;
|
||||
ULONGLONG Reserved1;
|
||||
ULONGLONG Reserved2;
|
||||
} FS_BPIO_INPUT, * PFS_BPIO_INPUT;
|
||||
|
||||
typedef struct _FS_BPIO_OUTPUT {
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
ULONG OutFlags;
|
||||
ULONGLONG Reserved1;
|
||||
ULONGLONG Reserved2;
|
||||
NTSTATUS Status;
|
||||
} FS_BPIO_OUTPUT, * PFS_BPIO_OUTPUT;
|
||||
#endif
|
||||
|
||||
|
||||
typedef enum _PYAS_DRIVER_STATE {
|
||||
PyasDriverStateCold = 0,
|
||||
PyasDriverStateStarting = 1,
|
||||
PyasDriverStateRunning = 2,
|
||||
PyasDriverStateStopping = 3,
|
||||
PyasDriverStateStopRetry = 4,
|
||||
PyasDriverStateStopped = 5
|
||||
} PYAS_DRIVER_STATE;
|
||||
|
||||
typedef struct _PYAS_MESSAGE {
|
||||
ULONG MessageCode;
|
||||
ULONG ProcessId;
|
||||
WCHAR Path[MAX_PATH_LEN];
|
||||
} PYAS_MESSAGE, * PPYAS_MESSAGE;
|
||||
|
||||
typedef enum _PYAS_COMMAND {
|
||||
PyasCommandAddWhitelist = 1,
|
||||
PyasCommandRemoveWhitelist = 2,
|
||||
PyasCommandLoadRuleFile = 3,
|
||||
PyasCommandClearRules = 4,
|
||||
PyasCommandAuthorizeUnload = 5,
|
||||
PyasCommandRevokeUnload = 6,
|
||||
PyasCommandQueryState = 7
|
||||
} PYAS_COMMAND;
|
||||
|
||||
typedef struct _PYAS_USER_MESSAGE {
|
||||
ULONG Command;
|
||||
WCHAR Path[MAX_PATH_LEN];
|
||||
} PYAS_USER_MESSAGE, * PPYAS_USER_MESSAGE;
|
||||
|
||||
typedef struct _DRIVER_DATA {
|
||||
PDRIVER_OBJECT DriverObject;
|
||||
PFLT_FILTER FilterHandle;
|
||||
PFLT_PORT ServerPort;
|
||||
PFLT_PORT ClientPort;
|
||||
PEPROCESS PyasProcess;
|
||||
ULONG PyasPid;
|
||||
LARGE_INTEGER Cookie;
|
||||
KSPIN_LOCK PortMutex;
|
||||
KSPIN_LOCK TrackerMutex;
|
||||
KSPIN_LOCK PidLock;
|
||||
BOOLEAN Initialized;
|
||||
volatile LONG DriverState;
|
||||
volatile LONG UnloadAuthorized;
|
||||
volatile LONG PortStopping;
|
||||
volatile LONG ClientCloseActive;
|
||||
EX_RUNDOWN_REF PortRundown;
|
||||
KEVENT CleanupCompleteEvent;
|
||||
PVOID ObRegistrationHandle;
|
||||
} DRIVER_DATA, * PDRIVER_DATA;
|
||||
|
||||
typedef struct _RULE_NODE {
|
||||
struct _RULE_NODE* Next;
|
||||
UNICODE_STRING Pattern;
|
||||
} RULE_NODE, * PRULE_NODE;
|
||||
|
||||
typedef enum _RULE_CATEGORY {
|
||||
RuleCategoryProcess,
|
||||
RuleCategoryFile,
|
||||
RuleCategoryRegistry,
|
||||
RuleCategoryDevice,
|
||||
RuleCategoryMemory,
|
||||
RuleCategoryThread,
|
||||
RuleCategoryUnknown
|
||||
} RULE_CATEGORY;
|
||||
|
||||
#define OP_WRITE 0x00000001
|
||||
#define OP_DELETE 0x00000002
|
||||
#define OP_CREATE 0x00000004
|
||||
#define OP_EXECUTE 0x00000008
|
||||
#define OP_RENAME 0x00000010
|
||||
#define OP_IOCTL 0x00000020
|
||||
#define OP_VM_READ 0x00000040
|
||||
#define OP_VM_WRITE 0x00000080
|
||||
#define OP_TERMINATE 0x00000100
|
||||
#define OP_SUSPEND_RESUME 0x00000200
|
||||
#define OP_DUP_HANDLE 0x00000400
|
||||
#define OP_SET_INFORMATION 0x00000800
|
||||
#define OP_VM_OPERATION 0x00001000
|
||||
#define OP_CREATE_THREAD 0x00002000
|
||||
#define OP_THREAD_SET_CONTEXT 0x00004000
|
||||
#define OP_THREAD_SET_TOKEN 0x00008000
|
||||
#define OP_CREATE_PROCESS 0x00010000
|
||||
#define OP_IMAGE_LOAD 0x00020000
|
||||
#define OP_IMPERSONATE 0x00040000
|
||||
|
||||
#define PYAS_HANDLE_CREATE 0x00000001
|
||||
#define PYAS_HANDLE_DUPLICATE 0x00000002
|
||||
|
||||
#define PYAS_OBJECT_PROCESS 0x00000001
|
||||
#define PYAS_OBJECT_THREAD 0x00000002
|
||||
|
||||
#define PYAS_MEMORY_PRIVATE 0x00000001
|
||||
#define PYAS_MEMORY_MAPPED 0x00000002
|
||||
#define PYAS_MEMORY_IMAGE 0x00000004
|
||||
|
||||
#define PYAS_PROTECT_EXECUTE 0x00000001
|
||||
#define PYAS_PROTECT_EXECUTE_WRITE 0x00000002
|
||||
|
||||
typedef enum _RULE_TRI_STATE {
|
||||
RuleTriAny = 0,
|
||||
RuleTriTrue = 1,
|
||||
RuleTriFalse = 2
|
||||
} RULE_TRI_STATE;
|
||||
|
||||
typedef enum _RULE_OPERATION_MATCH {
|
||||
RuleOperationAny = 0,
|
||||
RuleOperationAll = 1
|
||||
} RULE_OPERATION_MATCH;
|
||||
|
||||
typedef struct _DYNAMIC_RULE {
|
||||
ULONG Code;
|
||||
BOOLEAN Kill;
|
||||
ULONG Priority;
|
||||
RULE_CATEGORY Category;
|
||||
ULONG Operations;
|
||||
RULE_OPERATION_MATCH OperationMatch;
|
||||
BOOLEAN Invalid;
|
||||
|
||||
PRULE_NODE Initiator;
|
||||
PRULE_NODE InitiatorExclude;
|
||||
PRULE_NODE InitiatorParent;
|
||||
PRULE_NODE InitiatorParentExclude;
|
||||
PRULE_NODE InitiatorProcessTree;
|
||||
PRULE_NODE InitiatorProcessTreeExclude;
|
||||
PRULE_NODE Target;
|
||||
PRULE_NODE TargetExclude;
|
||||
PRULE_NODE TargetProcessTree;
|
||||
PRULE_NODE TargetProcessTreeExclude;
|
||||
PRULE_NODE Creator;
|
||||
PRULE_NODE CreatorExclude;
|
||||
PRULE_NODE Parent;
|
||||
PRULE_NODE ParentExclude;
|
||||
PRULE_NODE CommandLine;
|
||||
PRULE_NODE CommandLineExclude;
|
||||
PRULE_NODE Extensions;
|
||||
|
||||
ULONG HandleTypes;
|
||||
ULONG ObjectTypes;
|
||||
ULONG MinimumRiskScore;
|
||||
ULONG MaximumRiskScore;
|
||||
ULONG ThreadMemoryTypes;
|
||||
ULONG ThreadMemoryProtections;
|
||||
ULONGLONG MinimumRegionSize;
|
||||
ULONGLONG MaximumRegionSize;
|
||||
RULE_TRI_STATE ParentMismatch;
|
||||
RULE_TRI_STATE FileOpenNameAvailable;
|
||||
RULE_TRI_STATE SubsystemProcess;
|
||||
|
||||
ULONG Threshold;
|
||||
ULONG TimeWindow;
|
||||
|
||||
struct _DYNAMIC_RULE* Next;
|
||||
} DYNAMIC_RULE, * PDYNAMIC_RULE;
|
||||
|
||||
BOOLEAN EvaluateDeviceRule(HANDLE ProcessId, PULONG OutCode, PBOOLEAN OutKill);
|
||||
|
||||
extern DRIVER_DATA GlobalData;
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
|
||||
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject);
|
||||
NTSTATUS UninitializeRegistryProtection();
|
||||
|
||||
NTSTATUS InitializeProcessProtection();
|
||||
NTSTATUS UninitializeProcessProtection();
|
||||
BOOLEAN GetProcessRelation(HANDLE ProcessId, PHANDLE ParentProcessId, PHANDLE CreatorProcessId);
|
||||
|
||||
NTSTATUS InitializeMemoryProtection(PDRIVER_OBJECT DriverObject);
|
||||
NTSTATUS UninitializeMemoryProtection(BOOLEAN WaitWithoutTimeout);
|
||||
|
||||
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath);
|
||||
VOID UnloadRules();
|
||||
|
||||
NTSTATUS InitializeRulesEngine();
|
||||
VOID UninitializeRulesEngine();
|
||||
|
||||
VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr);
|
||||
VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr);
|
||||
|
||||
NTSTATUS LoadRuleFile(PCUNICODE_STRING FilePath);
|
||||
VOID ClearDynamicRules();
|
||||
|
||||
VOID ProcessNotifyCallbackEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo);
|
||||
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName);
|
||||
|
||||
ULONG SafeGetPyasPid();
|
||||
VOID SafeSetPyasPid(ULONG Pid);
|
||||
|
||||
BOOLEAN IsProcessTrusted(HANDLE ProcessId);
|
||||
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes);
|
||||
|
||||
BOOLEAN EvaluateProcessCreateRule(
|
||||
HANDLE CreatorPid,
|
||||
HANDLE ParentPid,
|
||||
HANDLE ProcessId,
|
||||
PCUNICODE_STRING TargetPath,
|
||||
PCUNICODE_STRING CommandLine,
|
||||
BOOLEAN FileOpenNameAvailable,
|
||||
BOOLEAN IsSubsystemProcess,
|
||||
PULONG OutCode,
|
||||
PBOOLEAN OutKill
|
||||
);
|
||||
BOOLEAN EvaluateProcessAccessRule(
|
||||
HANDLE SourcePid,
|
||||
HANDLE TargetPid,
|
||||
ULONG RequestedOperations,
|
||||
ULONG HandleType,
|
||||
ULONG ObjectType,
|
||||
PULONG DeniedOperations,
|
||||
PULONG OutCode,
|
||||
PBOOLEAN OutKill
|
||||
);
|
||||
BOOLEAN EvaluateThreadRule(
|
||||
HANDLE SourcePid,
|
||||
HANDLE TargetPid,
|
||||
PVOID StartAddress,
|
||||
ULONG MemoryType,
|
||||
ULONG MemoryProtection,
|
||||
SIZE_T RegionSize,
|
||||
PULONG OutCode,
|
||||
PBOOLEAN OutKill
|
||||
);
|
||||
BOOLEAN EvaluateImageLoadRule(HANDLE ProcessId, PCUNICODE_STRING ImagePath, PIMAGE_INFO ImageInfo, PULONG OutCode, PBOOLEAN OutKill);
|
||||
BOOLEAN EvaluateProcessRule(HANDLE ProcessId, PCUNICODE_STRING TargetPath, PCUNICODE_STRING CommandLine, PULONG OutCode, PBOOLEAN OutKill);
|
||||
BOOLEAN EvaluateFileRule(HANDLE ProcessId, PCUNICODE_STRING TargetPath, ULONG Operation, PVOID WriteBuffer, ULONG WriteLength, PULONG OutCode, PBOOLEAN OutKill);
|
||||
BOOLEAN EvaluateRegistryRule(HANDLE ProcessId, PCUNICODE_STRING KeyName, ULONG Operation, PULONG OutCode, PBOOLEAN OutKill);
|
||||
BOOLEAN EvaluateMemoryRule(HANDLE SourcePid, HANDLE TargetPid, ULONG Operation, PULONG OutCode, PBOOLEAN OutKill);
|
||||
BOOLEAN EvaluateProcessHandleRule(HANDLE SourcePid, HANDLE TargetPid, ULONG Operation, PULONG OutCode, PBOOLEAN OutKill);
|
||||
|
||||
VOID QueueRuleProcessTermination(HANDLE ProcessId, BOOLEAN Kill);
|
||||
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize);
|
||||
|
||||
FORCEINLINE PVOID PyasAllocate(SIZE_T Size) {
|
||||
return ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, PYAS_POOL_TAG);
|
||||
}
|
||||
|
||||
FORCEINLINE VOID PyasFree(PVOID Ptr) {
|
||||
if (Ptr) ExFreePoolWithTag(Ptr, PYAS_POOL_TAG);
|
||||
}
|
||||
+631
-220
@@ -1,220 +1,631 @@
|
||||
#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);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
UNREFERENCED_PARAMETER(VolumeDeviceType);
|
||||
UNREFERENCED_PARAMETER(VolumeFilesystemType);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS InstanceQueryTeardown(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID InstanceTeardownStart(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static VOID InstanceTeardownComplete(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
|
||||
if (GlobalData.ServerPort) {
|
||||
FltCloseCommunicationPort(GlobalData.ServerPort);
|
||||
GlobalData.ServerPort = NULL;
|
||||
}
|
||||
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
}
|
||||
|
||||
if (g_ImageNotifyRegistered) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ImageNotifyRegistered = FALSE;
|
||||
}
|
||||
|
||||
UninitializeRegistryProtection();
|
||||
UninitializeProcessProtection();
|
||||
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS PortMessage(PVOID PortCookie, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG ReturnOutputBufferLength) {
|
||||
UNREFERENCED_PARAMETER(PortCookie);
|
||||
UNREFERENCED_PARAMETER(OutputBuffer);
|
||||
UNREFERENCED_PARAMETER(OutputBufferLength);
|
||||
|
||||
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);
|
||||
}
|
||||
else {
|
||||
RemoveDynamicWhitelist(&us);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ReturnOutputBufferLength) {
|
||||
*ReturnOutputBufferLength = 0;
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS PortConnect(PFLT_PORT ClientPort, PVOID ServerPortCookie, PVOID ConnectionContext, ULONG SizeOfContext, PVOID* ConnectionPortCookie) {
|
||||
UNREFERENCED_PARAMETER(ServerPortCookie);
|
||||
UNREFERENCED_PARAMETER(ConnectionContext);
|
||||
UNREFERENCED_PARAMETER(SizeOfContext);
|
||||
UNREFERENCED_PARAMETER(ConnectionPortCookie);
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
|
||||
|
||||
GlobalData.ClientPort = ClientPort;
|
||||
GlobalData.PyasPid = (ULONG)(ULONG_PTR)PsGetCurrentProcessId();
|
||||
GlobalData.PyasProcess = PsGetCurrentProcess();
|
||||
|
||||
ExReInitializeRundownProtection(&GlobalData.PortRundown);
|
||||
|
||||
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID PortDisconnect(PVOID ConnectionCookie) {
|
||||
UNREFERENCED_PARAMETER(ConnectionCookie);
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
|
||||
|
||||
GlobalData.ClientPort = NULL;
|
||||
GlobalData.PyasPid = 0;
|
||||
GlobalData.PyasProcess = NULL;
|
||||
|
||||
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
|
||||
|
||||
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
|
||||
}
|
||||
|
||||
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
|
||||
{ IRP_MJ_CREATE, 0, ProtectFile_PreCreate, NULL },
|
||||
{ IRP_MJ_WRITE, 0, ProtectFile_PreWrite, NULL },
|
||||
{ IRP_MJ_SET_INFORMATION, 0, ProtectFile_PreSetInfo, NULL },
|
||||
{ IRP_MJ_SET_SECURITY, 0, ProtectFile_SetSecurity, NULL },
|
||||
{ IRP_MJ_FILE_SYSTEM_CONTROL, 0, ProtectFile_FileSystemControl, NULL },
|
||||
{ IRP_MJ_DEVICE_CONTROL, 0, ProtectBoot_PreDeviceControl, NULL },
|
||||
{ IRP_MJ_OPERATION_END }
|
||||
};
|
||||
|
||||
CONST FLT_REGISTRATION FilterRegistration = {
|
||||
sizeof(FLT_REGISTRATION),
|
||||
FLT_REGISTRATION_VERSION,
|
||||
0,
|
||||
NULL,
|
||||
Callbacks,
|
||||
DriverUnload,
|
||||
InstanceSetup,
|
||||
InstanceQueryTeardown,
|
||||
InstanceTeardownStart,
|
||||
InstanceTeardownComplete,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
PSECURITY_DESCRIPTOR sd = NULL;
|
||||
OBJECT_ATTRIBUTES oa = { 0 };
|
||||
UNICODE_STRING name = { 0 };
|
||||
|
||||
RtlZeroMemory(&GlobalData, sizeof(GlobalData));
|
||||
GlobalData.DriverObject = DriverObject;
|
||||
KeInitializeSpinLock(&GlobalData.PortMutex);
|
||||
KeInitializeSpinLock(&GlobalData.TrackerMutex);
|
||||
|
||||
ExInitializeRundownProtection(&GlobalData.PortRundown);
|
||||
|
||||
InitializeRulesEngine();
|
||||
|
||||
status = LoadRulesFromDisk(RegistryPath);
|
||||
|
||||
status = FltRegisterFilter(DriverObject, &FilterRegistration, &GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
return status;
|
||||
}
|
||||
|
||||
status = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
|
||||
if (NT_SUCCESS(status)) {
|
||||
RtlInitUnicodeString(&name, PYAS_PORT_NAME);
|
||||
InitializeObjectAttributes(&oa, &name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, sd);
|
||||
status = FltCreateCommunicationPort(GlobalData.FilterHandle, &GlobalData.ServerPort, &oa, NULL, PortConnect, PortDisconnect, PortMessage, 1);
|
||||
FltFreeSecurityDescriptor(sd);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
return status;
|
||||
}
|
||||
|
||||
InitializeProcessProtection();
|
||||
|
||||
InitializeRegistryProtection(DriverObject);
|
||||
|
||||
NTSTATUS imageNotifyStatus = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
if (NT_SUCCESS(imageNotifyStatus)) {
|
||||
g_ImageNotifyRegistered = TRUE;
|
||||
}
|
||||
|
||||
status = FltStartFiltering(GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
if (g_ImageNotifyRegistered) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ImageNotifyRegistered = FALSE;
|
||||
}
|
||||
|
||||
UninitializeRegistryProtection();
|
||||
UninitializeProcessProtection();
|
||||
|
||||
if (GlobalData.ServerPort) {
|
||||
FltCloseCommunicationPort(GlobalData.ServerPort);
|
||||
GlobalData.ServerPort = NULL;
|
||||
}
|
||||
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
}
|
||||
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
#include "DriverCommon.h"
|
||||
|
||||
DRIVER_DATA GlobalData;
|
||||
static BOOLEAN g_ProcessNotifyRegistered = FALSE;
|
||||
static BOOLEAN g_ProcessProtectionInitialized = FALSE;
|
||||
static BOOLEAN g_RegistryProtectionInitialized = FALSE;
|
||||
static BOOLEAN g_RulesEngineInitialized = FALSE;
|
||||
|
||||
static NTSTATUS InstanceSetup(
|
||||
PCFLT_RELATED_OBJECTS FltObjects,
|
||||
FLT_INSTANCE_SETUP_FLAGS Flags,
|
||||
DEVICE_TYPE VolumeDeviceType,
|
||||
FLT_FILESYSTEM_TYPE VolumeFilesystemType
|
||||
) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
UNREFERENCED_PARAMETER(VolumeDeviceType);
|
||||
UNREFERENCED_PARAMETER(VolumeFilesystemType);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS InstanceQueryTeardown(
|
||||
PCFLT_RELATED_OBJECTS FltObjects,
|
||||
FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags
|
||||
) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID InstanceTeardownStart(
|
||||
PCFLT_RELATED_OBJECTS FltObjects,
|
||||
FLT_INSTANCE_TEARDOWN_FLAGS Flags
|
||||
) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static VOID InstanceTeardownComplete(
|
||||
PCFLT_RELATED_OBJECTS FltObjects,
|
||||
FLT_INSTANCE_TEARDOWN_FLAGS Flags
|
||||
) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static NTSTATUS SelectFailure(NTSTATUS CurrentStatus, NTSTATUS CandidateStatus) {
|
||||
if (!NT_SUCCESS(CurrentStatus)) return CurrentStatus;
|
||||
return CandidateStatus;
|
||||
}
|
||||
|
||||
static NTSTATUS UnregisterRuntimeCallbacksPass(BOOLEAN WaitWithoutTimeout) {
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
|
||||
NTSTATUS MemoryStatus = UninitializeMemoryProtection(WaitWithoutTimeout);
|
||||
if (!NT_SUCCESS(MemoryStatus)) {
|
||||
Status = SelectFailure(Status, MemoryStatus);
|
||||
}
|
||||
|
||||
if (g_ProcessNotifyRegistered) {
|
||||
NTSTATUS ProcessNotifyStatus = PsSetCreateProcessNotifyRoutineEx(
|
||||
ProcessNotifyCallbackEx,
|
||||
TRUE
|
||||
);
|
||||
|
||||
if (NT_SUCCESS(ProcessNotifyStatus) || ProcessNotifyStatus == STATUS_INVALID_PARAMETER) {
|
||||
g_ProcessNotifyRegistered = FALSE;
|
||||
}
|
||||
else {
|
||||
Status = SelectFailure(Status, ProcessNotifyStatus);
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_ProcessNotifyRegistered && g_ProcessProtectionInitialized) {
|
||||
NTSTATUS ProcessStatus = UninitializeProcessProtection();
|
||||
if (NT_SUCCESS(ProcessStatus)) {
|
||||
g_ProcessProtectionInitialized = FALSE;
|
||||
}
|
||||
else {
|
||||
Status = SelectFailure(Status, ProcessStatus);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_RegistryProtectionInitialized) {
|
||||
NTSTATUS RegistryStatus = UninitializeRegistryProtection();
|
||||
if (NT_SUCCESS(RegistryStatus)) {
|
||||
g_RegistryProtectionInitialized = FALSE;
|
||||
}
|
||||
else {
|
||||
Status = SelectFailure(Status, RegistryStatus);
|
||||
}
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
static NTSTATUS UnregisterRuntimeCallbacks(BOOLEAN WaitWithoutTimeout) {
|
||||
for (;;) {
|
||||
NTSTATUS Status = UnregisterRuntimeCallbacksPass(WaitWithoutTimeout);
|
||||
if (NT_SUCCESS(Status) || !WaitWithoutTimeout) return Status;
|
||||
|
||||
LARGE_INTEGER Delay;
|
||||
Delay.QuadPart = -(10LL * 10 * 1000);
|
||||
KeDelayExecutionThread(KernelMode, FALSE, &Delay);
|
||||
}
|
||||
}
|
||||
|
||||
static PEPROCESS DetachConnectedProcessReference(PEPROCESS ExpectedProcess) {
|
||||
PEPROCESS Process = NULL;
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
|
||||
|
||||
if (!ExpectedProcess || GlobalData.PyasProcess == ExpectedProcess) {
|
||||
Process = GlobalData.PyasProcess;
|
||||
GlobalData.PyasProcess = NULL;
|
||||
}
|
||||
|
||||
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
|
||||
return Process;
|
||||
}
|
||||
|
||||
static VOID ReleaseConnectedProcessReference() {
|
||||
PEPROCESS Process = DetachConnectedProcessReference(NULL);
|
||||
|
||||
SafeSetPyasPid(0);
|
||||
InterlockedExchange(&GlobalData.UnloadAuthorized, 0);
|
||||
|
||||
if (Process) {
|
||||
ObDereferenceObject(Process);
|
||||
}
|
||||
}
|
||||
|
||||
static VOID CloseCurrentClientPort() {
|
||||
if (InterlockedCompareExchange(&GlobalData.ClientCloseActive, 1, 0) != 0) return;
|
||||
|
||||
PFLT_FILTER FilterHandle = NULL;
|
||||
PFLT_PORT ClientPort = NULL;
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
|
||||
FilterHandle = GlobalData.FilterHandle;
|
||||
ClientPort = GlobalData.ClientPort;
|
||||
GlobalData.ClientPort = NULL;
|
||||
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
|
||||
|
||||
if (FilterHandle && ClientPort) {
|
||||
FltCloseClientPort(FilterHandle, &ClientPort);
|
||||
}
|
||||
|
||||
InterlockedExchange(&GlobalData.ClientCloseActive, 0);
|
||||
}
|
||||
|
||||
static VOID WaitForClientPortClose() {
|
||||
LARGE_INTEGER Delay;
|
||||
Delay.QuadPart = -(10LL * 1000);
|
||||
|
||||
while (InterlockedCompareExchange(&GlobalData.ClientCloseActive, 0, 0) != 0) {
|
||||
KeDelayExecutionThread(KernelMode, FALSE, &Delay);
|
||||
}
|
||||
}
|
||||
|
||||
static VOID ReleaseFilterResources() {
|
||||
InterlockedExchange(&GlobalData.PortStopping, 1);
|
||||
|
||||
if (GlobalData.ServerPort) {
|
||||
PFLT_PORT ServerPort = GlobalData.ServerPort;
|
||||
GlobalData.ServerPort = NULL;
|
||||
FltCloseCommunicationPort(ServerPort);
|
||||
}
|
||||
|
||||
CloseCurrentClientPort();
|
||||
WaitForClientPortClose();
|
||||
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
|
||||
|
||||
if (GlobalData.FilterHandle) {
|
||||
PFLT_FILTER FilterHandle = GlobalData.FilterHandle;
|
||||
FltUnregisterFilter(FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
}
|
||||
|
||||
ReleaseConnectedProcessReference();
|
||||
}
|
||||
|
||||
static NTSTATUS CleanupDriverState(BOOLEAN WaitWithoutTimeout) {
|
||||
NTSTATUS Status = UnregisterRuntimeCallbacks(WaitWithoutTimeout);
|
||||
if (!NT_SUCCESS(Status)) return Status;
|
||||
|
||||
GlobalData.Initialized = FALSE;
|
||||
ReleaseFilterResources();
|
||||
|
||||
if (g_RulesEngineInitialized) {
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
g_RulesEngineInitialized = FALSE;
|
||||
}
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS BeginUnload(BOOLEAN MandatoryUnload) {
|
||||
for (;;) {
|
||||
LONG State = InterlockedCompareExchange(
|
||||
&GlobalData.DriverState,
|
||||
PyasDriverStateCold,
|
||||
PyasDriverStateCold
|
||||
);
|
||||
|
||||
if (State == PyasDriverStateStopped) return STATUS_SUCCESS;
|
||||
|
||||
if (State == PyasDriverStateStopping) {
|
||||
LARGE_INTEGER Timeout;
|
||||
PLARGE_INTEGER TimeoutPointer = NULL;
|
||||
|
||||
if (!MandatoryUnload) {
|
||||
Timeout.QuadPart = -(2LL * 10 * 1000 * 1000);
|
||||
TimeoutPointer = &Timeout;
|
||||
}
|
||||
|
||||
NTSTATUS WaitStatus = KeWaitForSingleObject(
|
||||
&GlobalData.CleanupCompleteEvent,
|
||||
Executive,
|
||||
KernelMode,
|
||||
FALSE,
|
||||
TimeoutPointer
|
||||
);
|
||||
|
||||
if (!MandatoryUnload && WaitStatus == STATUS_TIMEOUT) {
|
||||
return STATUS_DEVICE_BUSY;
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(WaitStatus)) return WaitStatus;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (State != PyasDriverStateRunning && State != PyasDriverStateStopRetry) {
|
||||
return STATUS_DEVICE_NOT_READY;
|
||||
}
|
||||
|
||||
if (!MandatoryUnload && InterlockedCompareExchange(&GlobalData.UnloadAuthorized, 0, 1) != 1) {
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (MandatoryUnload) {
|
||||
InterlockedExchange(&GlobalData.UnloadAuthorized, 0);
|
||||
}
|
||||
|
||||
if (InterlockedCompareExchange(
|
||||
&GlobalData.DriverState,
|
||||
PyasDriverStateStopping,
|
||||
State
|
||||
) != State) {
|
||||
if (!MandatoryUnload) return STATUS_DEVICE_BUSY;
|
||||
continue;
|
||||
}
|
||||
|
||||
KeClearEvent(&GlobalData.CleanupCompleteEvent);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
|
||||
BOOLEAN MandatoryUnload = FlagOn(Flags, FLTFL_FILTER_UNLOAD_MANDATORY) ? TRUE : FALSE;
|
||||
|
||||
NTSTATUS Status = BeginUnload(MandatoryUnload);
|
||||
if (!NT_SUCCESS(Status)) return Status;
|
||||
|
||||
LONG State = InterlockedCompareExchange(
|
||||
&GlobalData.DriverState,
|
||||
PyasDriverStateCold,
|
||||
PyasDriverStateCold
|
||||
);
|
||||
|
||||
if (State == PyasDriverStateStopped) return STATUS_SUCCESS;
|
||||
|
||||
Status = CleanupDriverState(MandatoryUnload);
|
||||
|
||||
InterlockedExchange(
|
||||
&GlobalData.DriverState,
|
||||
NT_SUCCESS(Status) ? PyasDriverStateStopped : PyasDriverStateStopRetry
|
||||
);
|
||||
|
||||
KeSetEvent(&GlobalData.CleanupCompleteEvent, IO_NO_INCREMENT, FALSE);
|
||||
return Status;
|
||||
}
|
||||
|
||||
static NTSTATUS PortMessage(
|
||||
PVOID PortCookie,
|
||||
PVOID InputBuffer,
|
||||
ULONG InputBufferLength,
|
||||
PVOID OutputBuffer,
|
||||
ULONG OutputBufferLength,
|
||||
PULONG ReturnOutputBufferLength
|
||||
) {
|
||||
if (ReturnOutputBufferLength) *ReturnOutputBufferLength = 0;
|
||||
|
||||
if (InterlockedCompareExchange(&GlobalData.PortStopping, 0, 0) != 0) {
|
||||
return STATUS_DELETE_PENDING;
|
||||
}
|
||||
|
||||
if (!ExAcquireRundownProtection(&GlobalData.PortRundown)) {
|
||||
return STATUS_DELETE_PENDING;
|
||||
}
|
||||
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
PYAS_USER_MESSAGE Message = { 0 };
|
||||
UNICODE_STRING Path = { 0 };
|
||||
LONG DriverState = PyasDriverStateCold;
|
||||
ULONG StateValue = PyasDriverStateCold;
|
||||
|
||||
if (!PortCookie || PortCookie != GlobalData.PyasProcess) {
|
||||
Status = STATUS_ACCESS_DENIED;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (!InputBuffer || InputBufferLength < sizeof(PYAS_USER_MESSAGE)) {
|
||||
Status = STATUS_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
__try {
|
||||
RtlCopyMemory(&Message, InputBuffer, sizeof(Message));
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
Status = GetExceptionCode();
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Message.Path[MAX_PATH_LEN - 1] = L'\0';
|
||||
|
||||
if (Message.Command == PyasCommandQueryState) {
|
||||
if (!OutputBuffer || OutputBufferLength < sizeof(ULONG) || !ReturnOutputBufferLength) {
|
||||
Status = STATUS_BUFFER_TOO_SMALL;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
StateValue = (ULONG)InterlockedCompareExchange(
|
||||
&GlobalData.DriverState,
|
||||
PyasDriverStateCold,
|
||||
PyasDriverStateCold
|
||||
);
|
||||
|
||||
__try {
|
||||
*(PULONG)OutputBuffer = StateValue;
|
||||
*ReturnOutputBufferLength = sizeof(ULONG);
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
Status = GetExceptionCode();
|
||||
}
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (Message.Command == PyasCommandAuthorizeUnload) {
|
||||
DriverState = InterlockedCompareExchange(
|
||||
&GlobalData.DriverState,
|
||||
PyasDriverStateCold,
|
||||
PyasDriverStateCold
|
||||
);
|
||||
|
||||
if (DriverState != PyasDriverStateRunning && DriverState != PyasDriverStateStopRetry) {
|
||||
Status = STATUS_DEVICE_NOT_READY;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
InterlockedExchange(&GlobalData.UnloadAuthorized, 1);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (Message.Command == PyasCommandRevokeUnload) {
|
||||
InterlockedExchange(&GlobalData.UnloadAuthorized, 0);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (Message.Command < PyasCommandAddWhitelist || Message.Command > PyasCommandClearRules) {
|
||||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
DriverState = InterlockedCompareExchange(
|
||||
&GlobalData.DriverState,
|
||||
PyasDriverStateCold,
|
||||
PyasDriverStateCold
|
||||
);
|
||||
|
||||
if (DriverState != PyasDriverStateRunning) {
|
||||
Status = STATUS_DEVICE_NOT_READY;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
RtlInitUnicodeString(&Path, Message.Path);
|
||||
|
||||
if (Message.Command == PyasCommandAddWhitelist) {
|
||||
if (Path.Length == 0) {
|
||||
Status = STATUS_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
AddDynamicWhitelist(&Path);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (Message.Command == PyasCommandRemoveWhitelist) {
|
||||
if (Path.Length == 0) {
|
||||
Status = STATUS_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
RemoveDynamicWhitelist(&Path);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
if (Message.Command == PyasCommandLoadRuleFile) {
|
||||
if (Path.Length == 0) {
|
||||
Status = STATUS_INVALID_PARAMETER;
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
Status = LoadRuleFile(&Path);
|
||||
goto Exit;
|
||||
}
|
||||
|
||||
ClearDynamicRules();
|
||||
|
||||
Exit:
|
||||
ExReleaseRundownProtection(&GlobalData.PortRundown);
|
||||
return Status;
|
||||
}
|
||||
|
||||
static NTSTATUS PortConnect(
|
||||
PFLT_PORT ClientPort,
|
||||
PVOID ServerPortCookie,
|
||||
PVOID ConnectionContext,
|
||||
ULONG SizeOfContext,
|
||||
PVOID* ConnectionPortCookie
|
||||
) {
|
||||
UNREFERENCED_PARAMETER(ServerPortCookie);
|
||||
UNREFERENCED_PARAMETER(ConnectionContext);
|
||||
UNREFERENCED_PARAMETER(SizeOfContext);
|
||||
|
||||
if (!ConnectionPortCookie) return STATUS_INVALID_PARAMETER;
|
||||
*ConnectionPortCookie = NULL;
|
||||
|
||||
if (InterlockedCompareExchange(&GlobalData.PortStopping, 0, 0) != 0) {
|
||||
return STATUS_DELETE_PENDING;
|
||||
}
|
||||
|
||||
LONG DriverState = InterlockedCompareExchange(
|
||||
&GlobalData.DriverState,
|
||||
PyasDriverStateCold,
|
||||
PyasDriverStateCold
|
||||
);
|
||||
|
||||
if (DriverState != PyasDriverStateRunning && DriverState != PyasDriverStateStopRetry) {
|
||||
return STATUS_DEVICE_NOT_READY;
|
||||
}
|
||||
|
||||
if (!ExAcquireRundownProtection(&GlobalData.PortRundown)) {
|
||||
return STATUS_DELETE_PENDING;
|
||||
}
|
||||
|
||||
PEPROCESS ConnectingProcess = PsGetCurrentProcess();
|
||||
ObReferenceObject(ConnectingProcess);
|
||||
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
|
||||
|
||||
DriverState = InterlockedCompareExchange(
|
||||
&GlobalData.DriverState,
|
||||
PyasDriverStateCold,
|
||||
PyasDriverStateCold
|
||||
);
|
||||
|
||||
if (InterlockedCompareExchange(&GlobalData.PortStopping, 0, 0) != 0) {
|
||||
Status = STATUS_DELETE_PENDING;
|
||||
}
|
||||
else if (DriverState != PyasDriverStateRunning && DriverState != PyasDriverStateStopRetry) {
|
||||
Status = STATUS_DEVICE_NOT_READY;
|
||||
}
|
||||
else if (GlobalData.ClientPort || GlobalData.PyasProcess) {
|
||||
Status = STATUS_DEVICE_BUSY;
|
||||
}
|
||||
else {
|
||||
GlobalData.ClientPort = ClientPort;
|
||||
GlobalData.PyasProcess = ConnectingProcess;
|
||||
*ConnectionPortCookie = ConnectingProcess;
|
||||
}
|
||||
|
||||
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
|
||||
|
||||
if (!NT_SUCCESS(Status)) {
|
||||
ObDereferenceObject(ConnectingProcess);
|
||||
ExReleaseRundownProtection(&GlobalData.PortRundown);
|
||||
return Status;
|
||||
}
|
||||
|
||||
SafeSetPyasPid((ULONG)(ULONG_PTR)PsGetCurrentProcessId());
|
||||
InterlockedExchange(&GlobalData.UnloadAuthorized, 0);
|
||||
ExReleaseRundownProtection(&GlobalData.PortRundown);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID PortDisconnect(PVOID ConnectionCookie) {
|
||||
if (!ConnectionCookie) return;
|
||||
|
||||
PEPROCESS Process = DetachConnectedProcessReference((PEPROCESS)ConnectionCookie);
|
||||
if (!Process) return;
|
||||
|
||||
CloseCurrentClientPort();
|
||||
InterlockedExchange(&GlobalData.UnloadAuthorized, 0);
|
||||
SafeSetPyasPid(0);
|
||||
ObDereferenceObject(Process);
|
||||
}
|
||||
|
||||
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
|
||||
{ IRP_MJ_CREATE, 0, ProtectFile_PreCreate, NULL },
|
||||
{ IRP_MJ_WRITE, 0, ProtectFile_PreWrite, NULL },
|
||||
{ IRP_MJ_SET_INFORMATION, 0, ProtectFile_PreSetInfo, NULL },
|
||||
{ IRP_MJ_SET_SECURITY, 0, ProtectFile_SetSecurity, NULL },
|
||||
{ IRP_MJ_FILE_SYSTEM_CONTROL, 0, ProtectFile_FileSystemControl, NULL },
|
||||
{ IRP_MJ_DEVICE_CONTROL, 0, ProtectBoot_PreDeviceControl, NULL },
|
||||
{ IRP_MJ_OPERATION_END }
|
||||
};
|
||||
|
||||
CONST FLT_REGISTRATION FilterRegistration = {
|
||||
sizeof(FLT_REGISTRATION),
|
||||
FLT_REGISTRATION_VERSION,
|
||||
0,
|
||||
NULL,
|
||||
Callbacks,
|
||||
DriverUnload,
|
||||
InstanceSetup,
|
||||
InstanceQueryTeardown,
|
||||
InstanceTeardownStart,
|
||||
InstanceTeardownComplete,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
extern "C" NTSTATUS DriverEntry(
|
||||
PDRIVER_OBJECT DriverObject,
|
||||
PUNICODE_STRING RegistryPath
|
||||
) {
|
||||
RtlZeroMemory(&GlobalData, sizeof(GlobalData));
|
||||
GlobalData.DriverObject = DriverObject;
|
||||
KeInitializeSpinLock(&GlobalData.PortMutex);
|
||||
KeInitializeSpinLock(&GlobalData.TrackerMutex);
|
||||
KeInitializeSpinLock(&GlobalData.PidLock);
|
||||
ExInitializeRundownProtection(&GlobalData.PortRundown);
|
||||
KeInitializeEvent(&GlobalData.CleanupCompleteEvent, NotificationEvent, FALSE);
|
||||
InterlockedExchange(&GlobalData.PortStopping, 0);
|
||||
InterlockedExchange(&GlobalData.ClientCloseActive, 0);
|
||||
InterlockedExchange(&GlobalData.DriverState, PyasDriverStateStarting);
|
||||
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
|
||||
|
||||
Status = InitializeRulesEngine();
|
||||
if (!NT_SUCCESS(Status)) {
|
||||
InterlockedExchange(&GlobalData.DriverState, PyasDriverStateStopped);
|
||||
KeSetEvent(&GlobalData.CleanupCompleteEvent, IO_NO_INCREMENT, FALSE);
|
||||
return Status;
|
||||
}
|
||||
g_RulesEngineInitialized = TRUE;
|
||||
|
||||
Status = LoadRulesFromDisk(RegistryPath);
|
||||
if (!NT_SUCCESS(Status)) goto Failure;
|
||||
|
||||
Status = FltRegisterFilter(DriverObject, &FilterRegistration, &GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(Status)) goto Failure;
|
||||
|
||||
Status = FltBuildDefaultSecurityDescriptor(&SecurityDescriptor, FLT_PORT_ALL_ACCESS);
|
||||
if (!NT_SUCCESS(Status)) goto Failure;
|
||||
|
||||
{
|
||||
UNICODE_STRING PortName;
|
||||
RtlInitUnicodeString(&PortName, PYAS_PORT_NAME);
|
||||
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
InitializeObjectAttributes(
|
||||
&ObjectAttributes,
|
||||
&PortName,
|
||||
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
|
||||
NULL,
|
||||
SecurityDescriptor
|
||||
);
|
||||
|
||||
Status = FltCreateCommunicationPort(
|
||||
GlobalData.FilterHandle,
|
||||
&GlobalData.ServerPort,
|
||||
&ObjectAttributes,
|
||||
NULL,
|
||||
PortConnect,
|
||||
PortDisconnect,
|
||||
PortMessage,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
FltFreeSecurityDescriptor(SecurityDescriptor);
|
||||
if (!NT_SUCCESS(Status)) goto Failure;
|
||||
|
||||
Status = InitializeRegistryProtection(DriverObject);
|
||||
if (!NT_SUCCESS(Status)) goto Failure;
|
||||
g_RegistryProtectionInitialized = TRUE;
|
||||
|
||||
Status = InitializeProcessProtection();
|
||||
if (!NT_SUCCESS(Status)) goto Failure;
|
||||
g_ProcessProtectionInitialized = TRUE;
|
||||
|
||||
Status = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallbackEx, FALSE);
|
||||
if (!NT_SUCCESS(Status)) goto Failure;
|
||||
g_ProcessNotifyRegistered = TRUE;
|
||||
|
||||
Status = InitializeMemoryProtection(DriverObject);
|
||||
if (!NT_SUCCESS(Status)) goto Failure;
|
||||
|
||||
Status = FltStartFiltering(GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(Status)) goto Failure;
|
||||
|
||||
GlobalData.Initialized = TRUE;
|
||||
InterlockedExchange(&GlobalData.DriverState, PyasDriverStateRunning);
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
Failure:
|
||||
CleanupDriverState(TRUE);
|
||||
InterlockedExchange(&GlobalData.DriverState, PyasDriverStateStopped);
|
||||
KeSetEvent(&GlobalData.CleanupCompleteEvent, IO_NO_INCREMENT, FALSE);
|
||||
return Status;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
@@ -92,6 +92,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)fltmgr.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/INTEGRITYCHECK %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
@@ -103,6 +104,7 @@
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>$(DDK_LIB_PATH)fltmgr.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/INTEGRITYCHECK %(AdditionalOptions)</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@@ -118,6 +120,7 @@
|
||||
<ClCompile Include="DriverEntry.cpp" />
|
||||
<ClCompile Include="ProtectBoot.cpp" />
|
||||
<ClCompile Include="ProtectFile.cpp" />
|
||||
<ClCompile Include="ProtectMemory.cpp" />
|
||||
<ClCompile Include="ProtectProcess.cpp" />
|
||||
<ClCompile Include="ProtectRegistry.cpp" />
|
||||
<ClCompile Include="ProtectRules.cpp" />
|
||||
|
||||
@@ -47,5 +47,8 @@
|
||||
<ClCompile Include="DriverEntry.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProtectMemory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -22,9 +22,13 @@ FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data,
|
||||
IoControlCode == IOCTL_DISK_FORMAT_TRACKS_EX) {
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
|
||||
if (EvaluateDeviceRule(Pid, &RuleCode, &Kill)) {
|
||||
UNICODE_STRING MsgStr = RTL_CONSTANT_STRING(L"Disk_Wiper_Attempt");
|
||||
SendMessageToUser(4001, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length);
|
||||
SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length);
|
||||
QueueRuleProcessTermination(Pid, Kill);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
return FLT_PREOP_COMPLETE;
|
||||
}
|
||||
|
||||
+76
-225
@@ -1,14 +1,5 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
static BOOLEAN IsHoneyToken(PUNICODE_STRING Path) {
|
||||
if (!Path || !Path->Buffer) return FALSE;
|
||||
if (WildcardMatch(L"*PYAS_Honey*", Path->Buffer, Path->Length) ||
|
||||
WildcardMatch(L"*Backup_Secret*", Path->Buffer, Path->Length)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
@@ -17,93 +8,39 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_R
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
UNICODE_STRING FileName = { 0 };
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
if (!NT_SUCCESS(status) || !nameInfo) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
if (!NT_SUCCESS(status) || !nameInfo) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
FileName = nameInfo->Name;
|
||||
if (!FileName.Buffer || FileName.Length == 0) {
|
||||
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer || nameInfo->Name.Length == 0) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
BOOLEAN Trusted = IsProcessTrusted(Pid);
|
||||
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
ULONG CreateDisposition = (Data->Iopb->Parameters.Create.Options >> 24) & 0xFF;
|
||||
BOOLEAN IsCreateAction = (CreateDisposition == FILE_CREATE ||
|
||||
CreateDisposition == FILE_SUPERSEDE ||
|
||||
CreateDisposition == FILE_OVERWRITE ||
|
||||
CreateDisposition == FILE_OVERWRITE_IF ||
|
||||
CreateDisposition == FILE_OPEN_IF);
|
||||
BOOLEAN IsCreateAction = (CreateDisposition == FILE_CREATE || CreateDisposition == FILE_SUPERSEDE || CreateDisposition == FILE_OVERWRITE || CreateDisposition == FILE_OVERWRITE_IF || CreateDisposition == FILE_OPEN_IF);
|
||||
ULONG DesiredAccess = Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess;
|
||||
|
||||
if (IsHoneyToken(&FileName)) {
|
||||
if (!Trusted) {
|
||||
if ((Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (FILE_WRITE_DATA | DELETE)) || IsCreateAction) {
|
||||
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
ULONG Operation = 0;
|
||||
if (IsCreateAction) Operation |= OP_CREATE;
|
||||
if (DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | WRITE_DAC | WRITE_OWNER | GENERIC_WRITE)) Operation |= OP_WRITE;
|
||||
if (DesiredAccess & (DELETE | FILE_DELETE_CHILD)) Operation |= OP_DELETE;
|
||||
|
||||
if (Operation != 0) {
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
if (EvaluateFileRule(Pid, &nameInfo->Name, Operation, NULL, 0, &RuleCode, &Kill)) {
|
||||
SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
QueueRuleProcessTermination(Pid, Kill);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
}
|
||||
}
|
||||
|
||||
if (WildcardMatch(L"\\Device\\PhysicalDrive*", FileName.Buffer, FileName.Length) ||
|
||||
(WildcardMatch(L"\\Device\\Harddisk*", FileName.Buffer, FileName.Length) && !WildcardMatch(L"*Volume*", FileName.Buffer, FileName.Length))) {
|
||||
|
||||
if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &
|
||||
(FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE | WRITE_DAC | WRITE_OWNER)) {
|
||||
|
||||
if (!Trusted) {
|
||||
UNICODE_STRING MsgStr = RTL_CONSTANT_STRING(L"Disk_Wiper_Attempt");
|
||||
SendMessageToUser(4001, (ULONG)(ULONG_PTR)Pid, MsgStr.Buffer, MsgStr.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckProtectedPathRule(&FileName)) {
|
||||
if (!Trusted) {
|
||||
if ((Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &
|
||||
(FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | DELETE | WRITE_DAC | GENERIC_WRITE)) || IsCreateAction) {
|
||||
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckFileExtensionRule(&FileName)) {
|
||||
if (!Trusted) {
|
||||
if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess & (DELETE | FILE_DELETE_CHILD)) {
|
||||
if (CheckRansomActivity(Pid, &FileName, NULL, 0, FALSE)) {
|
||||
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, FileName.Buffer, FileName.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (nameInfo) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
}
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return callbackStatus;
|
||||
}
|
||||
|
||||
@@ -115,178 +52,92 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
FILE_INFORMATION_CLASS infoClass = Data->Iopb->Parameters.SetFileInformation.FileInformationClass;
|
||||
|
||||
if (infoClass != FileDispositionInformation &&
|
||||
infoClass != FileRenameInformation &&
|
||||
infoClass != FileRenameInformationEx) {
|
||||
if (infoClass != FileDispositionInformation && infoClass != FileRenameInformation && infoClass != FileRenameInformationEx) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
|
||||
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
|
||||
if (!NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) || !nameInfo) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
if (!NT_SUCCESS(FltParseFileNameInformation(nameInfo)) || !nameInfo->Name.Buffer) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
if (CheckProtectedPathRule(&nameInfo->Name)) {
|
||||
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (CheckFileExtensionRule(&nameInfo->Name)) {
|
||||
if (CheckRansomActivity(Pid, &nameInfo->Name, NULL, 0, FALSE)) {
|
||||
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (nameInfo) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
}
|
||||
return callbackStatus;
|
||||
}
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
ULONG Operation = 0;
|
||||
|
||||
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
|
||||
|
||||
SECURITY_INFORMATION SecurityInfo = Data->Iopb->Parameters.SetSecurity.SecurityInformation;
|
||||
|
||||
if (SecurityInfo & (OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION)) {
|
||||
if (CheckProtectedPathRule(&nameInfo->Name)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
if (infoClass == FileDispositionInformation) {
|
||||
Operation = OP_DELETE;
|
||||
}
|
||||
else {
|
||||
Operation = OP_RENAME;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (nameInfo) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
if (EvaluateFileRule(Pid, &nameInfo->Name, Operation, NULL, 0, &RuleCode, &Kill)) {
|
||||
SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
QueueRuleProcessTermination(Pid, Kill);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
}
|
||||
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return callbackStatus;
|
||||
}
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
if (Data->Iopb->IrpFlags & (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO | IRP_NOCACHE)) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
if (Data->Iopb->IrpFlags & (IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO | IRP_NOCACHE)) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
ULONG WriteLength = Data->Iopb->Parameters.Write.Length;
|
||||
if (WriteLength == 0) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
NTSTATUS status = FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo);
|
||||
|
||||
if (!NT_SUCCESS(status) || !nameInfo) goto cleanup;
|
||||
|
||||
status = FltParseFileNameInformation(nameInfo);
|
||||
if (!NT_SUCCESS(status) || !nameInfo->Name.Buffer) goto cleanup;
|
||||
|
||||
if (CheckFileExtensionRule(&nameInfo->Name)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
PVOID SystemBuffer = NULL;
|
||||
PMDL Mdl = Data->Iopb->Parameters.Write.MdlAddress;
|
||||
|
||||
if (!Mdl) {
|
||||
if (Data->Iopb->Parameters.Write.WriteBuffer && KeGetCurrentIrql() <= APC_LEVEL) {
|
||||
if (NT_SUCCESS(FltLockUserBuffer(Data))) {
|
||||
Mdl = Data->Iopb->Parameters.Write.MdlAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Mdl) {
|
||||
SystemBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority | MdlMappingNoExecute);
|
||||
}
|
||||
|
||||
if (CheckRansomActivity(Pid, &nameInfo->Name, SystemBuffer, WriteLength, TRUE)) {
|
||||
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (nameInfo) {
|
||||
if (!NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) || !nameInfo) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
if (!NT_SUCCESS(FltParseFileNameInformation(nameInfo)) || !nameInfo->Name.Buffer) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
PVOID SystemBuffer = NULL;
|
||||
PMDL Mdl = Data->Iopb->Parameters.Write.MdlAddress;
|
||||
if (!Mdl && Data->Iopb->Parameters.Write.WriteBuffer && KeGetCurrentIrql() <= APC_LEVEL) {
|
||||
if (NT_SUCCESS(FltLockUserBuffer(Data))) Mdl = Data->Iopb->Parameters.Write.MdlAddress;
|
||||
}
|
||||
if (Mdl) SystemBuffer = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority | MdlMappingNoExecute);
|
||||
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
if (EvaluateFileRule(Pid, &nameInfo->Name, OP_WRITE, SystemBuffer, WriteLength, &RuleCode, &Kill)) {
|
||||
SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
QueueRuleProcessTermination(Pid, Kill);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
}
|
||||
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return callbackStatus;
|
||||
}
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(Data);
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
if (Data->Iopb->Parameters.FileSystemControl.Common.FsControlCode != FSCTL_MANAGE_BYPASS_IO) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
PFS_BPIO_INPUT InputBuffer = (PFS_BPIO_INPUT)Data->Iopb->Parameters.FileSystemControl.Neither.InputBuffer;
|
||||
if (!InputBuffer) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
|
||||
__try {
|
||||
Operation = InputBuffer->Operation;
|
||||
}
|
||||
__except (EXCEPTION_EXECUTE_HANDLER) {
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
PFLT_FILE_NAME_INFORMATION nameInfo = NULL;
|
||||
FLT_PREOP_CALLBACK_STATUS callbackStatus = FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
if (Operation == FS_BPIO_OP_ENABLE) {
|
||||
if (NT_SUCCESS(FltGetFileNameInformation(Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &nameInfo)) && nameInfo) {
|
||||
if (NT_SUCCESS(FltParseFileNameInformation(nameInfo))) {
|
||||
if (CheckFileExtensionRule(&nameInfo->Name) || CheckProtectedPathRule(&nameInfo->Name)) {
|
||||
Data->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nameInfo) {
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
}
|
||||
return callbackStatus;
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext) {
|
||||
UNREFERENCED_PARAMETER(Data);
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
@@ -0,0 +1,818 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
extern "C" {
|
||||
NTSTATUS ZwOpenThread(
|
||||
PHANDLE ThreadHandle,
|
||||
ACCESS_MASK DesiredAccess,
|
||||
POBJECT_ATTRIBUTES ObjectAttributes,
|
||||
PCLIENT_ID ClientId
|
||||
);
|
||||
|
||||
NTSTATUS ZwQueryInformationThread(
|
||||
HANDLE ThreadHandle,
|
||||
THREADINFOCLASS ThreadInformationClass,
|
||||
PVOID ThreadInformation,
|
||||
ULONG ThreadInformationLength,
|
||||
PULONG ReturnLength
|
||||
);
|
||||
}
|
||||
|
||||
#ifndef THREAD_QUERY_INFORMATION
|
||||
#define THREAD_QUERY_INFORMATION 0x0040
|
||||
#endif
|
||||
|
||||
#define THREAD_QUERY_SET_WIN32_START_ADDRESS 9
|
||||
|
||||
constexpr ULONG PYAS_MEM_IMAGE_TYPE = 0x01000000UL;
|
||||
|
||||
typedef enum _MEMORY_WORK_TYPE {
|
||||
MemoryWorkThreadAnalysis = 1,
|
||||
MemoryWorkViolationReport = 2
|
||||
} MEMORY_WORK_TYPE;
|
||||
|
||||
typedef struct _MEMORY_WORK_ITEM {
|
||||
LIST_ENTRY Entry;
|
||||
MEMORY_WORK_TYPE Type;
|
||||
PEPROCESS SourceProcess;
|
||||
PEPROCESS TargetProcess;
|
||||
HANDLE SourcePid;
|
||||
HANDLE TargetPid;
|
||||
HANDLE ThreadId;
|
||||
ULONG RuleCode;
|
||||
BOOLEAN Kill;
|
||||
WCHAR FallbackMessage[64];
|
||||
} MEMORY_WORK_ITEM, * PMEMORY_WORK_ITEM;
|
||||
|
||||
typedef struct _THREAD_MEMORY_REGION {
|
||||
PVOID StartAddress;
|
||||
ULONG MemoryType;
|
||||
ULONG MemoryProtection;
|
||||
SIZE_T RegionSize;
|
||||
} THREAD_MEMORY_REGION, * PTHREAD_MEMORY_REGION;
|
||||
|
||||
static BOOLEAN g_ThreadNotifyRegistered = FALSE;
|
||||
static BOOLEAN g_LoadImageNotifyRegistered = FALSE;
|
||||
static BOOLEAN g_MemoryProtectionInitialized = FALSE;
|
||||
static BOOLEAN g_MemoryRundownCompleted = FALSE;
|
||||
static EX_RUNDOWN_REF g_MemoryRundown;
|
||||
static volatile LONG g_MemoryStopping = 1;
|
||||
static volatile LONG g_PendingMemoryWork = 0;
|
||||
static LIST_ENTRY g_MemoryWorkQueue;
|
||||
static KSPIN_LOCK g_MemoryWorkLock;
|
||||
static KSEMAPHORE g_MemoryWorkSemaphore;
|
||||
static HANDLE g_MemoryWorkerHandle = NULL;
|
||||
constexpr auto MAX_PENDING_MEMORY_WORK = 256;
|
||||
constexpr auto MEMORY_WORKER_STOP_TIMEOUT_MS = 2000;
|
||||
|
||||
static PACCESS_MASK GetDesiredAccessPointer(POB_PRE_OPERATION_INFORMATION OperationInformation) {
|
||||
if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) {
|
||||
return &OperationInformation->Parameters->CreateHandleInformation.DesiredAccess;
|
||||
}
|
||||
|
||||
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
|
||||
return &OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ULONG GetHandleType(POB_PRE_OPERATION_INFORMATION OperationInformation) {
|
||||
if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE) {
|
||||
return PYAS_HANDLE_CREATE;
|
||||
}
|
||||
|
||||
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
|
||||
return PYAS_HANDLE_DUPLICATE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ULONG GetProcessOperations(ACCESS_MASK Access) {
|
||||
ULONG Operations = 0;
|
||||
|
||||
if (Access & PROCESS_VM_READ) Operations |= OP_VM_READ;
|
||||
if (Access & PROCESS_VM_WRITE) Operations |= OP_VM_WRITE;
|
||||
if (Access & PROCESS_VM_OPERATION) Operations |= OP_VM_OPERATION;
|
||||
if (Access & PROCESS_CREATE_THREAD) Operations |= OP_CREATE_THREAD;
|
||||
if (Access & PROCESS_TERMINATE) Operations |= OP_TERMINATE;
|
||||
if (Access & PROCESS_SUSPEND_RESUME) Operations |= OP_SUSPEND_RESUME;
|
||||
if (Access & PROCESS_DUP_HANDLE) Operations |= OP_DUP_HANDLE;
|
||||
if (Access & (PROCESS_SET_INFORMATION | PROCESS_SET_QUOTA)) Operations |= OP_SET_INFORMATION;
|
||||
if (Access & PROCESS_CREATE_PROCESS) Operations |= OP_CREATE_PROCESS;
|
||||
|
||||
return Operations;
|
||||
}
|
||||
|
||||
static ULONG GetThreadOperations(ACCESS_MASK Access) {
|
||||
ULONG Operations = 0;
|
||||
|
||||
if (Access & THREAD_SET_CONTEXT) Operations |= OP_THREAD_SET_CONTEXT;
|
||||
if (Access & THREAD_TERMINATE) Operations |= OP_TERMINATE;
|
||||
if (Access & THREAD_SUSPEND_RESUME) Operations |= OP_SUSPEND_RESUME;
|
||||
if (Access & THREAD_SET_INFORMATION) Operations |= OP_SET_INFORMATION;
|
||||
if (Access & THREAD_SET_THREAD_TOKEN) Operations |= OP_THREAD_SET_TOKEN;
|
||||
if (Access & (THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION)) Operations |= OP_IMPERSONATE;
|
||||
|
||||
return Operations;
|
||||
}
|
||||
|
||||
static ACCESS_MASK GetProcessDeniedAccess(ULONG Operations) {
|
||||
ACCESS_MASK DeniedAccess = 0;
|
||||
|
||||
if (Operations & OP_VM_READ) DeniedAccess |= PROCESS_VM_READ;
|
||||
if (Operations & OP_VM_WRITE) DeniedAccess |= PROCESS_VM_WRITE;
|
||||
if (Operations & OP_VM_OPERATION) DeniedAccess |= PROCESS_VM_OPERATION;
|
||||
if (Operations & OP_CREATE_THREAD) DeniedAccess |= PROCESS_CREATE_THREAD;
|
||||
if (Operations & OP_TERMINATE) DeniedAccess |= PROCESS_TERMINATE;
|
||||
if (Operations & OP_SUSPEND_RESUME) DeniedAccess |= PROCESS_SUSPEND_RESUME;
|
||||
if (Operations & OP_DUP_HANDLE) DeniedAccess |= PROCESS_DUP_HANDLE;
|
||||
if (Operations & OP_SET_INFORMATION) DeniedAccess |= PROCESS_SET_INFORMATION | PROCESS_SET_QUOTA;
|
||||
if (Operations & OP_CREATE_PROCESS) DeniedAccess |= PROCESS_CREATE_PROCESS;
|
||||
|
||||
return DeniedAccess;
|
||||
}
|
||||
|
||||
static ACCESS_MASK GetThreadDeniedAccess(ULONG Operations) {
|
||||
ACCESS_MASK DeniedAccess = 0;
|
||||
|
||||
if (Operations & OP_THREAD_SET_CONTEXT) DeniedAccess |= THREAD_SET_CONTEXT;
|
||||
if (Operations & OP_TERMINATE) DeniedAccess |= THREAD_TERMINATE;
|
||||
if (Operations & OP_SUSPEND_RESUME) DeniedAccess |= THREAD_SUSPEND_RESUME;
|
||||
if (Operations & OP_SET_INFORMATION) DeniedAccess |= THREAD_SET_INFORMATION;
|
||||
if (Operations & OP_THREAD_SET_TOKEN) DeniedAccess |= THREAD_SET_THREAD_TOKEN;
|
||||
if (Operations & OP_IMPERSONATE) DeniedAccess |= THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
|
||||
|
||||
return DeniedAccess;
|
||||
}
|
||||
|
||||
static BOOLEAN IsProcessIdentityCurrent(HANDLE ProcessId, PEPROCESS ExpectedProcess);
|
||||
|
||||
static VOID ReleaseMemoryWork(PMEMORY_WORK_ITEM Work) {
|
||||
if (!Work) return;
|
||||
|
||||
if (Work->TargetProcess) ObDereferenceObject(Work->TargetProcess);
|
||||
if (Work->SourceProcess) ObDereferenceObject(Work->SourceProcess);
|
||||
InterlockedDecrement(&g_PendingMemoryWork);
|
||||
PyasFree(Work);
|
||||
}
|
||||
|
||||
static PMEMORY_WORK_ITEM PopMemoryWork() {
|
||||
PMEMORY_WORK_ITEM Work = NULL;
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&g_MemoryWorkLock, &OldIrql);
|
||||
|
||||
if (!IsListEmpty(&g_MemoryWorkQueue)) {
|
||||
PLIST_ENTRY Entry = RemoveHeadList(&g_MemoryWorkQueue);
|
||||
Work = CONTAINING_RECORD(Entry, MEMORY_WORK_ITEM, Entry);
|
||||
}
|
||||
|
||||
KeReleaseSpinLock(&g_MemoryWorkLock, OldIrql);
|
||||
return Work;
|
||||
}
|
||||
|
||||
static VOID CancelQueuedMemoryWork() {
|
||||
for (;;) {
|
||||
PMEMORY_WORK_ITEM Work = PopMemoryWork();
|
||||
if (!Work) return;
|
||||
ReleaseMemoryWork(Work);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOLEAN QueueMemoryWork(PMEMORY_WORK_ITEM Work) {
|
||||
if (!Work) return FALSE;
|
||||
|
||||
LONG Pending = InterlockedIncrement(&g_PendingMemoryWork);
|
||||
if (Pending > MAX_PENDING_MEMORY_WORK) {
|
||||
InterlockedDecrement(&g_PendingMemoryWork);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&g_MemoryWorkLock, &OldIrql);
|
||||
|
||||
if (InterlockedCompareExchange(&g_MemoryStopping, 0, 0) != 0) {
|
||||
KeReleaseSpinLock(&g_MemoryWorkLock, OldIrql);
|
||||
InterlockedDecrement(&g_PendingMemoryWork);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
InsertTailList(&g_MemoryWorkQueue, &Work->Entry);
|
||||
KeReleaseSpinLock(&g_MemoryWorkLock, OldIrql);
|
||||
KeReleaseSemaphore(&g_MemoryWorkSemaphore, IO_NO_INCREMENT, 1, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static VOID DeliverViolation(PMEMORY_WORK_ITEM Work) {
|
||||
if (!Work) return;
|
||||
if (InterlockedCompareExchange(&g_MemoryStopping, 0, 0) != 0) return;
|
||||
|
||||
BOOLEAN SourceMatches = IsProcessIdentityCurrent(Work->SourcePid, Work->SourceProcess);
|
||||
BOOLEAN TargetMatches = IsProcessIdentityCurrent(Work->TargetPid, Work->TargetProcess);
|
||||
if (!SourceMatches || !TargetMatches) return;
|
||||
|
||||
PUNICODE_STRING TargetPath = NULL;
|
||||
|
||||
if (NT_SUCCESS(GetProcessImageName(Work->TargetPid, &TargetPath)) && TargetPath && TargetPath->Buffer) {
|
||||
SendMessageToUser(
|
||||
Work->RuleCode,
|
||||
(ULONG)(ULONG_PTR)Work->SourcePid,
|
||||
TargetPath->Buffer,
|
||||
TargetPath->Length
|
||||
);
|
||||
}
|
||||
else {
|
||||
UNICODE_STRING MessageString;
|
||||
RtlInitUnicodeString(&MessageString, Work->FallbackMessage);
|
||||
SendMessageToUser(
|
||||
Work->RuleCode,
|
||||
(ULONG)(ULONG_PTR)Work->SourcePid,
|
||||
MessageString.Buffer,
|
||||
MessageString.Length
|
||||
);
|
||||
}
|
||||
|
||||
if (TargetPath) ExFreePool(TargetPath);
|
||||
QueueRuleProcessTermination(Work->SourcePid, Work->Kill);
|
||||
}
|
||||
|
||||
static VOID ReportViolation(
|
||||
ULONG RuleCode,
|
||||
BOOLEAN Kill,
|
||||
HANDLE SourcePid,
|
||||
HANDLE TargetPid,
|
||||
PCWSTR FallbackMessage
|
||||
) {
|
||||
if (!RuleCode || !SourcePid || !TargetPid || !FallbackMessage) return;
|
||||
if (InterlockedCompareExchange(&g_MemoryStopping, 0, 0) != 0) return;
|
||||
|
||||
PEPROCESS SourceProcess = NULL;
|
||||
PEPROCESS TargetProcess = NULL;
|
||||
|
||||
NTSTATUS Status = PsLookupProcessByProcessId(SourcePid, &SourceProcess);
|
||||
if (NT_SUCCESS(Status)) {
|
||||
Status = PsLookupProcessByProcessId(TargetPid, &TargetProcess);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(Status)) {
|
||||
if (SourceProcess) ObDereferenceObject(SourceProcess);
|
||||
return;
|
||||
}
|
||||
|
||||
PMEMORY_WORK_ITEM Work = (PMEMORY_WORK_ITEM)PyasAllocate(sizeof(MEMORY_WORK_ITEM));
|
||||
if (!Work) {
|
||||
ObDereferenceObject(TargetProcess);
|
||||
ObDereferenceObject(SourceProcess);
|
||||
return;
|
||||
}
|
||||
|
||||
RtlZeroMemory(Work, sizeof(*Work));
|
||||
Work->Type = MemoryWorkViolationReport;
|
||||
Work->SourceProcess = SourceProcess;
|
||||
Work->TargetProcess = TargetProcess;
|
||||
Work->SourcePid = SourcePid;
|
||||
Work->TargetPid = TargetPid;
|
||||
Work->RuleCode = RuleCode;
|
||||
Work->Kill = Kill;
|
||||
RtlStringCchCopyW(Work->FallbackMessage, RTL_NUMBER_OF(Work->FallbackMessage), FallbackMessage);
|
||||
|
||||
if (!QueueMemoryWork(Work)) {
|
||||
ObDereferenceObject(TargetProcess);
|
||||
ObDereferenceObject(SourceProcess);
|
||||
PyasFree(Work);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOLEAN IsExecuteProtection(ULONG Protect) {
|
||||
ULONG BaseProtect = Protect & 0xFF;
|
||||
|
||||
return BaseProtect == PAGE_EXECUTE ||
|
||||
BaseProtect == PAGE_EXECUTE_READ ||
|
||||
BaseProtect == PAGE_EXECUTE_READWRITE ||
|
||||
BaseProtect == PAGE_EXECUTE_WRITECOPY;
|
||||
}
|
||||
|
||||
static BOOLEAN IsExecuteWriteProtection(ULONG Protect) {
|
||||
ULONG BaseProtect = Protect & 0xFF;
|
||||
|
||||
return BaseProtect == PAGE_EXECUTE_READWRITE ||
|
||||
BaseProtect == PAGE_EXECUTE_WRITECOPY;
|
||||
}
|
||||
|
||||
static BOOLEAN QueryThreadMemoryRegionForProcess(
|
||||
PEPROCESS Process,
|
||||
PVOID Address,
|
||||
PTHREAD_MEMORY_REGION Region
|
||||
) {
|
||||
if (!Process || !Region || !Address || KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
|
||||
|
||||
RtlZeroMemory(Region, sizeof(*Region));
|
||||
|
||||
KAPC_STATE ApcState;
|
||||
KeStackAttachProcess(Process, &ApcState);
|
||||
|
||||
MEMORY_BASIC_INFORMATION MemoryInformation = { 0 };
|
||||
SIZE_T ReturnLength = 0;
|
||||
NTSTATUS Status = ZwQueryVirtualMemory(
|
||||
ZwCurrentProcess(),
|
||||
Address,
|
||||
MemoryBasicInformation,
|
||||
&MemoryInformation,
|
||||
sizeof(MemoryInformation),
|
||||
&ReturnLength
|
||||
);
|
||||
|
||||
KeUnstackDetachProcess(&ApcState);
|
||||
|
||||
if (!NT_SUCCESS(Status)) return FALSE;
|
||||
if (MemoryInformation.State != MEM_COMMIT) return FALSE;
|
||||
if (!IsExecuteProtection(MemoryInformation.Protect)) return FALSE;
|
||||
|
||||
Region->StartAddress = Address;
|
||||
Region->RegionSize = MemoryInformation.RegionSize;
|
||||
|
||||
if (MemoryInformation.Type == MEM_PRIVATE) Region->MemoryType = PYAS_MEMORY_PRIVATE;
|
||||
else if (MemoryInformation.Type == MEM_MAPPED) Region->MemoryType = PYAS_MEMORY_MAPPED;
|
||||
else if (MemoryInformation.Type == PYAS_MEM_IMAGE_TYPE) Region->MemoryType = PYAS_MEMORY_IMAGE;
|
||||
else return FALSE;
|
||||
|
||||
Region->MemoryProtection = PYAS_PROTECT_EXECUTE;
|
||||
if (IsExecuteWriteProtection(MemoryInformation.Protect)) {
|
||||
Region->MemoryProtection |= PYAS_PROTECT_EXECUTE_WRITE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOLEAN QueryThreadMemoryRegion(
|
||||
HANDLE ProcessId,
|
||||
PVOID Address,
|
||||
PTHREAD_MEMORY_REGION Region
|
||||
) {
|
||||
if (!ProcessId) return FALSE;
|
||||
|
||||
PEPROCESS Process = NULL;
|
||||
NTSTATUS Status = PsLookupProcessByProcessId(ProcessId, &Process);
|
||||
if (!NT_SUCCESS(Status)) return FALSE;
|
||||
|
||||
BOOLEAN Result = QueryThreadMemoryRegionForProcess(Process, Address, Region);
|
||||
ObDereferenceObject(Process);
|
||||
return Result;
|
||||
}
|
||||
|
||||
BOOLEAN IsAddressInUnmappedMemory(HANDLE ProcessId, PVOID Address) {
|
||||
THREAD_MEMORY_REGION Region = { 0 };
|
||||
if (!QueryThreadMemoryRegion(ProcessId, Address, &Region)) return FALSE;
|
||||
return Region.MemoryType == PYAS_MEMORY_PRIVATE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsProcessIdentityCurrent(HANDLE ProcessId, PEPROCESS ExpectedProcess) {
|
||||
if (!ProcessId || !ExpectedProcess) return FALSE;
|
||||
|
||||
PEPROCESS CurrentProcess = NULL;
|
||||
NTSTATUS Status = PsLookupProcessByProcessId(ProcessId, &CurrentProcess);
|
||||
if (!NT_SUCCESS(Status)) return FALSE;
|
||||
|
||||
BOOLEAN Matches = CurrentProcess == ExpectedProcess;
|
||||
ObDereferenceObject(CurrentProcess);
|
||||
return Matches;
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS ObjectPreCallbackCore(
|
||||
POB_PRE_OPERATION_INFORMATION OperationInformation
|
||||
) {
|
||||
if (!OperationInformation || OperationInformation->KernelHandle) {
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
PACCESS_MASK DesiredAccess = GetDesiredAccessPointer(OperationInformation);
|
||||
ULONG HandleType = GetHandleType(OperationInformation);
|
||||
|
||||
if (!DesiredAccess || *DesiredAccess == 0 || HandleType == 0) {
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
HANDLE SourcePid = PsGetCurrentProcessId();
|
||||
if (!SourcePid || IsProcessTrusted(SourcePid)) {
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
HANDLE TargetPid = NULL;
|
||||
ULONG ObjectType = 0;
|
||||
ULONG RequestedOperations = 0;
|
||||
ACCESS_MASK OriginalAccess = *DesiredAccess;
|
||||
|
||||
if (OperationInformation->ObjectType == *PsProcessType) {
|
||||
TargetPid = PsGetProcessId((PEPROCESS)OperationInformation->Object);
|
||||
ObjectType = PYAS_OBJECT_PROCESS;
|
||||
RequestedOperations = GetProcessOperations(OriginalAccess);
|
||||
}
|
||||
else if (OperationInformation->ObjectType == *PsThreadType) {
|
||||
TargetPid = PsGetThreadProcessId((PETHREAD)OperationInformation->Object);
|
||||
ObjectType = PYAS_OBJECT_THREAD;
|
||||
RequestedOperations = GetThreadOperations(OriginalAccess);
|
||||
}
|
||||
else {
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
if (!TargetPid || SourcePid == TargetPid || RequestedOperations == 0) {
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
ULONG DeniedOperations = 0;
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
|
||||
if (!EvaluateProcessAccessRule(
|
||||
SourcePid,
|
||||
TargetPid,
|
||||
RequestedOperations,
|
||||
HandleType,
|
||||
ObjectType,
|
||||
&DeniedOperations,
|
||||
&RuleCode,
|
||||
&Kill
|
||||
)) {
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
ACCESS_MASK DeniedAccess = ObjectType == PYAS_OBJECT_PROCESS
|
||||
? GetProcessDeniedAccess(DeniedOperations)
|
||||
: GetThreadDeniedAccess(DeniedOperations);
|
||||
|
||||
ACCESS_MASK EffectiveDeniedAccess = OriginalAccess & DeniedAccess;
|
||||
if (EffectiveDeniedAccess == 0) return OB_PREOP_SUCCESS;
|
||||
|
||||
*DesiredAccess = OriginalAccess & ~EffectiveDeniedAccess;
|
||||
|
||||
ReportViolation(
|
||||
RuleCode,
|
||||
Kill,
|
||||
SourcePid,
|
||||
TargetPid,
|
||||
ObjectType == PYAS_OBJECT_PROCESS
|
||||
? L"Process_Handle_Access_Denied"
|
||||
: L"Thread_Handle_Access_Denied"
|
||||
);
|
||||
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS ObjectPreCallback(
|
||||
PVOID RegistrationContext,
|
||||
POB_PRE_OPERATION_INFORMATION OperationInformation
|
||||
) {
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
|
||||
if (!g_MemoryProtectionInitialized) return OB_PREOP_SUCCESS;
|
||||
if (InterlockedCompareExchange(&g_MemoryStopping, 0, 0) != 0) return OB_PREOP_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_MemoryRundown)) return OB_PREOP_SUCCESS;
|
||||
|
||||
OB_PREOP_CALLBACK_STATUS Result = ObjectPreCallbackCore(OperationInformation);
|
||||
ExReleaseRundownProtection(&g_MemoryRundown);
|
||||
return Result;
|
||||
}
|
||||
|
||||
static VOID ProcessThreadAnalysis(PMEMORY_WORK_ITEM Work) {
|
||||
if (!Work) return;
|
||||
if (InterlockedCompareExchange(&g_MemoryStopping, 0, 0) != 0) return;
|
||||
|
||||
PVOID StartAddress = NULL;
|
||||
HANDLE ThreadHandle = NULL;
|
||||
BOOLEAN IdentityValid =
|
||||
IsProcessIdentityCurrent(Work->SourcePid, Work->SourceProcess) &&
|
||||
IsProcessIdentityCurrent(Work->TargetPid, Work->TargetProcess);
|
||||
CLIENT_ID ClientId = { Work->TargetPid, Work->ThreadId };
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
|
||||
|
||||
NTSTATUS Status = STATUS_PROCESS_IS_TERMINATING;
|
||||
if (IdentityValid) {
|
||||
Status = ZwOpenThread(
|
||||
&ThreadHandle,
|
||||
THREAD_QUERY_INFORMATION,
|
||||
&ObjectAttributes,
|
||||
&ClientId
|
||||
);
|
||||
}
|
||||
|
||||
if (NT_SUCCESS(Status)) {
|
||||
Status = ZwQueryInformationThread(
|
||||
ThreadHandle,
|
||||
(THREADINFOCLASS)THREAD_QUERY_SET_WIN32_START_ADDRESS,
|
||||
&StartAddress,
|
||||
sizeof(StartAddress),
|
||||
NULL
|
||||
);
|
||||
ZwClose(ThreadHandle);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(Status) || !StartAddress) return;
|
||||
if (InterlockedCompareExchange(&g_MemoryStopping, 0, 0) != 0) return;
|
||||
|
||||
THREAD_MEMORY_REGION Region = { 0 };
|
||||
if (!QueryThreadMemoryRegionForProcess(Work->TargetProcess, StartAddress, &Region)) return;
|
||||
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
if (!EvaluateThreadRule(
|
||||
Work->SourcePid,
|
||||
Work->TargetPid,
|
||||
StartAddress,
|
||||
Region.MemoryType,
|
||||
Region.MemoryProtection,
|
||||
Region.RegionSize,
|
||||
&RuleCode,
|
||||
&Kill
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Work->RuleCode = RuleCode;
|
||||
Work->Kill = Kill;
|
||||
RtlStringCchCopyW(
|
||||
Work->FallbackMessage,
|
||||
RTL_NUMBER_OF(Work->FallbackMessage),
|
||||
L"Remote_Executable_Thread_Detected"
|
||||
);
|
||||
DeliverViolation(Work);
|
||||
}
|
||||
|
||||
static VOID MemoryWorkerThread(PVOID Parameter) {
|
||||
UNREFERENCED_PARAMETER(Parameter);
|
||||
|
||||
for (;;) {
|
||||
KeWaitForSingleObject(
|
||||
&g_MemoryWorkSemaphore,
|
||||
Executive,
|
||||
KernelMode,
|
||||
FALSE,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (InterlockedCompareExchange(&g_MemoryStopping, 0, 0) != 0) {
|
||||
CancelQueuedMemoryWork();
|
||||
break;
|
||||
}
|
||||
|
||||
PMEMORY_WORK_ITEM Work = PopMemoryWork();
|
||||
if (!Work) continue;
|
||||
|
||||
if (Work->Type == MemoryWorkThreadAnalysis) {
|
||||
ProcessThreadAnalysis(Work);
|
||||
}
|
||||
else if (Work->Type == MemoryWorkViolationReport) {
|
||||
DeliverViolation(Work);
|
||||
}
|
||||
|
||||
ReleaseMemoryWork(Work);
|
||||
}
|
||||
|
||||
PsTerminateSystemThread(STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static VOID ThreadNotifyCallback(
|
||||
HANDLE ProcessId,
|
||||
HANDLE ThreadId,
|
||||
BOOLEAN IsCreated
|
||||
) {
|
||||
if (!IsCreated || !g_MemoryProtectionInitialized) return;
|
||||
if (InterlockedCompareExchange(&g_MemoryStopping, 0, 0) != 0) return;
|
||||
|
||||
HANDLE SourcePid = PsGetCurrentProcessId();
|
||||
if (!SourcePid || SourcePid == ProcessId || IsProcessTrusted(SourcePid)) return;
|
||||
|
||||
PEPROCESS SourceProcess = PsGetCurrentProcess();
|
||||
PEPROCESS TargetProcess = NULL;
|
||||
ObReferenceObject(SourceProcess);
|
||||
|
||||
NTSTATUS Status = PsLookupProcessByProcessId(ProcessId, &TargetProcess);
|
||||
if (!NT_SUCCESS(Status)) {
|
||||
ObDereferenceObject(SourceProcess);
|
||||
return;
|
||||
}
|
||||
|
||||
PMEMORY_WORK_ITEM Work = (PMEMORY_WORK_ITEM)PyasAllocate(sizeof(MEMORY_WORK_ITEM));
|
||||
if (!Work) {
|
||||
ObDereferenceObject(TargetProcess);
|
||||
ObDereferenceObject(SourceProcess);
|
||||
return;
|
||||
}
|
||||
|
||||
RtlZeroMemory(Work, sizeof(*Work));
|
||||
Work->Type = MemoryWorkThreadAnalysis;
|
||||
Work->SourceProcess = SourceProcess;
|
||||
Work->TargetProcess = TargetProcess;
|
||||
Work->SourcePid = SourcePid;
|
||||
Work->TargetPid = ProcessId;
|
||||
Work->ThreadId = ThreadId;
|
||||
|
||||
if (!QueueMemoryWork(Work)) {
|
||||
ObDereferenceObject(TargetProcess);
|
||||
ObDereferenceObject(SourceProcess);
|
||||
PyasFree(Work);
|
||||
}
|
||||
}
|
||||
|
||||
static VOID LoadImageNotifyCallback(
|
||||
PUNICODE_STRING FullImageName,
|
||||
HANDLE ProcessId,
|
||||
PIMAGE_INFO ImageInfo
|
||||
) {
|
||||
if (!g_MemoryProtectionInitialized) return;
|
||||
if (InterlockedCompareExchange(&g_MemoryStopping, 0, 0) != 0) return;
|
||||
if (!FullImageName || !FullImageName->Buffer || !ImageInfo || ImageInfo->SystemModeImage) return;
|
||||
if (!ProcessId || !ExAcquireRundownProtection(&g_MemoryRundown)) return;
|
||||
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
if (EvaluateImageLoadRule(ProcessId, FullImageName, ImageInfo, &RuleCode, &Kill)) {
|
||||
SendMessageToUser(
|
||||
RuleCode,
|
||||
(ULONG)(ULONG_PTR)ProcessId,
|
||||
FullImageName->Buffer,
|
||||
FullImageName->Length
|
||||
);
|
||||
QueueRuleProcessTermination(ProcessId, Kill);
|
||||
}
|
||||
|
||||
ExReleaseRundownProtection(&g_MemoryRundown);
|
||||
}
|
||||
|
||||
static NTSTATUS RemoveMemoryCallbacks() {
|
||||
NTSTATUS FirstFailure = STATUS_SUCCESS;
|
||||
|
||||
if (GlobalData.ObRegistrationHandle) {
|
||||
ObUnRegisterCallbacks(GlobalData.ObRegistrationHandle);
|
||||
GlobalData.ObRegistrationHandle = NULL;
|
||||
}
|
||||
|
||||
if (g_ThreadNotifyRegistered) {
|
||||
NTSTATUS Status = PsRemoveCreateThreadNotifyRoutine(ThreadNotifyCallback);
|
||||
if (NT_SUCCESS(Status) || Status == STATUS_PROCEDURE_NOT_FOUND) {
|
||||
g_ThreadNotifyRegistered = FALSE;
|
||||
}
|
||||
else if (NT_SUCCESS(FirstFailure)) {
|
||||
FirstFailure = Status;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_LoadImageNotifyRegistered) {
|
||||
NTSTATUS Status = PsRemoveLoadImageNotifyRoutine(LoadImageNotifyCallback);
|
||||
if (NT_SUCCESS(Status) || Status == STATUS_PROCEDURE_NOT_FOUND) {
|
||||
g_LoadImageNotifyRegistered = FALSE;
|
||||
}
|
||||
else if (NT_SUCCESS(FirstFailure)) {
|
||||
FirstFailure = Status;
|
||||
}
|
||||
}
|
||||
|
||||
return FirstFailure;
|
||||
}
|
||||
|
||||
static NTSTATUS StartMemoryWorker() {
|
||||
if (g_MemoryWorkerHandle) return STATUS_SUCCESS;
|
||||
|
||||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||||
InitializeObjectAttributes(
|
||||
&ObjectAttributes,
|
||||
NULL,
|
||||
OBJ_KERNEL_HANDLE,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
return PsCreateSystemThread(
|
||||
&g_MemoryWorkerHandle,
|
||||
SYNCHRONIZE,
|
||||
&ObjectAttributes,
|
||||
NULL,
|
||||
NULL,
|
||||
MemoryWorkerThread,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
static NTSTATUS StopMemoryWorker(BOOLEAN WaitWithoutTimeout) {
|
||||
CancelQueuedMemoryWork();
|
||||
|
||||
if (!g_MemoryWorkerHandle) return STATUS_SUCCESS;
|
||||
|
||||
KeReleaseSemaphore(&g_MemoryWorkSemaphore, IO_NO_INCREMENT, 1, FALSE);
|
||||
|
||||
LARGE_INTEGER Timeout;
|
||||
PLARGE_INTEGER TimeoutPointer = NULL;
|
||||
|
||||
if (!WaitWithoutTimeout) {
|
||||
Timeout.QuadPart = -((LONGLONG)MEMORY_WORKER_STOP_TIMEOUT_MS * 10 * 1000);
|
||||
TimeoutPointer = &Timeout;
|
||||
}
|
||||
|
||||
NTSTATUS Status = ZwWaitForSingleObject(
|
||||
g_MemoryWorkerHandle,
|
||||
FALSE,
|
||||
TimeoutPointer
|
||||
);
|
||||
|
||||
if (Status == STATUS_TIMEOUT) return STATUS_DEVICE_BUSY;
|
||||
if (!NT_SUCCESS(Status)) return Status;
|
||||
|
||||
ZwClose(g_MemoryWorkerHandle);
|
||||
g_MemoryWorkerHandle = NULL;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS InitializeMemoryProtection(PDRIVER_OBJECT DriverObject) {
|
||||
UNREFERENCED_PARAMETER(DriverObject);
|
||||
|
||||
if (g_MemoryProtectionInitialized) return STATUS_SUCCESS;
|
||||
|
||||
InitializeListHead(&g_MemoryWorkQueue);
|
||||
KeInitializeSpinLock(&g_MemoryWorkLock);
|
||||
KeInitializeSemaphore(&g_MemoryWorkSemaphore, 0, MAXLONG);
|
||||
ExInitializeRundownProtection(&g_MemoryRundown);
|
||||
g_MemoryRundownCompleted = FALSE;
|
||||
InterlockedExchange(&g_PendingMemoryWork, 0);
|
||||
InterlockedExchange(&g_MemoryStopping, 0);
|
||||
g_MemoryProtectionInitialized = TRUE;
|
||||
|
||||
NTSTATUS Status = StartMemoryWorker();
|
||||
if (!NT_SUCCESS(Status)) {
|
||||
InterlockedExchange(&g_MemoryStopping, 1);
|
||||
g_MemoryProtectionInitialized = FALSE;
|
||||
return Status;
|
||||
}
|
||||
|
||||
Status = PsSetCreateThreadNotifyRoutine(ThreadNotifyCallback);
|
||||
if (!NT_SUCCESS(Status)) {
|
||||
InterlockedExchange(&g_MemoryStopping, 1);
|
||||
StopMemoryWorker(TRUE);
|
||||
g_MemoryProtectionInitialized = FALSE;
|
||||
return Status;
|
||||
}
|
||||
g_ThreadNotifyRegistered = TRUE;
|
||||
|
||||
Status = PsSetLoadImageNotifyRoutine(LoadImageNotifyCallback);
|
||||
if (!NT_SUCCESS(Status)) {
|
||||
InterlockedExchange(&g_MemoryStopping, 1);
|
||||
RemoveMemoryCallbacks();
|
||||
ExWaitForRundownProtectionRelease(&g_MemoryRundown);
|
||||
g_MemoryRundownCompleted = TRUE;
|
||||
StopMemoryWorker(TRUE);
|
||||
g_MemoryProtectionInitialized = FALSE;
|
||||
return Status;
|
||||
}
|
||||
g_LoadImageNotifyRegistered = TRUE;
|
||||
|
||||
OB_OPERATION_REGISTRATION OperationRegistration[2] = { 0 };
|
||||
OperationRegistration[0].ObjectType = PsProcessType;
|
||||
OperationRegistration[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
OperationRegistration[0].PreOperation = ObjectPreCallback;
|
||||
|
||||
OperationRegistration[1].ObjectType = PsThreadType;
|
||||
OperationRegistration[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
OperationRegistration[1].PreOperation = ObjectPreCallback;
|
||||
|
||||
OB_CALLBACK_REGISTRATION CallbackRegistration = { 0 };
|
||||
CallbackRegistration.Version = OB_FLT_REGISTRATION_VERSION;
|
||||
CallbackRegistration.OperationRegistrationCount = RTL_NUMBER_OF(OperationRegistration);
|
||||
CallbackRegistration.OperationRegistration = OperationRegistration;
|
||||
RtlInitUnicodeString(&CallbackRegistration.Altitude, PYAS_OB_ALTITUDE);
|
||||
|
||||
Status = ObRegisterCallbacks(&CallbackRegistration, &GlobalData.ObRegistrationHandle);
|
||||
if (NT_SUCCESS(Status)) return STATUS_SUCCESS;
|
||||
|
||||
InterlockedExchange(&g_MemoryStopping, 1);
|
||||
RemoveMemoryCallbacks();
|
||||
ExWaitForRundownProtectionRelease(&g_MemoryRundown);
|
||||
g_MemoryRundownCompleted = TRUE;
|
||||
StopMemoryWorker(TRUE);
|
||||
g_MemoryProtectionInitialized = FALSE;
|
||||
return Status;
|
||||
}
|
||||
|
||||
NTSTATUS UninitializeMemoryProtection(BOOLEAN WaitWithoutTimeout) {
|
||||
if (!g_MemoryProtectionInitialized) return STATUS_SUCCESS;
|
||||
|
||||
InterlockedExchange(&g_MemoryStopping, 1);
|
||||
|
||||
NTSTATUS CallbackStatus = RemoveMemoryCallbacks();
|
||||
|
||||
if (!g_MemoryRundownCompleted) {
|
||||
ExWaitForRundownProtectionRelease(&g_MemoryRundown);
|
||||
g_MemoryRundownCompleted = TRUE;
|
||||
}
|
||||
|
||||
NTSTATUS WorkerStatus = StopMemoryWorker(WaitWithoutTimeout);
|
||||
if (!NT_SUCCESS(CallbackStatus)) return CallbackStatus;
|
||||
if (!NT_SUCCESS(WorkerStatus)) return WorkerStatus;
|
||||
|
||||
if (g_ThreadNotifyRegistered || g_LoadImageNotifyRegistered || GlobalData.ObRegistrationHandle) {
|
||||
return STATUS_DEVICE_BUSY;
|
||||
}
|
||||
|
||||
g_MemoryProtectionInitialized = FALSE;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
+262
-203
@@ -1,226 +1,285 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
static PVOID ObRegistrationHandle = NULL;
|
||||
static OB_CALLBACK_REGISTRATION ObRegistration;
|
||||
static OB_OPERATION_REGISTRATION ObCallbacks[2];
|
||||
constexpr auto PROCESS_RELATION_BUCKET_COUNT = 1024;
|
||||
constexpr auto PROCESS_RELATION_BUCKET_WAYS = 4;
|
||||
|
||||
static BOOLEAN IsSystemImage(PUNICODE_STRING FullImageName) {
|
||||
if (!FullImageName || !FullImageName->Buffer) return FALSE;
|
||||
typedef HANDLE(NTAPI* PPS_GET_PROCESS_INHERITED_FROM_UNIQUE_PROCESS_ID)(PEPROCESS Process);
|
||||
|
||||
if (WildcardMatch(L"*\\Windows\\System32\\*", FullImageName->Buffer, FullImageName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\SysWOW64\\*", FullImageName->Buffer, FullImageName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\WinSxS\\*", FullImageName->Buffer, FullImageName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\Microsoft.NET\\*", FullImageName->Buffer, FullImageName->Length) ||
|
||||
WildcardMatch(L"*\\Common Files\\Microsoft Shared\\*", FullImageName->Buffer, FullImageName->Length) ||
|
||||
WildcardMatch(L"*\\Program Files*\\*", FullImageName->Buffer, FullImageName->Length)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
typedef struct _PROCESS_RELATION_ENTRY {
|
||||
HANDLE ProcessId;
|
||||
HANDLE ParentProcessId;
|
||||
HANDLE CreatorProcessId;
|
||||
LARGE_INTEGER CreateTime;
|
||||
LARGE_INTEGER ParentCreateTime;
|
||||
ULONGLONG Sequence;
|
||||
} PROCESS_RELATION_ENTRY, * PPROCESS_RELATION_ENTRY;
|
||||
|
||||
static PROCESS_RELATION_ENTRY g_ProcessRelations[PROCESS_RELATION_BUCKET_COUNT][PROCESS_RELATION_BUCKET_WAYS];
|
||||
static KSPIN_LOCK g_ProcessRelationLock;
|
||||
static EX_RUNDOWN_REF g_ProcessRundown;
|
||||
static volatile LONG g_ProcessStopping = 1;
|
||||
static volatile LONG64 g_ProcessRelationSequence = 0;
|
||||
static BOOLEAN g_ProcessProtectionInitialized = FALSE;
|
||||
static PPS_GET_PROCESS_INHERITED_FROM_UNIQUE_PROCESS_ID g_GetInheritedProcessId = NULL;
|
||||
|
||||
static ULONG GetProcessRelationBucket(HANDLE ProcessId) {
|
||||
return ((ULONG)(ULONG_PTR)ProcessId >> 2) & (PROCESS_RELATION_BUCKET_COUNT - 1);
|
||||
}
|
||||
|
||||
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo) {
|
||||
UNREFERENCED_PARAMETER(ImageInfo);
|
||||
static BOOLEAN QueryProcessCreateTime(HANDLE ProcessId, PLARGE_INTEGER CreateTime) {
|
||||
if (CreateTime) CreateTime->QuadPart = 0;
|
||||
if (!ProcessId || !CreateTime || KeGetCurrentIrql() > APC_LEVEL) return FALSE;
|
||||
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return;
|
||||
if (!FullImageName || !FullImageName->Buffer) return;
|
||||
if (ProcessId == (HANDLE)0) return;
|
||||
|
||||
if (IsSystemImage(FullImageName)) return;
|
||||
|
||||
if (IsTargetProtected(ProcessId)) {
|
||||
if (CheckFileExtensionRule(FullImageName)) {
|
||||
SendMessageToUser(6001, (ULONG)(ULONG_PTR)ProcessId, FullImageName->Buffer, FullImageName->Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BOOLEAN IsCriticalSystemProcess(HANDLE ProcessId) {
|
||||
if (ProcessId == (HANDLE)4) return TRUE;
|
||||
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
|
||||
|
||||
NTSTATUS status;
|
||||
BOOLEAN isCritical = FALSE;
|
||||
PEPROCESS Process = NULL;
|
||||
PUNICODE_STRING imageFileName = NULL;
|
||||
NTSTATUS Status = PsLookupProcessByProcessId(ProcessId, &Process);
|
||||
if (!NT_SUCCESS(Status)) return FALSE;
|
||||
|
||||
status = PsLookupProcessByProcessId(ProcessId, &Process);
|
||||
if (!NT_SUCCESS(status)) return FALSE;
|
||||
CreateTime->QuadPart = PsGetProcessCreateTimeQuadPart(Process);
|
||||
ObDereferenceObject(Process);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
status = SeLocateProcessImageName(Process, &imageFileName);
|
||||
static VOID StoreProcessRelation(
|
||||
PEPROCESS Process,
|
||||
HANDLE ProcessId,
|
||||
HANDLE ParentProcessId,
|
||||
HANDLE CreatorProcessId,
|
||||
PLARGE_INTEGER KnownParentCreateTime
|
||||
) {
|
||||
if (!Process || !ProcessId) return;
|
||||
|
||||
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
|
||||
if (WildcardMatch(L"*\\Windows\\System32\\csrss.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\System32\\lsass.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\System32\\services.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\System32\\wininit.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*\\Windows\\System32\\smss.exe", imageFileName->Buffer, imageFileName->Length)) {
|
||||
isCritical = TRUE;
|
||||
}
|
||||
ExFreePool(imageFileName);
|
||||
PROCESS_RELATION_ENTRY NewEntry = { 0 };
|
||||
NewEntry.ProcessId = ProcessId;
|
||||
NewEntry.ParentProcessId = ParentProcessId;
|
||||
NewEntry.CreatorProcessId = CreatorProcessId;
|
||||
NewEntry.CreateTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process);
|
||||
|
||||
if (KnownParentCreateTime) {
|
||||
NewEntry.ParentCreateTime = *KnownParentCreateTime;
|
||||
}
|
||||
else if (ParentProcessId) {
|
||||
QueryProcessCreateTime(ParentProcessId, &NewEntry.ParentCreateTime);
|
||||
}
|
||||
|
||||
NewEntry.Sequence = (ULONGLONG)InterlockedIncrement64(&g_ProcessRelationSequence);
|
||||
|
||||
ULONG Bucket = GetProcessRelationBucket(ProcessId);
|
||||
ULONG Selected = 0;
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&g_ProcessRelationLock, &OldIrql);
|
||||
|
||||
for (ULONG Index = 0; Index < PROCESS_RELATION_BUCKET_WAYS; Index++) {
|
||||
PPROCESS_RELATION_ENTRY Entry = &g_ProcessRelations[Bucket][Index];
|
||||
|
||||
if (Entry->ProcessId == ProcessId || Entry->ProcessId == NULL) {
|
||||
Selected = Index;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Entry->Sequence < g_ProcessRelations[Bucket][Selected].Sequence) {
|
||||
Selected = Index;
|
||||
}
|
||||
}
|
||||
|
||||
g_ProcessRelations[Bucket][Selected] = NewEntry;
|
||||
KeReleaseSpinLock(&g_ProcessRelationLock, OldIrql);
|
||||
}
|
||||
|
||||
static VOID RemoveProcessRelation(HANDLE ProcessId) {
|
||||
if (!ProcessId) return;
|
||||
|
||||
ULONG Bucket = GetProcessRelationBucket(ProcessId);
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&g_ProcessRelationLock, &OldIrql);
|
||||
|
||||
for (ULONG Index = 0; Index < PROCESS_RELATION_BUCKET_WAYS; Index++) {
|
||||
PPROCESS_RELATION_ENTRY Entry = &g_ProcessRelations[Bucket][Index];
|
||||
if (Entry->ProcessId == ProcessId) {
|
||||
RtlZeroMemory(Entry, sizeof(*Entry));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
KeReleaseSpinLock(&g_ProcessRelationLock, OldIrql);
|
||||
}
|
||||
|
||||
BOOLEAN GetProcessRelation(
|
||||
HANDLE ProcessId,
|
||||
PHANDLE ParentProcessId,
|
||||
PHANDLE CreatorProcessId
|
||||
) {
|
||||
if (ParentProcessId) *ParentProcessId = NULL;
|
||||
if (CreatorProcessId) *CreatorProcessId = NULL;
|
||||
if (!ProcessId || !g_ProcessProtectionInitialized || KeGetCurrentIrql() > APC_LEVEL) return FALSE;
|
||||
|
||||
PEPROCESS Process = NULL;
|
||||
NTSTATUS Status = PsLookupProcessByProcessId(ProcessId, &Process);
|
||||
if (!NT_SUCCESS(Status)) return FALSE;
|
||||
|
||||
LARGE_INTEGER CurrentCreateTime = { 0 };
|
||||
CurrentCreateTime.QuadPart = PsGetProcessCreateTimeQuadPart(Process);
|
||||
|
||||
PROCESS_RELATION_ENTRY Snapshot = { 0 };
|
||||
ULONG Bucket = GetProcessRelationBucket(ProcessId);
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&g_ProcessRelationLock, &OldIrql);
|
||||
|
||||
for (ULONG Index = 0; Index < PROCESS_RELATION_BUCKET_WAYS; Index++) {
|
||||
PPROCESS_RELATION_ENTRY Entry = &g_ProcessRelations[Bucket][Index];
|
||||
if (Entry->ProcessId == ProcessId) {
|
||||
Snapshot = *Entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
KeReleaseSpinLock(&g_ProcessRelationLock, OldIrql);
|
||||
|
||||
BOOLEAN CacheValid = Snapshot.ProcessId == ProcessId &&
|
||||
Snapshot.CreateTime.QuadPart == CurrentCreateTime.QuadPart;
|
||||
|
||||
if (CacheValid && Snapshot.ParentProcessId && Snapshot.ParentCreateTime.QuadPart != 0) {
|
||||
LARGE_INTEGER LiveParentCreateTime = { 0 };
|
||||
if (QueryProcessCreateTime(Snapshot.ParentProcessId, &LiveParentCreateTime) &&
|
||||
LiveParentCreateTime.QuadPart != Snapshot.ParentCreateTime.QuadPart) {
|
||||
CacheValid = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (CacheValid) {
|
||||
if (ParentProcessId) *ParentProcessId = Snapshot.ParentProcessId;
|
||||
if (CreatorProcessId) *CreatorProcessId = Snapshot.CreatorProcessId;
|
||||
ObDereferenceObject(Process);
|
||||
return Snapshot.ParentProcessId != NULL || Snapshot.CreatorProcessId != NULL;
|
||||
}
|
||||
|
||||
if (Snapshot.ProcessId == ProcessId) {
|
||||
RemoveProcessRelation(ProcessId);
|
||||
}
|
||||
|
||||
HANDLE LiveParentProcessId = NULL;
|
||||
LARGE_INTEGER LiveParentCreateTime = { 0 };
|
||||
|
||||
if (g_GetInheritedProcessId) {
|
||||
LiveParentProcessId = g_GetInheritedProcessId(Process);
|
||||
}
|
||||
|
||||
if (!LiveParentProcessId || LiveParentProcessId == ProcessId) {
|
||||
ObDereferenceObject(Process);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!QueryProcessCreateTime(LiveParentProcessId, &LiveParentCreateTime) ||
|
||||
LiveParentCreateTime.QuadPart > CurrentCreateTime.QuadPart) {
|
||||
ObDereferenceObject(Process);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
StoreProcessRelation(
|
||||
Process,
|
||||
ProcessId,
|
||||
LiveParentProcessId,
|
||||
NULL,
|
||||
&LiveParentCreateTime
|
||||
);
|
||||
|
||||
if (ParentProcessId) *ParentProcessId = LiveParentProcessId;
|
||||
if (CreatorProcessId) *CreatorProcessId = NULL;
|
||||
|
||||
ObDereferenceObject(Process);
|
||||
return isCritical;
|
||||
}
|
||||
|
||||
static BOOLEAN IsBlacklistedAdminTool(HANDLE ProcessId) {
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
|
||||
|
||||
NTSTATUS status;
|
||||
BOOLEAN isBlacklisted = FALSE;
|
||||
PEPROCESS Process = NULL;
|
||||
PUNICODE_STRING imageFileName = NULL;
|
||||
|
||||
status = PsLookupProcessByProcessId(ProcessId, &Process);
|
||||
if (!NT_SUCCESS(status)) return FALSE;
|
||||
|
||||
status = SeLocateProcessImageName(Process, &imageFileName);
|
||||
|
||||
if (NT_SUCCESS(status) && imageFileName) {
|
||||
if (imageFileName->Buffer) {
|
||||
if (WildcardMatch(L"*Taskmgr.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*taskkill.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*ProcessHacker.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*procmon.exe", imageFileName->Buffer, imageFileName->Length)) {
|
||||
isBlacklisted = TRUE;
|
||||
}
|
||||
}
|
||||
ExFreePool(imageFileName);
|
||||
}
|
||||
|
||||
ObDereferenceObject(Process);
|
||||
return isBlacklisted;
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS PreOpenProcess(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) {
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
|
||||
if (OperationInformation->KernelHandle) return OB_PREOP_SUCCESS;
|
||||
|
||||
PEPROCESS TargetProcess = (PEPROCESS)OperationInformation->Object;
|
||||
if (!TargetProcess) return OB_PREOP_SUCCESS;
|
||||
|
||||
HANDLE TargetPid = PsGetProcessId(TargetProcess);
|
||||
HANDLE SourcePid = PsGetCurrentProcessId();
|
||||
|
||||
if (SourcePid == TargetPid) return OB_PREOP_SUCCESS;
|
||||
if ((ULONG)(ULONG_PTR)SourcePid == GlobalData.PyasPid) return OB_PREOP_SUCCESS;
|
||||
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
|
||||
|
||||
if (IsTargetProtected(TargetPid)) {
|
||||
if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS;
|
||||
|
||||
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
|
||||
|
||||
if (bIsTrusted) {
|
||||
if (IsBlacklistedAdminTool(SourcePid)) {
|
||||
bIsTrusted = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsTrusted) return OB_PREOP_SUCCESS;
|
||||
|
||||
ACCESS_MASK DenyMask = PROCESS_TERMINATE |
|
||||
PROCESS_VM_OPERATION |
|
||||
PROCESS_VM_WRITE |
|
||||
PROCESS_CREATE_THREAD |
|
||||
PROCESS_VM_READ |
|
||||
PROCESS_DUP_HANDLE |
|
||||
PROCESS_SUSPEND_RESUME |
|
||||
PROCESS_SET_INFORMATION |
|
||||
PROCESS_SET_QUOTA;
|
||||
|
||||
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
|
||||
|
||||
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
|
||||
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
|
||||
}
|
||||
}
|
||||
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation) {
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
|
||||
if (OperationInformation->KernelHandle) return OB_PREOP_SUCCESS;
|
||||
|
||||
PETHREAD TargetThread = (PETHREAD)OperationInformation->Object;
|
||||
if (!TargetThread) return OB_PREOP_SUCCESS;
|
||||
|
||||
PEPROCESS TargetProcess = IoThreadToProcess(TargetThread);
|
||||
if (!TargetProcess) return OB_PREOP_SUCCESS;
|
||||
|
||||
HANDLE TargetPid = PsGetProcessId(TargetProcess);
|
||||
HANDLE SourcePid = PsGetCurrentProcessId();
|
||||
|
||||
if (SourcePid == TargetPid) return OB_PREOP_SUCCESS;
|
||||
if ((ULONG)(ULONG_PTR)SourcePid == GlobalData.PyasPid) return OB_PREOP_SUCCESS;
|
||||
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
|
||||
|
||||
if (IsTargetProtected(TargetPid)) {
|
||||
if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS;
|
||||
|
||||
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
|
||||
|
||||
if (bIsTrusted) {
|
||||
if (IsBlacklistedAdminTool(SourcePid)) {
|
||||
bIsTrusted = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsTrusted) return OB_PREOP_SUCCESS;
|
||||
|
||||
ACCESS_MASK DenyMask = THREAD_TERMINATE |
|
||||
THREAD_SUSPEND_RESUME |
|
||||
THREAD_SET_CONTEXT |
|
||||
THREAD_SET_INFORMATION |
|
||||
THREAD_SET_THREAD_TOKEN;
|
||||
|
||||
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
|
||||
|
||||
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
|
||||
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
|
||||
}
|
||||
}
|
||||
|
||||
return OB_PREOP_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
NTSTATUS InitializeProcessProtection() {
|
||||
static UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"320000.PYAS.Ob");
|
||||
if (g_ProcessProtectionInitialized) return STATUS_SUCCESS;
|
||||
|
||||
ObCallbacks[0].ObjectType = PsProcessType;
|
||||
ObCallbacks[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
ObCallbacks[0].PreOperation = PreOpenProcess;
|
||||
ObCallbacks[0].PostOperation = NULL;
|
||||
KeInitializeSpinLock(&g_ProcessRelationLock);
|
||||
RtlZeroMemory(g_ProcessRelations, sizeof(g_ProcessRelations));
|
||||
InterlockedExchange64(&g_ProcessRelationSequence, 0);
|
||||
ExInitializeRundownProtection(&g_ProcessRundown);
|
||||
|
||||
ObCallbacks[1].ObjectType = PsThreadType;
|
||||
ObCallbacks[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
ObCallbacks[1].PreOperation = PreOpenThread;
|
||||
ObCallbacks[1].PostOperation = NULL;
|
||||
UNICODE_STRING RoutineName = RTL_CONSTANT_STRING(L"PsGetProcessInheritedFromUniqueProcessId");
|
||||
g_GetInheritedProcessId = reinterpret_cast<PPS_GET_PROCESS_INHERITED_FROM_UNIQUE_PROCESS_ID>(
|
||||
MmGetSystemRoutineAddress(&RoutineName)
|
||||
);
|
||||
|
||||
ObRegistration.Version = OB_FLT_REGISTRATION_VERSION;
|
||||
ObRegistration.OperationRegistrationCount = 2;
|
||||
ObRegistration.Altitude = Altitude;
|
||||
ObRegistration.RegistrationContext = NULL;
|
||||
ObRegistration.OperationRegistration = ObCallbacks;
|
||||
|
||||
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;
|
||||
InterlockedExchange(&g_ProcessStopping, 0);
|
||||
g_ProcessProtectionInitialized = TRUE;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID UninitializeProcessProtection() {
|
||||
if (ObRegistrationHandle) {
|
||||
ObUnRegisterCallbacks(ObRegistrationHandle);
|
||||
ObRegistrationHandle = NULL;
|
||||
NTSTATUS UninitializeProcessProtection() {
|
||||
if (!g_ProcessProtectionInitialized) return STATUS_SUCCESS;
|
||||
|
||||
InterlockedExchange(&g_ProcessStopping, 1);
|
||||
ExWaitForRundownProtectionRelease(&g_ProcessRundown);
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&g_ProcessRelationLock, &OldIrql);
|
||||
RtlZeroMemory(g_ProcessRelations, sizeof(g_ProcessRelations));
|
||||
KeReleaseSpinLock(&g_ProcessRelationLock, OldIrql);
|
||||
|
||||
g_GetInheritedProcessId = NULL;
|
||||
g_ProcessProtectionInitialized = FALSE;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID ProcessNotifyCallbackEx(
|
||||
PEPROCESS Process,
|
||||
HANDLE ProcessId,
|
||||
PPS_CREATE_NOTIFY_INFO CreateInfo
|
||||
) {
|
||||
if (!g_ProcessProtectionInitialized) return;
|
||||
if (InterlockedCompareExchange(&g_ProcessStopping, 0, 0) != 0) return;
|
||||
if (!ExAcquireRundownProtection(&g_ProcessRundown)) return;
|
||||
|
||||
if (!CreateInfo) {
|
||||
RemoveProcessRelation(ProcessId);
|
||||
ExReleaseRundownProtection(&g_ProcessRundown);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(CreateInfo->CreationStatus)) {
|
||||
ExReleaseRundownProtection(&g_ProcessRundown);
|
||||
return;
|
||||
}
|
||||
|
||||
HANDLE CreatorPid = CreateInfo->CreatingThreadId.UniqueProcess;
|
||||
HANDLE ParentPid = CreateInfo->ParentProcessId;
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
|
||||
BOOLEAN Blocked = EvaluateProcessCreateRule(
|
||||
CreatorPid,
|
||||
ParentPid,
|
||||
ProcessId,
|
||||
CreateInfo->ImageFileName,
|
||||
CreateInfo->CommandLine,
|
||||
CreateInfo->FileOpenNameAvailable ? TRUE : FALSE,
|
||||
CreateInfo->IsSubsystemProcess ? TRUE : FALSE,
|
||||
&RuleCode,
|
||||
&Kill
|
||||
);
|
||||
|
||||
if (Blocked) {
|
||||
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
|
||||
|
||||
if (CreateInfo->ImageFileName && CreateInfo->ImageFileName->Buffer) {
|
||||
SendMessageToUser(
|
||||
RuleCode,
|
||||
(ULONG)(ULONG_PTR)CreatorPid,
|
||||
CreateInfo->ImageFileName->Buffer,
|
||||
CreateInfo->ImageFileName->Length
|
||||
);
|
||||
}
|
||||
|
||||
QueueRuleProcessTermination(CreatorPid, Kill);
|
||||
}
|
||||
else {
|
||||
StoreProcessRelation(Process, ProcessId, ParentPid, CreatorPid, NULL);
|
||||
}
|
||||
|
||||
ExReleaseRundownProtection(&g_ProcessRundown);
|
||||
}
|
||||
@@ -2,14 +2,6 @@
|
||||
|
||||
static LARGE_INTEGER Cookie;
|
||||
|
||||
static BOOLEAN IsBamRegistryPath(PCUNICODE_STRING KeyName) {
|
||||
if (!KeyName || !KeyName->Buffer) return FALSE;
|
||||
if (WildcardMatch(L"*\\Services\\bam\\*", KeyName->Buffer, KeyName->Length)) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsWriteAccess(ACCESS_MASK Access) {
|
||||
if (Access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK |
|
||||
DELETE | WRITE_DAC | WRITE_OWNER | GENERIC_WRITE)) {
|
||||
@@ -106,12 +98,13 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
|
||||
UNICODE_STRING FullPath;
|
||||
RtlInitUnicodeString(&FullPath, FullPathBuffer);
|
||||
|
||||
if (CheckRegistryRule(&FullPath)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
if (EvaluateRegistryRule(Pid, &FullPath, OP_CREATE | OP_WRITE, &RuleCode, &Kill)) {
|
||||
SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
QueueRuleProcessTermination(Pid, Kill);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
PyasFree(FullPathBuffer);
|
||||
}
|
||||
@@ -124,12 +117,13 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
|
||||
|
||||
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
|
||||
if (KeyName) {
|
||||
if (CheckRegistryRule(KeyName)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, KeyName->Buffer, KeyName->Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
if (EvaluateRegistryRule(Pid, KeyName, OP_DELETE, &RuleCode, &Kill)) {
|
||||
SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, KeyName->Buffer, KeyName->Length);
|
||||
QueueRuleProcessTermination(Pid, Kill);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
CmCallbackReleaseKeyObjectIDEx(KeyName);
|
||||
}
|
||||
@@ -142,44 +136,41 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
|
||||
PCUNICODE_STRING KeyName = NULL;
|
||||
|
||||
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
|
||||
if (KeyName) {
|
||||
if (KeyName->Buffer) {
|
||||
if (!IsBamRegistryPath(KeyName)) {
|
||||
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
|
||||
(Info->ValueName ? Info->ValueName->Length : 0) +
|
||||
sizeof(WCHAR);
|
||||
if (KeyName && KeyName->Buffer) {
|
||||
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
|
||||
(Info->ValueName ? Info->ValueName->Length : 0) +
|
||||
sizeof(WCHAR);
|
||||
|
||||
if (FullSize <= 0xFFFF) {
|
||||
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
|
||||
if (Buffer) {
|
||||
UNICODE_STRING FullPath;
|
||||
FullPath.Buffer = Buffer;
|
||||
FullPath.Length = 0;
|
||||
FullPath.MaximumLength = (USHORT)FullSize;
|
||||
if (FullSize <= 0xFFFF) {
|
||||
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
|
||||
if (Buffer) {
|
||||
UNICODE_STRING FullPath;
|
||||
FullPath.Buffer = Buffer;
|
||||
FullPath.Length = 0;
|
||||
FullPath.MaximumLength = (USHORT)FullSize;
|
||||
|
||||
RtlCopyUnicodeString(&FullPath, KeyName);
|
||||
RtlCopyUnicodeString(&FullPath, KeyName);
|
||||
|
||||
if (Info->ValueName && Info->ValueName->Length > 0) {
|
||||
if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') {
|
||||
RtlAppendUnicodeToString(&FullPath, L"\\");
|
||||
}
|
||||
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
|
||||
}
|
||||
|
||||
if (CheckRegistryRule(&FullPath)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
PyasFree(Buffer);
|
||||
if (Info->ValueName && Info->ValueName->Length > 0) {
|
||||
if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') {
|
||||
RtlAppendUnicodeToString(&FullPath, L"\\");
|
||||
}
|
||||
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
|
||||
}
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
if (EvaluateRegistryRule(Pid, &FullPath, OP_WRITE, &RuleCode, &Kill)) {
|
||||
SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
QueueRuleProcessTermination(Pid, Kill);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
PyasFree(Buffer);
|
||||
}
|
||||
}
|
||||
CmCallbackReleaseKeyObjectIDEx(KeyName);
|
||||
}
|
||||
if (KeyName) CmCallbackReleaseKeyObjectIDEx(KeyName);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -189,44 +180,41 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
|
||||
PCUNICODE_STRING KeyName = NULL;
|
||||
|
||||
if (NT_SUCCESS(CmCallbackGetKeyObjectIDEx(&Cookie, Info->Object, NULL, &KeyName, 0))) {
|
||||
if (KeyName) {
|
||||
if (KeyName->Buffer) {
|
||||
if (!IsBamRegistryPath(KeyName)) {
|
||||
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
|
||||
(Info->ValueName ? Info->ValueName->Length : 0) +
|
||||
sizeof(WCHAR);
|
||||
if (KeyName && KeyName->Buffer) {
|
||||
ULONG FullSize = KeyName->Length + sizeof(WCHAR) +
|
||||
(Info->ValueName ? Info->ValueName->Length : 0) +
|
||||
sizeof(WCHAR);
|
||||
|
||||
if (FullSize <= 0xFFFF) {
|
||||
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
|
||||
if (Buffer) {
|
||||
UNICODE_STRING FullPath;
|
||||
FullPath.Buffer = Buffer;
|
||||
FullPath.Length = 0;
|
||||
FullPath.MaximumLength = (USHORT)FullSize;
|
||||
if (FullSize <= 0xFFFF) {
|
||||
PWCHAR Buffer = (PWCHAR)PyasAllocate(FullSize);
|
||||
if (Buffer) {
|
||||
UNICODE_STRING FullPath;
|
||||
FullPath.Buffer = Buffer;
|
||||
FullPath.Length = 0;
|
||||
FullPath.MaximumLength = (USHORT)FullSize;
|
||||
|
||||
RtlCopyUnicodeString(&FullPath, KeyName);
|
||||
RtlCopyUnicodeString(&FullPath, KeyName);
|
||||
|
||||
if (Info->ValueName && Info->ValueName->Length > 0) {
|
||||
if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') {
|
||||
RtlAppendUnicodeToString(&FullPath, L"\\");
|
||||
}
|
||||
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
|
||||
}
|
||||
|
||||
if (CheckRegistryRule(&FullPath)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
PyasFree(Buffer);
|
||||
if (Info->ValueName && Info->ValueName->Length > 0) {
|
||||
if (FullPath.Length > 0 && FullPath.Buffer[(FullPath.Length / sizeof(WCHAR)) - 1] != L'\\') {
|
||||
RtlAppendUnicodeToString(&FullPath, L"\\");
|
||||
}
|
||||
RtlAppendUnicodeStringToString(&FullPath, Info->ValueName);
|
||||
}
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
ULONG RuleCode = 0;
|
||||
BOOLEAN Kill = FALSE;
|
||||
if (EvaluateRegistryRule(Pid, &FullPath, OP_DELETE, &RuleCode, &Kill)) {
|
||||
SendMessageToUser(RuleCode, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
QueueRuleProcessTermination(Pid, Kill);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
PyasFree(Buffer);
|
||||
}
|
||||
}
|
||||
CmCallbackReleaseKeyObjectIDEx(KeyName);
|
||||
}
|
||||
if (KeyName) CmCallbackReleaseKeyObjectIDEx(KeyName);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
@@ -235,20 +223,20 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
|
||||
}
|
||||
|
||||
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject) {
|
||||
static UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"320000.PYAS.Cm");
|
||||
NTSTATUS status = CmRegisterCallbackEx(RegistryCallback, &Altitude, DriverObject, NULL, &Cookie, NULL);
|
||||
if (Cookie.QuadPart != 0) return STATUS_SUCCESS;
|
||||
|
||||
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;
|
||||
UNICODE_STRING Altitude;
|
||||
RtlInitUnicodeString(&Altitude, L"320000.4201");
|
||||
return CmRegisterCallbackEx(RegistryCallback, &Altitude, DriverObject, NULL, &Cookie, NULL);
|
||||
}
|
||||
|
||||
VOID UninitializeRegistryProtection() {
|
||||
if (Cookie.QuadPart != 0) {
|
||||
CmUnRegisterCallback(Cookie);
|
||||
NTSTATUS UninitializeRegistryProtection() {
|
||||
if (Cookie.QuadPart == 0) return STATUS_SUCCESS;
|
||||
|
||||
NTSTATUS Status = CmUnRegisterCallback(Cookie);
|
||||
if (NT_SUCCESS(Status)) {
|
||||
Cookie.QuadPart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
+2124
-932
File diff suppressed because it is too large.
Load diff
+478
-227
@@ -1,228 +1,479 @@
|
||||
{
|
||||
"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",
|
||||
"*\\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\\*",
|
||||
],
|
||||
"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",
|
||||
]
|
||||
}
|
||||
"DynamicRules": [
|
||||
{
|
||||
"Name": "Process_Protect_Antivirus_Command",
|
||||
"Code": 1001,
|
||||
"Kill": false,
|
||||
"Category": "Process",
|
||||
"Initiator": [
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\cmd.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\WindowsPowerShell\\*\\powershell.exe",
|
||||
"\\Device\\HarddiskVolume*\\Program Files\\PowerShell\\*\\pwsh.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\taskkill.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\taskmgr.exe",
|
||||
],
|
||||
"InitiatorExclude": [
|
||||
"*\\PYAS.exe",
|
||||
],
|
||||
"CommandLine": [
|
||||
"*taskkill*PYAS.exe*",
|
||||
"*taskkill*msedgewebview2.exe*",
|
||||
],
|
||||
"Operations": [
|
||||
"Execute",
|
||||
],
|
||||
"Target": [
|
||||
"*\\taskkill.exe",
|
||||
"*\\cmd.exe",
|
||||
"*\\powershell.exe",
|
||||
"*\\pwsh.exe",
|
||||
"*\\wmic.exe",
|
||||
"*\\net.exe",
|
||||
"*\\net1.exe",
|
||||
],
|
||||
"Priority": 110,
|
||||
"FileOpenNameAvailable": true,
|
||||
"SubsystemProcess": false,
|
||||
},
|
||||
{
|
||||
"Name": "Process_Protect_PYAS_Handle",
|
||||
"Code": 1002,
|
||||
"Kill": false,
|
||||
"Category": "Process",
|
||||
"InitiatorExclude": [
|
||||
"*\\PYAS.exe",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Local\\Temp\\*\\PYAS_Setup.tmp",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\explorer.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\csrss.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\lsass.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\services.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\svchost.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\SystemApps\\*\\SearchHost.exe",
|
||||
],
|
||||
"Target": [
|
||||
"*\\PYAS.exe",
|
||||
"*\\msedgewebview2.exe",
|
||||
],
|
||||
"TargetProcessTree": [
|
||||
"*\\PYAS.exe",
|
||||
],
|
||||
"Operations": [
|
||||
"Terminate",
|
||||
"SuspendResume",
|
||||
"DuplicateHandle",
|
||||
"CreateProcess",
|
||||
"SetThreadToken",
|
||||
"Impersonate",
|
||||
],
|
||||
"Priority": 130,
|
||||
"InitiatorProcessTreeExclude": [
|
||||
"*\\PYAS.exe",
|
||||
],
|
||||
"HandleTypes": [
|
||||
"Create",
|
||||
"Duplicate",
|
||||
],
|
||||
"OperationMatch": "Any",
|
||||
"MinimumRiskScore": 20,
|
||||
"ObjectTypes": [
|
||||
"Process",
|
||||
"Thread",
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "File_Protect_Antivirus",
|
||||
"Code": 2001,
|
||||
"Kill": false,
|
||||
"Category": "File",
|
||||
"InitiatorExclude": [
|
||||
"*\\PYAS.exe",
|
||||
"*\\PYAS_Setup.exe",
|
||||
"*\\unins000.exe",
|
||||
"*\\notepad*.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\cleanmgr.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\explorer.exe",
|
||||
],
|
||||
"Target": [
|
||||
"*\\PYAS.exe",
|
||||
"*\\ProgramData\\PYAS\\*.json",
|
||||
"*\\Engine\\Heuristic\\*.yar",
|
||||
"*\\Engine\\Heuristic\\*.yara",
|
||||
"*\\Engine\\Heuristic\\*.yc",
|
||||
"*\\Engine\\Heuristic\\*.yrc",
|
||||
"*\\Engine\\Heuristic\\*.ip",
|
||||
"*\\Engine\\Heuristic\\*.txt",
|
||||
"*\\Engine\\Pattern\\*.onnx",
|
||||
"*\\Engine\\Properties\\*.json",
|
||||
"*\\Engine\\Properties\\*.onnx",
|
||||
"*\\Interface\\static\\css\\*.css",
|
||||
"*\\Interface\\static\\img\\*.ico",
|
||||
"*\\Interface\\static\\js\\*.js",
|
||||
"*\\Interface\\templates\\*.html",
|
||||
"*\\Plugins\\Filter\\PYAS_Driver.sys",
|
||||
"*\\Plugins\\Rules\\*.json",
|
||||
],
|
||||
"Operations": [
|
||||
"Write",
|
||||
"Delete",
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "File_Protect_System",
|
||||
"Code": 2002,
|
||||
"Kill": true,
|
||||
"Category": "File",
|
||||
"InitiatorExclude": [
|
||||
"*\\PYAS.exe",
|
||||
"*\\Huorong\\Sysdiag\\bin\\sysclean.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\SearchIndexer.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\lsass.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\svchost.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\services.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\taskhostw.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\TrustedInstaller.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\WinSxS\\*\\TiWorker.exe",
|
||||
],
|
||||
"Target": [
|
||||
"\\Device\\HarddiskVolume*\\bootmgr",
|
||||
"\\Device\\HarddiskVolume*\\boot.ini",
|
||||
"\\Device\\HarddiskVolume*\\BOOTNXT",
|
||||
"\\Device\\HarddiskVolume*\\EFI\\*",
|
||||
"\\Device\\HarddiskVolume*\\Boot\\*",
|
||||
"\\Device\\HarddiskVolume*\\Recovery\\*",
|
||||
],
|
||||
"Operations": [
|
||||
"Write",
|
||||
"Delete",
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "File_Protect_System_Exploit",
|
||||
"Code": 2003,
|
||||
"Kill": true,
|
||||
"Category": "File",
|
||||
"InitiatorExclude": [
|
||||
"*\\PYAS.exe",
|
||||
],
|
||||
"Target": [
|
||||
"*\\CON",
|
||||
"*\\CON\\*",
|
||||
"*\\PRN",
|
||||
"*\\PRN\\*",
|
||||
"*\\AUX",
|
||||
"*\\AUX\\*",
|
||||
"*\\NUL",
|
||||
"*\\NUL\\*",
|
||||
"*\\COM?",
|
||||
"*\\COM?\\*",
|
||||
"*\\LPT?",
|
||||
"*\\LPT?\\*",
|
||||
"*\\OSDATA",
|
||||
"*\\OSDATA\\*",
|
||||
"*\\*<*",
|
||||
"*\\*>*",
|
||||
],
|
||||
"Operations": [
|
||||
"Create",
|
||||
"Write",
|
||||
"Rename",
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "File_Protect_System_Ransom",
|
||||
"Code": 2004,
|
||||
"Kill": true,
|
||||
"Category": "File",
|
||||
"Initiator": [
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\cmd.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\WindowsPowerShell\\*\\powershell.exe",
|
||||
"\\Device\\HarddiskVolume*\\Program Files\\PowerShell\\*\\pwsh.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\wscript.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\cscript.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\mshta.exe",
|
||||
],
|
||||
"Target": [
|
||||
"\\Device\\HarddiskVolume*\\Windows\\*",
|
||||
],
|
||||
"TargetExclude": [
|
||||
"\\Device\\HarddiskVolume*\\Windows\\Temp\\*",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\SystemTemp\\*",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\Prefetch\\*",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\Logs\\*",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\WinSxS\\*",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\servicing\\*",
|
||||
],
|
||||
"Operations": [
|
||||
"Delete",
|
||||
"Rename",
|
||||
],
|
||||
"Threshold": 50,
|
||||
"TimeWindow": 3000,
|
||||
},
|
||||
{
|
||||
"Name": "File_Protect_Ransom",
|
||||
"Code": 2005,
|
||||
"Kill": true,
|
||||
"Category": "File",
|
||||
"InitiatorExclude": [
|
||||
"\\Device\\HarddiskVolume*\\Windows\\*",
|
||||
"\\Device\\HarddiskVolume*\\Program Files\\*",
|
||||
"\\Device\\HarddiskVolume*\\Program Files (x86)\\*",
|
||||
"*\\Google\\Chrome\\Application\\chrome.exe",
|
||||
"*\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
"*\\Microsoft\\Edge*\\Application\\*\\msedgewebview2.exe",
|
||||
"*\\Microsoft\\Edge*\\Install\\*\\setup.exe",
|
||||
"*\\Microsoft\\Edge*\\Application\\*\\Installer\\setup.exe",
|
||||
"*\\Mozilla Firefox\\firefox.exe",
|
||||
"*\\Thorium\\Application\\thorium.exe",
|
||||
"*\\Steam\\steam.exe",
|
||||
"*\\steam\\*\\steam.exe",
|
||||
"*\\Steam\\steamapps\\common\\*",
|
||||
"*\\Battle.net\\*\\Battle.net.exe",
|
||||
"*\\Epic Games\\Launcher\\Portal\\Binaries\\*\\EpicGamesLauncher.exe",
|
||||
"*\\PC4399_WPFLauncher.exe",
|
||||
"*\\Neverness To Everness\\NTELauncher.exe",
|
||||
"*\\ThunderEv.exe",
|
||||
"*\\ThunderStart.exe",
|
||||
"*\\bin\\APlayer.exe",
|
||||
"*\\bilibili\\bilibili.exe",
|
||||
"*\\KuGou.exe",
|
||||
"*\\XLGame.exe",
|
||||
"*\\XLLiveUD.exe",
|
||||
"*\\hrkill-*.exe",
|
||||
"*\\Huorong\\Sysdiag\\bin\\HipsDaemon.exe",
|
||||
"*\\360Safe.exe",
|
||||
"*\\PYAS.exe",
|
||||
"*\\WPS Office\\ksolaunch.exe",
|
||||
"*\\WPS Office\\*\\wps.exe",
|
||||
"*\\Application\\AndrowsLauncher.exe",
|
||||
"*\\resources\\bin\\SDK\\DownloadSDKServer.exe",
|
||||
"*\\bin\\*\\codex.exe",
|
||||
"*\\bin\\codex.exe",
|
||||
],
|
||||
"Target": [
|
||||
"\\Device\\HarddiskVolume*\\Users\\*",
|
||||
],
|
||||
"TargetExclude": [
|
||||
"*\\Documents\\My Games\\*",
|
||||
"*\\Documents\\Tencent Files\\*",
|
||||
"*\\Documents\\WeChat Files\\*",
|
||||
"*\\Documents\\WPS Cloud Files\\*",
|
||||
"*\\Users\\*\\steam\\*",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\User Data\\*",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\*",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\AppData\\Local\\*",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\Temp\\*",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\Local\\Temp\\*",
|
||||
"\\Device\\HarddiskVolume*\\Users\\*\\cache\\*",
|
||||
"*\\ventoy\\Boot\\*",
|
||||
"*\\AdminRegionConfig.*",
|
||||
"*\\nt_qq\\nt_*\\*",
|
||||
],
|
||||
"Extensions": [
|
||||
".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",
|
||||
".apk",
|
||||
".jar",
|
||||
".class",
|
||||
".db",
|
||||
".sqlite",
|
||||
],
|
||||
"Operations": [
|
||||
"Write",
|
||||
"Delete",
|
||||
"Rename",
|
||||
],
|
||||
"Threshold": 5,
|
||||
"TimeWindow": 3000,
|
||||
},
|
||||
{
|
||||
"Name": "Device_Protect_Disk_Wiper",
|
||||
"Code": 2006,
|
||||
"Kill": false,
|
||||
"Category": "Device",
|
||||
"InitiatorExclude": [
|
||||
"*\\PYAS.exe",
|
||||
"*\\rufus-*.exe",
|
||||
"*\\DiskInfo*.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\diskpart.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\diskmgmt.msc",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\taskhostw.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\vds.exe",
|
||||
"\\Device\\HarddiskVolume*\\Windows\\System32\\format.com",
|
||||
],
|
||||
"Operations": [
|
||||
"Ioctl",
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "Registry_Protect_System",
|
||||
"Code": 3001,
|
||||
"Kill": true,
|
||||
"Category": "Registry",
|
||||
"InitiatorExclude": [
|
||||
"\\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\\*",
|
||||
"*\\Google\\Chrome\\Application\\chrome.exe",
|
||||
"*\\Microsoft\\Edge\\Application\\msedge.exe",
|
||||
"*\\Mozilla Firefox\\firefox.exe",
|
||||
"*\\Thorium\\Application\\thorium.exe",
|
||||
"*\\Sources\\SetupHost.exe",
|
||||
"*\\Work\\*\\DismHost.exe",
|
||||
"*\\Steam\\steam.exe",
|
||||
"*\\steam\\*\\steam.exe",
|
||||
"*\\Steam\\steamapps\\common\\*",
|
||||
"*\\Battle.net\\*\\Battle.net.exe",
|
||||
"*\\Epic Games\\Launcher\\Portal\\Binaries\\*\\EpicGamesLauncher.exe",
|
||||
"*\\PC4399_WPFLauncher.exe",
|
||||
"*\\Neverness To Everness\\NTELauncher.exe",
|
||||
"*\\ThunderEv.exe",
|
||||
"*\\ThunderStart.exe",
|
||||
"*\\bin\\APlayer.exe",
|
||||
"*\\bilibili\\bilibili.exe",
|
||||
"*\\KuGou.exe",
|
||||
"*\\XLGame.exe",
|
||||
"*\\XLLiveUD.exe",
|
||||
"*\\hrkill-*.exe",
|
||||
"*\\Huorong\\Sysdiag\\bin\\HipsDaemon.exe",
|
||||
"*\\360Safe.exe",
|
||||
"*\\PYAS.exe",
|
||||
"*\\WPS Office\\ksolaunch.exe",
|
||||
"*\\WPS Office\\*\\wps.exe",
|
||||
"*\\WPS Office\\*\\*.exe",
|
||||
"*\\Application\\AndrowsLauncher.exe",
|
||||
"*\\resources\\bin\\SDK\\DownloadSDKServer.exe",
|
||||
"*\\bin\\*\\codex.exe",
|
||||
"*\\bin\\codex.exe",
|
||||
],
|
||||
"Target": [
|
||||
"\\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",
|
||||
],
|
||||
"Operations": [
|
||||
"Write",
|
||||
"Delete",
|
||||
"Create",
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user