Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4803b89294 | ||
|
|
e48adbd87d | ||
|
|
227314d83c | ||
|
|
3c0e21eb02 | ||
|
|
86d6153ef1 | ||
|
|
78cf8bded3 | ||
|
|
26d12181cd | ||
|
|
8d184b011c | ||
|
|
ed3a1444ec | ||
|
|
d3e70a47cd | ||
|
|
3171151219 | ||
|
|
e407867635 | ||
|
|
6d9cfab55f | ||
|
|
f0bce029f0 | ||
|
|
d47960a0cf | ||
|
|
8416018afe | ||
|
|
e93fc70169 | ||
|
|
5b3474c781 | ||
|
|
8d2a9f3b2b | ||
|
|
73d09f9b09 | ||
|
|
4e7c6efa2a | ||
|
|
81a884bc61 |
No files matched your search
+526
-318
File diff suppressed because it is too large.
Load diff
+47
-25
@@ -3,6 +3,7 @@ import pandas as pd
|
||||
import numpy as np
|
||||
import lightgbm as lgb
|
||||
import onnxmltools
|
||||
|
||||
from onnxmltools.convert.common.data_types import FloatTensorType
|
||||
from sklearn.model_selection import train_test_split
|
||||
from sklearn.metrics import classification_report, accuracy_score, roc_auc_score
|
||||
@@ -10,8 +11,8 @@ from sklearn.metrics import classification_report, accuracy_score, roc_auc_score
|
||||
####################################################################################################
|
||||
|
||||
DB_PATH = "pe_features.db"
|
||||
MODEL_FILE = "pe_malware_lgbm.txt"
|
||||
ONNX_FILE = "pe_malware.onnx"
|
||||
MODEL_FILE = "model.txt"
|
||||
ONNX_FILE = "model.onnx"
|
||||
FEATURE_FILE = "features.json"
|
||||
TEST_SIZE = 0.0001
|
||||
RANDOM_SEED = 42
|
||||
@@ -20,7 +21,7 @@ LGBM_PARAMS = {
|
||||
'objective': 'binary',
|
||||
'metric': ['binary_logloss', 'auc'],
|
||||
'boosting_type': 'gbdt',
|
||||
'num_leaves': 63,
|
||||
'num_leaves': 31,
|
||||
'learning_rate': 0.05,
|
||||
'feature_fraction': 0.8,
|
||||
'bagging_fraction': 0.8,
|
||||
@@ -29,13 +30,16 @@ LGBM_PARAMS = {
|
||||
'seed': RANDOM_SEED,
|
||||
'n_jobs': -1,
|
||||
'max_depth': -1,
|
||||
'min_data_in_leaf': 20,
|
||||
'min_data_in_leaf': 10,
|
||||
'lambda_l1': 0.1,
|
||||
'lambda_l2': 0.1
|
||||
'lambda_l2': 0.1,
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def clean_col_name(name):
|
||||
return re.sub(r'[^A-Za-z0-9_]+', '', name)
|
||||
|
||||
def get_raw_schema(db_path):
|
||||
if not os.path.exists(db_path):
|
||||
return None
|
||||
@@ -44,38 +48,53 @@ def get_raw_schema(db_path):
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("PRAGMA table_info(PeData)")
|
||||
columns = [row[1] for row in cursor.fetchall()]
|
||||
exclude_lower = {'label', 'id', 'filename', 'filelength', 'rowid', 'probability', 'predictedlabel', 'score'}
|
||||
return [c for c in columns if c.lower().strip() not in exclude_lower]
|
||||
except:
|
||||
exclude = {'label', 'id', 'filename', 'filelength', 'rowid', 'probability', 'predictedlabel', 'score', 'filehash', 'timedatestamp'}
|
||||
return [c for c in columns if c.lower().strip() not in exclude]
|
||||
except Exception as e:
|
||||
print(f"[-] Schema extraction error: {e}")
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
def load_and_clean_data(db_path, raw_cols):
|
||||
####################################################################################################
|
||||
|
||||
def load_data(db_path, raw_cols):
|
||||
print(f"[*] Connecting to database: {db_path}")
|
||||
conn = sqlite3.connect(db_path)
|
||||
req_cols = list(set(raw_cols + ['Label']))
|
||||
sql = f"SELECT {', '.join(req_cols)} FROM PeData"
|
||||
|
||||
try:
|
||||
df = pd.read_sql_query(sql, conn)
|
||||
if df.empty: return None, None, None
|
||||
chunks = []
|
||||
for chunk in pd.read_sql_query(sql, conn, chunksize=20000):
|
||||
label_col = next((c for c in chunk.columns if c.lower().strip() == 'label'), None)
|
||||
if not label_col:
|
||||
print("[-] Critical Error: Label column not found.")
|
||||
return None, None, None
|
||||
|
||||
label_col = next((c for c in df.columns if c.lower().strip() == 'label'), None)
|
||||
if not label_col:
|
||||
print("[-] Critical Error: Label column not found.")
|
||||
cols_to_cast = [c for c in chunk.columns if c != label_col]
|
||||
chunk[cols_to_cast] = chunk[cols_to_cast].astype(np.float32)
|
||||
chunk[label_col] = chunk[label_col].astype(np.int8)
|
||||
chunks.append(chunk)
|
||||
|
||||
if not chunks:
|
||||
return None, None, None
|
||||
|
||||
df = pd.concat(chunks, ignore_index=True)
|
||||
|
||||
label_col = next((c for c in df.columns if c.lower().strip() == 'label'), None)
|
||||
y = df[label_col].astype(int)
|
||||
X = df.drop(columns=[label_col], errors='ignore')
|
||||
|
||||
valid_features = [c for c in raw_cols if c in X.columns]
|
||||
X = X[valid_features].copy()
|
||||
|
||||
X = X.rename(columns=lambda x: re.sub('[^A-Za-z0-9_]+', '', x))
|
||||
rename_map = {old: clean_col_name(old) for old in X.columns}
|
||||
X = X.rename(columns=rename_map)
|
||||
|
||||
if X.isnull().values.any():
|
||||
X = X.fillna(X.median(numeric_only=True))
|
||||
if X.columns.duplicated().any():
|
||||
print("[-] Warning: Duplicate column names detected after cleaning. Deduplicating...")
|
||||
X = X.loc[:, ~X.columns.duplicated()]
|
||||
|
||||
final_features = X.columns.tolist()
|
||||
return X, y, final_features
|
||||
@@ -95,13 +114,11 @@ def train_process(X, y):
|
||||
n_neg = (y_train == 0).sum()
|
||||
|
||||
base_weight = n_neg / n_pos if n_pos > 0 else 1.0
|
||||
|
||||
weight_ratio_base = 1.0
|
||||
weight_ratio_target = 0.1
|
||||
final_pos_weight = base_weight * (weight_ratio_target / weight_ratio_base)
|
||||
weight_ratio_target = 0.01
|
||||
final_pos_weight = base_weight * weight_ratio_target
|
||||
|
||||
print(f"[*] Balance Report: Safe={n_neg}, Malware={n_pos}")
|
||||
print(f"[*] Weight Config: Base Balanced={base_weight:.4f}, Final Adjusted (Favor Safe)={final_pos_weight:.4f}")
|
||||
print(f"[*] Weight Config: Base Balanced={base_weight:.4f}, Final Adjusted={final_pos_weight:.4f}")
|
||||
|
||||
train_data = lgb.Dataset(X_train, label=y_train)
|
||||
valid_data = lgb.Dataset(X_test, label=y_test, reference=train_data)
|
||||
@@ -109,7 +126,7 @@ def train_process(X, y):
|
||||
params = LGBM_PARAMS.copy()
|
||||
params['scale_pos_weight'] = final_pos_weight
|
||||
|
||||
print("\n[*] Starting LightGBM training (Full Force, No Early Stopping)...")
|
||||
print("\n[*] Starting LightGBM training...")
|
||||
start_time = time.time()
|
||||
|
||||
model = lgb.train(
|
||||
@@ -127,6 +144,8 @@ def train_process(X, y):
|
||||
|
||||
return model, X_test, y_test
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def export_onnx_model(model, feature_names):
|
||||
print(f"[*] Exporting ONNX model to {ONNX_FILE}...")
|
||||
try:
|
||||
@@ -141,6 +160,8 @@ def export_onnx_model(model, feature_names):
|
||||
except Exception as e:
|
||||
print(f"[-] ONNX export failed: {e}")
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def evaluate_and_save(model, X_test, y_test):
|
||||
print("\n------------------- Evaluation Report -------------------\n")
|
||||
y_prob = model.predict(X_test, num_iteration=model.best_iteration)
|
||||
@@ -171,7 +192,7 @@ def evaluate_and_save(model, X_test, y_test):
|
||||
####################################################################################################
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n---------------- PE Malware Trainer v3.0 ----------------\n")
|
||||
print("\n---------------- PE Malware Trainer v3.1 ----------------\n")
|
||||
|
||||
if not os.path.exists(DB_PATH):
|
||||
print(f"[-] Error: Database {DB_PATH} not found.")
|
||||
@@ -182,7 +203,7 @@ if __name__ == "__main__":
|
||||
print("[-] Error: Could not read schema.")
|
||||
sys.exit(1)
|
||||
|
||||
X, y, _ = load_and_clean_data(DB_PATH, raw_schema)
|
||||
X, y, _ = load_data(DB_PATH, raw_schema)
|
||||
if X is None:
|
||||
sys.exit(1)
|
||||
|
||||
@@ -193,3 +214,4 @@ if __name__ == "__main__":
|
||||
print("\n\n[-] Training interrupted.")
|
||||
except Exception as e:
|
||||
print(f"\n[-] Critical Error: {e}")
|
||||
|
||||
@@ -104,6 +104,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.setAttribute(Qt.WA_TranslucentBackground)
|
||||
self.setWindowFlags(Qt.FramelessWindowHint)
|
||||
self.init_environ()
|
||||
self.init_clean_logs()
|
||||
self.init_variable()
|
||||
self.load_config()
|
||||
self.init_interface()
|
||||
@@ -117,6 +118,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.ui.setupUi(self)
|
||||
self.python = sys.executable
|
||||
self.process_timer = QTimer(self)
|
||||
self.lock_mutex = threading.Lock()
|
||||
|
||||
self.sign = sign_scanner()
|
||||
self.sign.init_windll(["wintrust"])
|
||||
@@ -133,6 +135,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
self.driver_port = None
|
||||
self.scan_running = False
|
||||
self.scan_finished = False
|
||||
self.virus_lock = {}
|
||||
self.virus_results = []
|
||||
self.scan_count = 0
|
||||
@@ -144,12 +147,14 @@ class MainWindow_Controller(QMainWindow):
|
||||
]
|
||||
self.lang_names = [
|
||||
"traditional_switch", "simplified_switch", "english_switch",
|
||||
"japanese_switch", "korean_switch", "french_switch", "spanish_switch",
|
||||
"hindi_switch", "arabic_switch", "russian_switch", "slovenian_switch",
|
||||
]
|
||||
self.pyas_default = {
|
||||
"version": "3.4.0",
|
||||
"version": "3.4.2",
|
||||
"api_host": "https://pyas-security.com/",
|
||||
"api_key": "fBRZxYS1UxykM-qzNOlKOEl63WILzlvgNMn6QfsG6FXCAAIktCrOPTAfY5_hEyuZ",
|
||||
"suffix": [".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".mui", ".cpl"],
|
||||
"suffix": [".exe", ".dll", ".sys", ".ocx", ".scr", ".efi", ".acm", ".ax", ".cpl", ".drv", ".com", ".mui", ".pyd"],
|
||||
"block": [2001, 3001, 4001, 5001, 6001],
|
||||
"size": 256 * 1024 * 1024,
|
||||
"language": "english_switch",
|
||||
@@ -209,6 +214,8 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
self.path_systemp = os.path.join(self.path_system, "Temp")
|
||||
self.file_config = os.path.join(self.path_config, "PYAS", "Config.json")
|
||||
self.file_log = os.path.join(self.path_config, "PYAS", "Report.log")
|
||||
|
||||
self.path_properties = os.path.join(self.path_pyas, "Engine", "Properties")
|
||||
self.path_pattern = os.path.join(self.path_pyas, "Engine", "Pattern")
|
||||
self.path_heuristic = os.path.join(self.path_pyas, "Engine", "Heuristic")
|
||||
@@ -252,6 +259,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.ntdll.NtQueryInformationProcess.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.ULONG, ctypes.c_void_p, ctypes.wintypes.ULONG, ctypes.POINTER(ctypes.wintypes.ULONG)]
|
||||
self.ntdll.NtQueryInformationProcess.restype = ctypes.wintypes.ULONG
|
||||
|
||||
self.ntdll.NtSuspendProcess.argtypes = [ctypes.wintypes.HANDLE]
|
||||
self.ntdll.NtSuspendProcess.restype = ctypes.c_ulong
|
||||
|
||||
self.ntdll.NtResumeProcess.argtypes = [ctypes.wintypes.HANDLE]
|
||||
self.ntdll.NtResumeProcess.restype = ctypes.c_ulong
|
||||
|
||||
self.shell32.CommandLineToArgvW.argtypes = [ctypes.wintypes.LPCWSTR, ctypes.POINTER(ctypes.c_int)]
|
||||
self.shell32.CommandLineToArgvW.restype = ctypes.POINTER(ctypes.wintypes.LPWSTR)
|
||||
|
||||
@@ -260,6 +273,33 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.fltlib.FilterGetMessage.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_void_p, ctypes.wintypes.DWORD, ctypes.c_void_p]
|
||||
self.fltlib.FilterGetMessage.restype = ctypes.c_long
|
||||
|
||||
self.kernel32.CreateToolhelp32Snapshot.restype = ctypes.wintypes.HANDLE
|
||||
self.kernel32.CreateToolhelp32Snapshot.argtypes = [ctypes.wintypes.DWORD, ctypes.wintypes.DWORD]
|
||||
|
||||
self.kernel32.Process32FirstW.restype = ctypes.wintypes.BOOL
|
||||
self.kernel32.Process32FirstW.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_void_p]
|
||||
|
||||
self.kernel32.Process32NextW.restype = ctypes.wintypes.BOOL
|
||||
self.kernel32.Process32NextW.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_void_p]
|
||||
|
||||
self.kernel32.OpenProcess.restype = ctypes.wintypes.HANDLE
|
||||
self.kernel32.OpenProcess.argtypes = [ctypes.wintypes.DWORD, ctypes.wintypes.BOOL, ctypes.wintypes.DWORD]
|
||||
|
||||
self.kernel32.CloseHandle.restype = ctypes.wintypes.BOOL
|
||||
self.kernel32.CloseHandle.argtypes = [ctypes.wintypes.HANDLE]
|
||||
|
||||
self.kernel32.TerminateProcess.restype = ctypes.wintypes.BOOL
|
||||
self.kernel32.TerminateProcess.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_uint]
|
||||
|
||||
self.kernel32.CreateMutexW.restype = ctypes.wintypes.HANDLE
|
||||
self.kernel32.CreateMutexW.argtypes = [ctypes.c_void_p, ctypes.wintypes.BOOL, ctypes.c_wchar_p]
|
||||
|
||||
self.kernel32.CreateFileW.restype = ctypes.wintypes.HANDLE
|
||||
self.kernel32.CreateFileW.argtypes = [ctypes.c_wchar_p, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.c_void_p, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.wintypes.HANDLE]
|
||||
|
||||
self.kernel32.ReadProcessMemory.restype = ctypes.wintypes.BOOL
|
||||
self.kernel32.ReadProcessMemory.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_size_t, ctypes.POINTER(ctypes.c_size_t)]
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def save_config(self, file_path, config):
|
||||
@@ -309,6 +349,11 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def init_engine_thread(self):
|
||||
try:
|
||||
self.backup_mbr()
|
||||
self.relock_file()
|
||||
self.init_whitelist()
|
||||
self.init_thread()
|
||||
|
||||
self.heuristic.load_path(self.path_heuristic, callback=lambda x: self.init_log_signal.emit(os.path.basename(x)))
|
||||
self.pattern.load_path(self.path_pattern, callback=lambda x: self.init_log_signal.emit(os.path.basename(x)))
|
||||
self.properties.load_path(self.path_properties, callback=lambda x: self.init_log_signal.emit(os.path.basename(x)))
|
||||
@@ -561,12 +606,36 @@ class MainWindow_Controller(QMainWindow):
|
||||
def change_window(self, widget_name, duration=200):
|
||||
widget = self.widgets.get(widget_name)
|
||||
if widget and widget != self.last_widget:
|
||||
scan_win = self.widgets.get("scan_window")
|
||||
method_win = self.widgets.get("method_window")
|
||||
|
||||
if self.last_widget in (scan_win, method_win) and widget in self.main_window and widget != scan_win:
|
||||
if getattr(self, "scan_finished", False):
|
||||
def delayed_reset():
|
||||
if self.last_widget not in (scan_win, method_win):
|
||||
self.scan_finished = False
|
||||
self.scan_running = False
|
||||
self.virus_results.clear()
|
||||
self.scan_reset_signal.emit()
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.scan_progress_signal.emit(self.trans(lang, "此選項可以選擇掃描方式"))
|
||||
self.progress_title_signal.emit(self.trans(lang, "病毒掃描"))
|
||||
|
||||
QTimer.singleShot(duration, delayed_reset)
|
||||
|
||||
widget.raise_()
|
||||
widget.hide()
|
||||
for window in self.menu_window:
|
||||
window.raise_()
|
||||
for window in self.sub_window:
|
||||
window.hide()
|
||||
|
||||
if widget in (scan_win, method_win):
|
||||
if getattr(self, "scan_finished", False):
|
||||
self.widgets["solve_button"].setVisible(len(self.virus_results) > 0)
|
||||
self.widgets["stop_button"].hide()
|
||||
self.widgets["method_button"].show()
|
||||
|
||||
self.animate_geometry(widget, duration)
|
||||
self.last_widget = widget
|
||||
|
||||
@@ -622,22 +691,17 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def show_startup(self):
|
||||
try:
|
||||
if self.singleton_mutex("pyas_security"):
|
||||
self.backup_mbr()
|
||||
self.relock_file()
|
||||
self.init_whitelist()
|
||||
self.init_thread()
|
||||
self.init_connect()
|
||||
|
||||
param = ""
|
||||
if self.args_pyas:
|
||||
param = self.args_pyas[0].replace("/", "-")
|
||||
if "-h" not in param:
|
||||
self.show_button()
|
||||
self.apply_settings()
|
||||
self.start_daemon_thread(self.init_engine_thread)
|
||||
else:
|
||||
if not self.singleton_mutex("pyas_security"):
|
||||
sys.exit(0)
|
||||
|
||||
self.init_connect()
|
||||
self.apply_settings()
|
||||
|
||||
param = self.args_pyas[0].replace("/", "-") if self.args_pyas else ""
|
||||
if "-h" not in param:
|
||||
self.show_button()
|
||||
|
||||
self.start_daemon_thread(self.init_engine_thread)
|
||||
except Exception as e:
|
||||
self.send_message(f"show_startup | {e}", "warn", False)
|
||||
|
||||
@@ -672,6 +736,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
def reset_button(self):
|
||||
if self.send_message("您確定要重置所有設定嗎?", "quest", True):
|
||||
self.pyas_config = self.pyas_default.copy()
|
||||
self.save_config(self.file_config, self.pyas_config)
|
||||
self.close_button()
|
||||
|
||||
def singleton_mutex(self, name):
|
||||
@@ -729,11 +794,36 @@ class MainWindow_Controller(QMainWindow):
|
||||
log_widget = self.widgets.get("log_text")
|
||||
if log_widget:
|
||||
log_widget.append(output)
|
||||
|
||||
self.write_log(output)
|
||||
return select
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def write_log(self, text):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(self.file_log), exist_ok=True)
|
||||
with open(self.file_log, "a", encoding="utf-8", errors="ignore") as f:
|
||||
f.write(f"{text}\n")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def init_clean_logs(self):
|
||||
try:
|
||||
if not os.path.exists(self.file_log):
|
||||
return
|
||||
cutoff_time = time.time() - 30 * 86400
|
||||
cutoff_str = time.strftime("[%Y-%m-%d", time.localtime(cutoff_time))
|
||||
with open(self.file_log, "r", encoding="utf-8", errors="ignore") as f:
|
||||
lines = f.readlines()
|
||||
with open(self.file_log, "w", encoding="utf-8", errors="ignore") as f:
|
||||
for line in lines:
|
||||
if len(line) >= 11 and line[:11] >= cutoff_str:
|
||||
f.write(line)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def invoke_method(self, obj, method, *args):
|
||||
@@ -773,9 +863,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
count = len(self.virus_results)
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
self.widgets["virus_list"].clear()
|
||||
self.widgets["stop_button"].show()
|
||||
self.widgets["method_button"].hide()
|
||||
self.widgets["method_window"].hide()
|
||||
self.widgets["stop_button"].setVisible(self.scan_running)
|
||||
self.widgets["method_button"].setVisible(not self.scan_running)
|
||||
|
||||
@Slot(str)
|
||||
def slot_progress_title(self, text: str):
|
||||
@@ -794,6 +884,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
@Slot(str)
|
||||
def slot_scan_result(self, msg):
|
||||
self.scan_finished = True
|
||||
count = len(self.virus_results)
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
self.widgets["progress_text"].setText(msg)
|
||||
@@ -823,6 +914,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
def init_scan(self):
|
||||
try:
|
||||
self.scan_running = True
|
||||
self.scan_finished = False
|
||||
self.virus_results = []
|
||||
self.scan_count = 0
|
||||
self.scan_start = time.time()
|
||||
@@ -859,14 +951,21 @@ class MainWindow_Controller(QMainWindow):
|
||||
for file_path in self.yield_files(targets):
|
||||
if not self.scan_running:
|
||||
break
|
||||
|
||||
norm_path = self.norm_path(file_path)
|
||||
was_locked = False
|
||||
|
||||
try:
|
||||
norm_path = self.norm_path(file_path)
|
||||
if norm_path in self.virus_lock:
|
||||
self.lock_file(norm_path, False)
|
||||
was_locked = True
|
||||
|
||||
if self.is_in_whitelist(norm_path):
|
||||
continue
|
||||
self.scan_count += 1
|
||||
|
||||
suffix = self.pyas_config.get("suffix", [".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".mui"])
|
||||
ext = os.path.splitext(file_path)[-1].lower()
|
||||
suffix = self.pyas_config.get("suffix", [".exe", ".dll", ".sys", ".ocx", ".scr", ".efi", ".acm", ".ax", ".cpl", ".drv", ".com", ".mui", ".pyd"])
|
||||
ext = os.path.splitext(norm_path)[-1].lower()
|
||||
if ext not in suffix:
|
||||
continue
|
||||
|
||||
@@ -882,6 +981,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
gc.collect()
|
||||
except Exception as e:
|
||||
self.send_message(f"scan_worker | {e}", "warn", False)
|
||||
finally:
|
||||
if was_locked:
|
||||
try:
|
||||
self.lock_file(norm_path, True)
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
count = len(self.virus_results)
|
||||
elapsed = int(time.time() - self.scan_start)
|
||||
@@ -905,11 +1010,6 @@ class MainWindow_Controller(QMainWindow):
|
||||
yield from self.yield_files(t)
|
||||
|
||||
def scan_engine(self, file_path):
|
||||
try:
|
||||
if self.sign.sign_verify(file_path):
|
||||
return False
|
||||
except Exception as e:
|
||||
self.send_message(f"sign_verify | {e}", "warn", False)
|
||||
try:
|
||||
pe_label, pe_score = self.properties.pe_scan(file_path)
|
||||
if pe_label:
|
||||
@@ -1000,26 +1100,29 @@ class MainWindow_Controller(QMainWindow):
|
||||
####################################################################################################
|
||||
|
||||
def solve_button(self):
|
||||
deleted = 0
|
||||
virus_list = self.widgets["virus_list"]
|
||||
start_ts = time.time()
|
||||
if self.send_message("您確定要刪除檔案嗎?", "quest"):
|
||||
deleted = 0
|
||||
virus_list = self.widgets["virus_list"]
|
||||
start_ts = time.time()
|
||||
|
||||
for i in reversed(range(virus_list.count())):
|
||||
item = virus_list.item(i)
|
||||
if item.checkState() == Qt.Checked:
|
||||
file_path = self.norm_path(item.text().split(">")[1].strip())
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.progress_title_signal.emit(self.trans(lang, "正在刪除"))
|
||||
self.scan_progress_signal.emit(file_path)
|
||||
for i in reversed(range(virus_list.count())):
|
||||
item = virus_list.item(i)
|
||||
if item.checkState() == Qt.Checked:
|
||||
file_path = self.norm_path(item.text().split(">")[1].strip())
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.progress_title_signal.emit(self.trans(lang, "正在刪除"))
|
||||
self.scan_progress_signal.emit(file_path)
|
||||
|
||||
try:
|
||||
os.remove(file_path)
|
||||
deleted += 1
|
||||
virus_list.takeItem(i)
|
||||
self.virus_results.remove(file_path)
|
||||
except Exception as e:
|
||||
self.send_message(f"solve_button | {e}", "warn", False)
|
||||
QApplication.processEvents()
|
||||
try:
|
||||
self.manage_named_list("quarantine", [file_path], action="remove", lock_func=self.lock_file)
|
||||
os.remove(file_path)
|
||||
deleted += 1
|
||||
virus_list.takeItem(i)
|
||||
if file_path in self.virus_results:
|
||||
self.virus_results.remove(file_path)
|
||||
except Exception as e:
|
||||
self.send_message(f"solve_button | {e}", "warn", False)
|
||||
QApplication.processEvents()
|
||||
|
||||
count = len(self.virus_results)
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
@@ -1072,9 +1175,11 @@ class MainWindow_Controller(QMainWindow):
|
||||
file_path = self.norm_path(path)
|
||||
if file_path and self.send_message("您確定要刪除檔案嗎?", "quest"):
|
||||
try:
|
||||
self.manage_named_list("quarantine", [file_path], action="remove", lock_func=self.lock_file)
|
||||
os.remove(file_path)
|
||||
widget.takeItem(widget.row(item))
|
||||
self.virus_results.remove(file_path)
|
||||
if file_path in self.virus_results:
|
||||
self.virus_results.remove(file_path)
|
||||
except Exception as e:
|
||||
self.send_message(f"show_scan_menu | {e}", "warn", False)
|
||||
|
||||
@@ -1082,10 +1187,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
file_path = self.norm_path(path)
|
||||
if file_path and self.send_message("您確定要增加到白名單嗎?", "quest"):
|
||||
try:
|
||||
self.manage_named_list("quarantine", [file_path], action="remove", lock_func=self.lock_file)
|
||||
n = self.manage_named_list("white_list", [file_path], action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info", True)
|
||||
widget.takeItem(widget.row(item))
|
||||
self.virus_results.remove(file_path)
|
||||
if file_path in self.virus_results:
|
||||
self.virus_results.remove(file_path)
|
||||
except Exception as e:
|
||||
self.send_message(f"show_scan_menu | {e}", "warn", False)
|
||||
|
||||
@@ -1096,7 +1203,8 @@ class MainWindow_Controller(QMainWindow):
|
||||
n = self.manage_named_list("quarantine", [file_path], action="add", with_hash=True, lock_func=self.lock_file)
|
||||
self.send_message(f"成功增加到隔離區,共 {n} 個檔案", "info", True)
|
||||
widget.takeItem(widget.row(item))
|
||||
self.virus_results.remove(file_path)
|
||||
if file_path in self.virus_results:
|
||||
self.virus_results.remove(file_path)
|
||||
except Exception as e:
|
||||
self.send_message(f"show_scan_menu | {e}", "warn", False)
|
||||
|
||||
@@ -1108,13 +1216,18 @@ class MainWindow_Controller(QMainWindow):
|
||||
def list_process(self):
|
||||
pe = self.get_process_entry()
|
||||
snapshot = self.kernel32.CreateToolhelp32Snapshot(0x00000002, 0)
|
||||
result, success = [], self.kernel32.Process32FirstW(snapshot, ctypes.byref(pe))
|
||||
|
||||
result = []
|
||||
|
||||
if snapshot in (-1, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF):
|
||||
return result
|
||||
|
||||
success = self.kernel32.Process32FirstW(snapshot, ctypes.byref(pe))
|
||||
while success:
|
||||
pid = pe.th32ProcessID
|
||||
name, file_path = (None, None)
|
||||
if pid > 4:
|
||||
name, file_path = self.get_exe_info(pid)
|
||||
|
||||
if not name:
|
||||
try:
|
||||
name = pe.szExeFile
|
||||
@@ -1123,6 +1236,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
result.append((pid, name, file_path))
|
||||
success = self.kernel32.Process32NextW(snapshot, ctypes.byref(pe))
|
||||
|
||||
self.kernel32.CloseHandle(snapshot)
|
||||
return result
|
||||
|
||||
@@ -1215,12 +1329,14 @@ class MainWindow_Controller(QMainWindow):
|
||||
try:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if h:
|
||||
file_path = self.norm_path(self.get_process_file(h))
|
||||
if self.path_equal(file_path, self.file_pyas):
|
||||
self.close_button()
|
||||
else:
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.kernel32.CloseHandle(h)
|
||||
try:
|
||||
file_path = self.norm_path(self.get_process_file(h))
|
||||
if self.path_equal(file_path, self.file_pyas):
|
||||
self.close_button()
|
||||
else:
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
finally:
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception as e:
|
||||
self.send_message(f"kill_process | {e}", "warn", False)
|
||||
self.refresh_process()
|
||||
@@ -1747,15 +1863,16 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def lock_file(self, file, lock):
|
||||
try:
|
||||
if lock:
|
||||
if file not in self.virus_lock:
|
||||
self.virus_lock[file] = os.open(file, os.O_RDWR)
|
||||
msvcrt.locking(self.virus_lock[file], msvcrt.LK_NBRLCK, os.path.getsize(file))
|
||||
else:
|
||||
if file in self.virus_lock:
|
||||
msvcrt.locking(self.virus_lock[file], msvcrt.LK_UNLCK, os.path.getsize(file))
|
||||
os.close(self.virus_lock[file])
|
||||
del self.virus_lock[file]
|
||||
with self.lock_mutex:
|
||||
if lock:
|
||||
if file not in self.virus_lock:
|
||||
self.virus_lock[file] = os.open(file, os.O_RDWR)
|
||||
msvcrt.locking(self.virus_lock[file], msvcrt.LK_NBRLCK, os.path.getsize(file))
|
||||
else:
|
||||
if file in self.virus_lock:
|
||||
msvcrt.locking(self.virus_lock[file], msvcrt.LK_UNLCK, os.path.getsize(file))
|
||||
os.close(self.virus_lock[file])
|
||||
del self.virus_lock[file]
|
||||
except Exception as e:
|
||||
self.send_message(f"lock_file | {e}", "warn", False)
|
||||
|
||||
@@ -1775,21 +1892,22 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.exist_process = self.get_process_list()
|
||||
while self.pyas_config.get("process_switch", False):
|
||||
try:
|
||||
time.sleep(0.2)
|
||||
time.sleep(0.1)
|
||||
cur = self.get_process_list()
|
||||
for pid in cur - self.exist_process:
|
||||
self.handle_new_process(pid)
|
||||
new_pids = cur - self.exist_process
|
||||
for pid in new_pids:
|
||||
self.start_daemon_thread(self.handle_new_process, pid)
|
||||
self.exist_process = cur
|
||||
except Exception as e:
|
||||
self.send_message(f"protect_proc_thread | {e}", "warn", False)
|
||||
|
||||
def handle_new_process(self, pid):
|
||||
try:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if not h:
|
||||
return
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if not h:
|
||||
return
|
||||
|
||||
self.suspend_process(h, True)
|
||||
self.suspend_process(h, True)
|
||||
try:
|
||||
cmdline = self.get_process_cmdline(h)
|
||||
paths = []
|
||||
for arg in self.split_commandline(cmdline):
|
||||
@@ -1798,37 +1916,34 @@ class MainWindow_Controller(QMainWindow):
|
||||
seen = set()
|
||||
paths = [p for p in paths if p and (p not in seen and not seen.add(p))]
|
||||
|
||||
suffix = self.pyas_config.get("suffix", [".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".mui"])
|
||||
suffix = self.pyas_config.get("suffix", [".exe", ".dll", ".sys", ".ocx", ".scr", ".efi", ".acm", ".ax", ".cpl", ".drv", ".com", ".mui", ".pyd"])
|
||||
|
||||
for p in paths:
|
||||
file_path = self.norm_path(self.device_path_to_drive(p)) if p else ""
|
||||
|
||||
file_path = self.norm_path(self.device_path_to_drive(p))
|
||||
if not file_path or not os.path.isfile(file_path):
|
||||
continue
|
||||
|
||||
ext = os.path.splitext(file_path)[-1].lower()
|
||||
if ext not in suffix:
|
||||
continue
|
||||
|
||||
if self.is_in_whitelist(file_path):
|
||||
continue
|
||||
|
||||
self.start_daemon_thread(self.cloud_check, file_path)
|
||||
|
||||
if self.scan_engine(file_path):
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.send_message(f"進程防護 | 靜態掃描攔截 | {pid} | None | {file_path}", "notify", True)
|
||||
self.start_daemon_thread(self.cloud_check, file_path)
|
||||
|
||||
self.suspend_process(h, False)
|
||||
self.kernel32.CloseHandle(h)
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
self.send_message(f"handle_new_process | {e}", "warn", False)
|
||||
finally:
|
||||
self.suspend_process(h, False)
|
||||
self.kernel32.CloseHandle(h)
|
||||
|
||||
def suspend_process(self, h_process, suspend):
|
||||
try:
|
||||
if not hasattr(self.ntdll, "NtSuspendProcess"):
|
||||
self.ntdll.NtSuspendProcess.argtypes = [ctypes.c_void_p]
|
||||
self.ntdll.NtSuspendProcess.restype = ctypes.c_ulong
|
||||
self.ntdll.NtResumeProcess.argtypes = [ctypes.c_void_p]
|
||||
self.ntdll.NtResumeProcess.restype = ctypes.c_ulong
|
||||
if suspend:
|
||||
self.ntdll.NtSuspendProcess(h_process)
|
||||
else:
|
||||
@@ -1841,6 +1956,10 @@ class MainWindow_Controller(QMainWindow):
|
||||
def get_process_list(self):
|
||||
exist_process, pe = set(), self.get_process_entry()
|
||||
hSnapshot = self.kernel32.CreateToolhelp32Snapshot(0x00000002, 0)
|
||||
|
||||
if hSnapshot in (-1, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF):
|
||||
return exist_process
|
||||
|
||||
if self.kernel32.Process32FirstW(hSnapshot, ctypes.byref(pe)):
|
||||
while True:
|
||||
exist_process.add(pe.th32ProcessID)
|
||||
@@ -1962,7 +2081,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
file_path = self.norm_path(os.path.join(self.path_user, raw_filename), must_exist=True)
|
||||
if file_path and notify.Action in [2, 3, 4] and not self.is_in_whitelist(file_path):
|
||||
|
||||
suffix = self.pyas_config.get("suffix", [".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys", ".mui"])
|
||||
suffix = self.pyas_config.get("suffix", [".exe", ".dll", ".sys", ".ocx", ".scr", ".efi", ".acm", ".ax", ".cpl", ".drv", ".com", ".mui", ".pyd"])
|
||||
ext = os.path.splitext(file_path)[-1].lower()
|
||||
if ext not in suffix:
|
||||
continue
|
||||
@@ -1981,7 +2100,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
def protect_net_thread(self):
|
||||
while self.pyas_config.get("network_switch", False):
|
||||
try:
|
||||
time.sleep(0.5)
|
||||
time.sleep(0.2)
|
||||
conns = self.get_connections_list()
|
||||
if not hasattr(self, "exist_connections") or self.exist_connections is None:
|
||||
self.exist_connections = set()
|
||||
|
||||
+428
-3
@@ -69,12 +69,437 @@ translate_dict = {
|
||||
"重置":"Reset ","窗口":"Window ","暫存":"Temp ","自訂":"Custom ",
|
||||
"不支持":"Not Support ","共":"Total ","秒":"Seconds ","耗時":"Takes ",
|
||||
"沙箱":"Sandbox ", "虛擬":"Virtual ", "隔離":"Quarantine ", "時":"",
|
||||
"建立":"Create ","環境":"Environment ","沙箱":"Sandbox ","化":"", "表":"",
|
||||
"建立":"Create ","環境":"Environment ","化":"","表":"",
|
||||
"達":"Reached ", "上限":"Limit ", "區":"", "金鑰":"Key ", "產品":"Product ",
|
||||
"運行庫":".NET CLR ","殼碼":"Shellcode ","線程":"Thread ","注入":"Inject ",
|
||||
"前往":"Go ","檢查":"Check ","可用":"Available ","靜態":"Static ",
|
||||
"版本":"Version ","是":"","剩餘":"Remain ","遠控":"Remote ","監控":"Monitor ",
|
||||
" ":" "},
|
||||
"版本":"Version ","是":"","剩餘":"Remain ","遠控":"Remote "," ":" "},
|
||||
|
||||
"japanese_switch": {"開啟":"オン","關閉":"オフ","开启":"オン","关闭":"オフ",",":", ",
|
||||
"立即刪除":"即時削除","安全模式":"セーフモード","官方網站":"公式サイト",
|
||||
"腳本":"スクリプト","鏈式":"チェーン","記憶體":"メモリ","正在加載":"読み込み中","加載":"ロード",
|
||||
"行為":"振る舞い","可疑":"疑わしい","勒索":"ランサム","主要":"メイン",
|
||||
"許可條款":"ライセンス条項","惡意":"悪意のある","開發":"開発",
|
||||
"啟用":"有効化","禁用":"無効化","正在初始化中":"初期化中","初始化":"初期化",
|
||||
"正在掃描":"スキャン中","正在刪除":"削除中","正在追蹤":"追跡中",
|
||||
"最小化到":"最小化先","靈敏度":"感度","正在檢查":"確認中",
|
||||
"確定要":"本当によろしいですか","引擎靈敏度":"エンジン感度","更新":"更新",
|
||||
"白色主題":"ホワイト","黃色主題":"イエロー","紅色主題":"レッド","語言":"言語",
|
||||
"藍色主題":"ブルー","綠色主題":"グリーン","自訂主題":"カスタム","黑色主題":"ブラック",
|
||||
"特別感謝":"貢献者","版本資訊":"バージョン情報","通訊":"通信",
|
||||
"註冊":"レジストリ","退出":"終了","檔案":"ファイル","當前":"現在の",
|
||||
"狀態":"状態","掃描":"スキャン","工具":"ツール","防護":"保護",
|
||||
"失敗":"失敗","請":"してください","路徑":"パス","方式":"方式",
|
||||
"需要":"必要","病毒":"ウイルス","選擇":"選択","返回":"戻る",
|
||||
"受到":"受けた","錯誤":"エラー","完成":"完了","引擎":"エンジン",
|
||||
"托盤":"トレイ","圖標":"アイコン","不安全":"非セキュア","增強":"強化",
|
||||
"系統":"システム","雲端":"クラウド","重啟":"再起動","修復":"修復",
|
||||
"裝置":"デバイス","發現":"検出","成功":"成功","清理":"クリーンアップ",
|
||||
"確定":"確定","執行":"実行","警告":"警告","使用":"使用",
|
||||
"網路":"ネットワーク","變更":"変更","總數":"合計","結束":"終了",
|
||||
"選項":"オプション","監控":"監視","連接":"接続","可以":"可能",
|
||||
"進程":"プロセス","攔截":"ブロック","引導":"ブート","刪除":"削除",
|
||||
"分區":"パーティション","設定":"設定","關於":"情報","模式":"モード",
|
||||
"提高":"向上","安全":"セキュリティ","顯色":"色","顏色":"色",
|
||||
"主題":"テーマ","回收":"回収","無用":"不要な","導入":"インポート",
|
||||
"風險":"リスク","介面":"インターフェース","核心":"コア","服務":"サービス",
|
||||
"管理":"管理","所有":"すべての","您":"あなた","取消":"キャンセル","未":"未",
|
||||
"垃圾":"ジャンク","位元":"バイト","本機":"PC","停止":"停止",
|
||||
"到":"へ","白名單":"ホワイトリスト","軟體":"ソフトウェア","正在":"",
|
||||
"增加":"追加","移除":"削除","項目":"項目","生成":"生成","稱":"",
|
||||
"複製":"コピー","此":"この","並":"および","全盤":"フル","最新":"最新",
|
||||
"已存在":"既に存在します","數據庫":"データベース","誤報":"誤検知",
|
||||
"高":"高","新":"新","和":"と","可能會":"可能性があります","會":"します",
|
||||
"日誌":"ログ","已":"済み","彈窗":"ポップアップ","要":"する","驅動":"ドライバ",
|
||||
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"ルール",
|
||||
"密鑰":"キー","檢測":"検知","製作":"作成","運行":"実行中",
|
||||
"擴展":"拡張","套件":"キット","自動":"自動","上報":"報告",
|
||||
"分析":"分析","自身":"自身","其他":"その他","配置":"設定",
|
||||
"丟失":"紛失","網站":"ウェブサイト","不兼容":"非互換","列表":"リスト",
|
||||
"竄改":"改ざん","第三方":"サードパーティ","實時":"リアルタイム","查看":"表示",
|
||||
"指定":"指定","清空":"空にする","資源回收桶":"ごみ箱","無法":"できません",
|
||||
"與":"と","當":"時","功能":"機能","異常":"異常","來":"へ",
|
||||
"重置":"リセット","窗口":"ウィンドウ","暫存":"一時","自訂":"カスタム",
|
||||
"不支持":"未対応","共":"合計","秒":"秒","耗時":"所要時間",
|
||||
"沙箱":"サンドボックス","虛擬":"仮想","隔離":"隔離","時":"",
|
||||
"建立":"作成","環境":"環境","化":"","表":"",
|
||||
"達":"到達","上限":"上限","區":"","金鑰":"キー","產品":"製品",
|
||||
"運行庫":".NET CLR","殼碼":"シェルコード","線程":"スレッド","注入":"インジェクション",
|
||||
"前往":"移動","檢查":"確認","可用":"利用可能","靜態":"静的",
|
||||
"版本":"バージョン","是":"","剩餘":"残り","遠控":"遠隔操作"," ":" "},
|
||||
|
||||
"korean_switch": {"開啟":"켜기","關閉":"끄기","开启":"켜기","关闭":"끄기",",":", ",
|
||||
"立即刪除":"즉시 삭제","安全模式":"안전 모드","官方網站":"공식 웹사이트",
|
||||
"腳本":"스크립트","鏈式":"체인","記憶體":"메모리","正在加載":"로딩 중","加載":"로드",
|
||||
"行為":"행위","可疑":"의심스러운","勒索":"랜섬웨어","主要":"주요",
|
||||
"許可條款":"라이선스 약관","惡意":"악성","開發":"개발",
|
||||
"啟用":"활성화","禁用":"비활성화","正在初始化中":"초기화 중","初始化":"초기화",
|
||||
"正在掃描":"스캔 중","正在刪除":"삭제 중","正在追蹤":"추적 중",
|
||||
"最小化到":"최소화","靈敏度":"민감도","正在檢查":"검사 중",
|
||||
"確定要":"정말","引擎靈敏度":"엔진 민감도","更新":"업데이트",
|
||||
"白色主題":"화이트","黃色主題":"옐로우","紅色主題":"레드","語言":"언어",
|
||||
"藍色主題":"블루","綠色主題":"그린","自訂主題":"사용자 지정","黑色主題":"블랙",
|
||||
"特別感謝":"기여자","版本資訊":"버전 정보","通訊":"통신",
|
||||
"註冊":"레지스트리","退出":"종료","檔案":"파일","當前":"현재",
|
||||
"狀態":"상태","掃描":"스캔","工具":"도구","防護":"보호",
|
||||
"失敗":"실패","請":"부탁합니다","路徑":"경로","方式":"방식",
|
||||
"需要":"필요","病毒":"바이러스","選擇":"선택","返回":"뒤로",
|
||||
"受到":"받음","錯誤":"오류","完成":"완료","引擎":"엔진",
|
||||
"托盤":"트레이","圖標":"아이콘","不安全":"안전하지 않음","增強":"강화",
|
||||
"系統":"시스템","雲端":"클라우드","重啟":"재부팅","修復":"복구",
|
||||
"裝置":"장치","發現":"발견","成功":"성공","清理":"정리",
|
||||
"確定":"확인","執行":"실행","警告":"경고","使用":"사용",
|
||||
"網路":"네트워크","變更":"변경","總數":"총계","結束":"종료",
|
||||
"選項":"옵션","監控":"모니터링","連接":"연결","可以":"가능",
|
||||
"進程":"프로세스","攔截":"차단","引導":"부트","刪除":"삭제",
|
||||
"分區":"파티션","設定":"설정","關於":"정보","模式":"모드",
|
||||
"提高":"향상","安全":"보안","顯色":"색상","顏色":"색상",
|
||||
"主題":"테마","回收":"회수","無用":"불필요한","導入":"가져오기",
|
||||
"風險":"위험","介面":"인터페이스","核心":"코어","服務":"서비스",
|
||||
"管理":"관리","所有":"모든","您":"귀하","取消":"취소","未":"미",
|
||||
"垃圾":"정크","位元":"바이트","本機":"PC","停止":"중지",
|
||||
"到":"로","白名單":"화이트리스트","軟體":"소프트웨어","正在":"",
|
||||
"增加":"추가","移除":"제거","項目":"항목","生成":"생성","稱":"",
|
||||
"複製":"복사","此":"이","並":"및","全盤":"전체","最新":"최신",
|
||||
"已存在":"이미 존재함","數據庫":"데이터베이스","誤報":"오탐지",
|
||||
"高":"높음","新":"신규","和":"와","可能會":"할 수 있습니다","會":"합니다",
|
||||
"日誌":"로그","已":"완료","彈窗":"팝업","要":"할","驅動":"드라이버",
|
||||
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"규칙",
|
||||
"密鑰":"키","檢測":"탐지","製作":"제작","運行":"실행 중",
|
||||
"擴展":"확장","套件":"키트","自動":"자동","上報":"보고",
|
||||
"分析":"분석","自身":"자신","其他":"기타","配置":"구성",
|
||||
"丟失":"분실","網站":"웹사이트","不兼容":"호환되지 않음","列表":"목록",
|
||||
"竄改":"변조","第三方":"서드파티","實時":"실시간","查看":"보기",
|
||||
"指定":"지정","清空":"비우기","資源回收桶":"휴지통","無法":"할 수 없음",
|
||||
"與":"와","當":"할 때","功能":"기능","異常":"이상","來":"오다",
|
||||
"重置":"초기화","窗口":"창","暫存":"임시","自訂":"사용자 지정",
|
||||
"不支持":"지원하지 않음","共":"총","秒":"초","耗時":"소요 시간",
|
||||
"沙箱":"샌드박스","虛擬":"가상","隔離":"격리","時":"",
|
||||
"建立":"생성","環境":"환경","化":"","表":"",
|
||||
"達":"도달","上限":"제한","區":"","金鑰":"키","제품":"제품",
|
||||
"運行庫":".NET CLR","殼碼":"셸코드","線程":"스레드","注入":"인젝션",
|
||||
"前往":"이동","檢查":"검사","可用":"사용 가능","靜態":"정적",
|
||||
"版本":"버전","是":"","剩餘":"남은","遠控":"원격 제어"," ":" "},
|
||||
|
||||
"french_switch": {"開啟":"On ","關閉":"Off ","开启":"On ","关闭":"Off ",",":", ",
|
||||
"立即刪除":"Supprimer ","安全模式":"Mode sans échec ","官方網站":"Site officiel ",
|
||||
"腳本":"Script ","鏈式":"Chaîne ","記憶體":"Mémoire ","正在加載":"Chargement ","加載":"Charger ",
|
||||
"行為":"Comportement ","可疑":"Suspect ","勒索":"Ransomware ","主要":"Principal ",
|
||||
"許可條款":"Licence ","惡意":"Malveillant ","開發":"Développeurs ",
|
||||
"啟用":"Activer ","禁用":"Désactiver ","正在初始化中":"Initialisation ","初始化":"Initialiser ",
|
||||
"正在掃描":"Analyse en cours ","正在刪除":"Suppression ","正在追蹤":"Suivi en cours ",
|
||||
"最小化到":"Réduire vers ","靈敏度":"Sensibilité ","正在檢查":"Vérification ",
|
||||
"確定要":"Êtes-vous sûr ","引擎靈敏度":"Sensibilité ","更新":"Mettre à jour ",
|
||||
"白色主題":"Blanc","黃色主題":"Jaune","紅色主題":"Rouge","語言":"Langue ",
|
||||
"藍色主題":"Bleu","綠色主題":"Vert","自訂主題":"Personnalisé","黑色主題":"Noir",
|
||||
"特別感謝":"Contributeurs ","版本資訊":"Information ","通訊":"Connexion ",
|
||||
"註冊":"Registre ","退出":"Quitter ","檔案":"Fichier ","當前":"Actuellement ",
|
||||
"狀態":"État ","掃描":"Analyser ","工具":"Outils ","防護":"Protection ",
|
||||
"失敗":"Échec ","請":"Veuillez ","路徑":"Chemin ","方式":"Méthode ",
|
||||
"需要":"Nécessaire ","病毒":"Virus ","選擇":"Sélectionner ","返回":"Retour ",
|
||||
"受到":"Reçu ","錯誤":"Erreur ","完成":"Terminé ","引擎":"Moteur ",
|
||||
"托盤":"Barre ","圖標":"Icône ","不安全":"Non sécurisé ","增強":"Améliorer ",
|
||||
"系統":"Système ","雲端":"Cloud ","重啟":"Redémarrer ","修復":"Réparer ",
|
||||
"裝置":"Appareil ","發現":"Trouvé ","成功":"Succès ","清理":"Nettoyer ",
|
||||
"確定":"OK ","執行":"Exécuter ","警告":"Avertissement ","使用":"Utiliser ",
|
||||
"網路":"Réseau ","變更":"Modifier ","總數":"Total ","結束":"Fin ",
|
||||
"選項":"Options ","監控":"Surveiller ","連接":"Connecter ","可以":"Peut ",
|
||||
"進程":"Processus ","攔截":"Bloquer ","引導":"Amorçage ","刪除":"Supprimer ",
|
||||
"分區":"Partition ","設定":"Paramètres ","關於":"À propos ","模式":"Mode ",
|
||||
"提高":"Améliorer ","安全":"Sécurité ","顯色":"Couleur ","顏色":"Couleur ",
|
||||
"主題":"Thème ","回收":"Corbeille ","無用":"Inutile ","導入":"Importer ",
|
||||
"風險":"Risque ","介面":"UI ","核心":"Cœur ","服務":"Services ",
|
||||
"管理":"Gérer ","所有":"Tout ","您":"Vous ","取消":"Annuler ","未":"Non ",
|
||||
"垃圾":"Déchet ","位元":"octets ","本機":"PC ","停止":"Arrêter ",
|
||||
"到":"Vers ","白名單":"Liste blanche ","軟體":"Logiciel ","正在":"",
|
||||
"增加":"Ajouter ","移除":"Retirer ","項目":"Éléments ","生成":"Créer ","稱":"",
|
||||
"複製":"Copier ","此":"Ce ","並":"Et ","全盤":"Complet ","最新":"Dernier ",
|
||||
"已存在":"Existe déjà ","數據庫":"Base de données ","誤報":"Faux positif ",
|
||||
"高":"Haut ","新":"Nouveau ","和":"Et ","可能會":"Possible ","會":"Peut ",
|
||||
"日誌":"Journal ","已":"Déjà ","彈窗":"Fenêtre ","要":"À ","驅動":"Pilote ",
|
||||
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"Règle ",
|
||||
"密鑰":"Clé ","檢測":"Détecter ","製作":"Créateur ","運行":"En cours ",
|
||||
"擴展":"Extension ","套件":"Kit ","自動":"Auto ","上報":"Signaler ",
|
||||
"分析":"Analyse ","自身":"Soi-même ","其他":"Autre ","配置":"Config ",
|
||||
"丟失":"Perdu ","網站":"Site ","不兼容":"Incompatible ","列表":"Liste ",
|
||||
"竄改":"Altérer ","第三方":"Tiers ","實時":"Temps réel ","查看":"Voir ",
|
||||
"指定":"Spécifier ","清空":"Vider ","資源回收桶":"Corbeille ","無法":"Impossible ",
|
||||
"與":"Avec ","當":"Quand ","功能":"Fonction ","異常":"Anormal ","來":"Pour ",
|
||||
"重置":"Réinitialiser ","窗口":"Fenêtre ","暫存":"Temp ","自訂":"Personnalisé ",
|
||||
"不支持":"Non supporté ","共":"Total ","秒":"Secondes ","耗時":"Prend ",
|
||||
"沙箱":"Sandbox ","虛擬":"Virtuel ","隔離":"Quarantaine ","時":"",
|
||||
"建立":"Créer ","環境":"Environnement ","化":"","表":"",
|
||||
"達":"Atteint ","上限":"Limite ","區":"","金鑰":"Clé ","產品":"Produit ",
|
||||
"運行庫":".NET CLR ","殼碼":"Shellcode ","線程":"Thread ","注入":"Injecter ",
|
||||
"前往":"Aller ","檢查":"Vérifier ","可用":"Disponible ","靜態":"Statique ",
|
||||
"版本":"Version ","是":"","剩餘":"Restant ","遠控":"Distant "," ":" "},
|
||||
|
||||
"spanish_switch": {"開啟":"On ","關閉":"Off ","开启":"On ","关闭":"Off ",",":", ",
|
||||
"立即刪除":"Eliminar ","安全模式":"Modo seguro ","官方網站":"Sitio web ",
|
||||
"腳本":"Script ","鏈式":"Cadena ","記憶體":"Memoria ","正在加載":"Cargando ","加載":"Cargar ",
|
||||
"行為":"Comportamiento ","可疑":"Sospechoso ","勒索":"Ransomware ","主要":"Principal ",
|
||||
"許可條款":"Licencia ","惡意":"Malicioso ","開發":"Desarrolladores ",
|
||||
"啟用":"Activar ","禁用":"Desactivar ","正在初始化中":"Inicializando ","初始化":"Inicializar ",
|
||||
"正在掃描":"Escaneando ","正在刪除":"Eliminando ","正在追蹤":"Rastreando ",
|
||||
"最小化到":"Minimizar a ","靈敏度":"Sensibilidad ","正在檢查":"Comprobando ",
|
||||
"確定要":"Seguro que ","引擎靈敏度":"Sensibilidad ","更新":"Actualizar ",
|
||||
"白色主題":"Blanco","黃色主題":"Amarillo","紅色主題":"Rojo","語言":"Idioma ",
|
||||
"藍色主題":"Azul","綠色主題":"Verde","自訂主題":"Personalizado","黑色主題":"Negro",
|
||||
"特別感謝":"Colaboradores ","版本資訊":"Información ","通訊":"Conectar ",
|
||||
"註冊":"Registro ","退出":"Salir ","檔案":"Archivo ","當前":"Actualmente ",
|
||||
"狀態":"Estado ","掃描":"Escanear ","工具":"Herramientas ","防護":"Protección ",
|
||||
"失敗":"Fallido ","請":"Por favor ","路徑":"Ruta ","方式":"Método ",
|
||||
"需要":"Necesita ","病毒":"Virus ","選擇":"Seleccionar ","返回":"Volver ",
|
||||
"受到":"Recibido ","錯誤":"Error ","完成":"Completado ","引擎":"Motor ",
|
||||
"托盤":"Bandeja ","圖標":"Icono ","不安全":"Inseguro ","增強":"Mejorar ",
|
||||
"系統":"Sistema ","雲端":"Nube ","重啟":"Reiniciar ","修復":"Reparar ",
|
||||
"裝置":"Dispositivo ","發現":"Encontrado ","成功":"Éxito ","清理":"Limpiar ",
|
||||
"確定":"OK ","執行":"Ejecutar ","警告":"Advertencia ","使用":"Usar ",
|
||||
"網路":"Red ","變更":"Cambiar ","總數":"Total ","結束":"Fin ",
|
||||
"選項":"Opciones ","監控":"Monitor ","連接":"Conectar ","可以":"Puede ",
|
||||
"進程":"Proceso ","攔截":"Bloquear ","引導":"Arranque ","刪除":"Eliminar ",
|
||||
"分區":"Partición ","設定":"Ajustes ","關於":"Acerca de ","模式":"Modo ",
|
||||
"提高":"Mejorar ","安全":"Seguridad ","顯色":"Color ","顏色":"Color ",
|
||||
"主題":"Tema ","回收":"Papelera ","無用":"Inútil ","導入":"Importar ",
|
||||
"風險":"Riesgo ","介面":"UI ","核心":"Núcleo ","服務":"Servicios ",
|
||||
"管理":"Gestionar ","所有":"Todo ","您":"Tú ","取消":"Cancelar ","未":"No ",
|
||||
"垃圾":"Basura ","位元":"bytes ","本機":"PC ","停止":"Detener ",
|
||||
"到":"A ","白名單":"Lista blanca ","軟體":"Software ","正在":"",
|
||||
"增加":"Añadir ","移除":"Eliminar ","項目":"Elementos ","生成":"Crear ","稱":"",
|
||||
"複製":"Copiar ","此":"Este ","並":"Y ","全盤":"Completo ","最新":"Último ",
|
||||
"已存在":"Ya existe ","數據庫":"Base de datos ","誤報":"Falso positivo ",
|
||||
"高":"Alto ","新":"Nuevo ","和":"Y ","可能會":"Posible ","會":"Puede ",
|
||||
"日誌":"Registro ","已":"Ya ","彈窗":"Ventana ","要":"A ","驅動":"Controlador ",
|
||||
"的":"","顯示":"","個":"","嗎":"","了":"","行":"","立即":"","規則":"Regla ",
|
||||
"密鑰":"Clave ","檢測":"Detectar ","製作":"Creador ","運行":"Ejecutando ",
|
||||
"擴展":"Extensión ","套件":"Kit ","自動":"Auto ","上報":"Reportar ",
|
||||
"分析":"Análisis ","自身":"Sí mismo ","其他":"Otro ","配置":"Config ",
|
||||
"丟失":"Perdido ","網站":"Sitio ","不兼容":"Incompatible ","列表":"Lista ",
|
||||
"竄改":"Manipular ","第三方":"Terceros ","實時":"Tiempo real ","查看":"Ver ",
|
||||
"指定":"Especificar ","清空":"Vaciar ","資源回收桶":"Papelera ","無法":"No puede ",
|
||||
"與":"Con ","當":"Cuando ","功能":"Función ","異常":"Anormal ","來":"Para ",
|
||||
"重置":"Restablecer ","窗口":"Ventana ","暫存":"Temp ","自訂":"Personalizado ",
|
||||
"不支持":"No soportado ","共":"Total ","秒":"Segundos ","耗時":"Tarda ",
|
||||
"沙箱":"Sandbox ","虛擬":"Virtual ","隔離":"Cuarentena ","時":"",
|
||||
"建立":"Crear ","環境":"Entorno ","化":"","表":"",
|
||||
"達":"Alcanzado ","上限":"Límite ","區":"","金鑰":"Clave ","產品":"Producto ",
|
||||
"運行庫":".NET CLR ","殼碼":"Shellcode ","線程":"Hilo ","注入":"Inyectar ",
|
||||
"前往":"Ir ","檢查":"Comprobar ","可用":"Disponible ","靜態":"Estático ",
|
||||
"版本":"Versión ","是":"","剩餘":"Restante ","遠控":"Remoto "," ":" "},
|
||||
|
||||
"hindi_switch": {"開啟":"चालू ", "關閉":"बंद ", "开启":"चालू ", "关闭":"बंद ", ",":", ",
|
||||
"立即刪除":"अभी हटाएं ", "安全模式":"सुरक्षित मोड ", "官方網站":"आधिकारिक वेबसाइट ",
|
||||
"腳本":"स्क्रिप्ट ", "鏈式":"चेन ", "記憶體":"मेमोरी ", "正在加載":"लोड हो रहा है ", "加載":"लोड करें ",
|
||||
"行為":"व्यवहार ", "可疑":"संदिग्ध ", "勒索":"रैनसमवेयर ", "主要":"मुख्य ",
|
||||
"許可條款":"लाइसेंस शर्तें ", "惡意":"दुर्भावनापूर्ण ", "開發":"डेवलपर्स ",
|
||||
"啟用":"सक्षम करें ", "禁用":"अक्षम करें ", "正在初始化中":"प्रारंभ हो रहा है ", "初始化":"प्रारंभ करें ",
|
||||
"正在掃描":"स्कैन हो रहा है ", "正在刪除":"हटाया जा रहा है ", "正在追蹤":"ट्रैक किया जा रहा है ",
|
||||
"最小化到":"इसमें छोटा करें ", "靈敏度":"संवेदनशीलता ", "正在檢查":"जांच हो रही है ",
|
||||
"確定要":"क्या आप सुनिश्चित हैं ", "引擎靈敏度":"इंजन संवेदनशीलता ", "更新":"अपडेट करें ",
|
||||
"白色主題":"सफ़ेद", "黃色主題":"पीला", "紅色主題":"लाल", "語言":"भाषा ",
|
||||
"藍色主題":"नीला", "綠色主題":"हरा", "自訂主題":"कस्टम", "黑色主題":"काला",
|
||||
"特別感謝":"योगदानकर्ता ", "版本資訊":"संस्करण जानकारी ", "通訊":"संचार ",
|
||||
"註冊":"रजिस्ट्री ", "退出":"बाहर निकलें ", "檔案":"फ़ाइल ", "當前":"वर्तमान ",
|
||||
"狀態":"स्थिति ", "掃描":"स्कैन ", "工具":"उपकरण ", "防護":"सुरक्षा ",
|
||||
"失敗":"विफल ", "請":"कृपया ", "路徑":"पथ ", "方式":"तरीका ",
|
||||
"需要":"आवश्यक ", "病毒":"वायरस ", "選擇":"चुनें ", "返回":"वापस",
|
||||
"受到":"प्राप्त हुआ ", "錯誤":"त्रुटि ", "完成":"पूरा हुआ ", "引擎":"इंजन ",
|
||||
"托盤":"ट्रे ", "圖標":"आइकन ", "不安全":"असुरक्षित ", "增強":"बढ़ाना ",
|
||||
"系統":"सिस्टम ", "雲端":"क्लाउड ", "重啟":"रीबूट ", "修復":"मरम्मत ",
|
||||
"裝置":"डिवाइस ", "發現":"मिला ", "成功":"सफल ", "清理":"सफाई ",
|
||||
"確定":"ठीक ", "執行":"निष्पादित करें ", "警告":"चेतावनी ", "使用":"उपयोग ",
|
||||
"網路":"नेटवर्क ", "變更":"बदलें ", "總數":"कुल ", "結束":"समाप्त ",
|
||||
"選項":"विकल्प ", "監控":"मॉनिटर ", "連接":"कनेक्ट ", "可以":"सकता है ",
|
||||
"進程":"प्रक्रिया ", "攔截":"ब्लॉक ", "引導":"बूट ", "刪除":"हटाएं ",
|
||||
"分區":"पार्टीशन ", "設定":"सेटिंग्स ", "關於":"के बारे में ", "模式":"मोड ",
|
||||
"提高":"सुधारें ", "安全":"सुरक्षा ", "顯色":"रंग ", "顏色":"रंग ",
|
||||
"主題":"थीम ", "回收":"रीसायकल ", "無用":"बेकार ", "導入":"आयात ",
|
||||
"風險":"जोखिम ", "介面":"यूआई ", "核心":"कोर ", "服務":"सेवाएं ",
|
||||
"管理":"प्रबंधित करें ", "所有":"सभी ", "您":"आप ", "取消":"रद्द करें ", "未":"नहीं ",
|
||||
"垃圾":"कचरा ", "位元":"बाइट ", "本機":"पीसी ", "停止":"रोकें ",
|
||||
"到":"तक ", "白名單":"श्वेत सूची ", "軟體":"सॉफ़्टवेयर ", "正在":"",
|
||||
"增加":"जोड़ें ", "移除":"हटाएं ", "項目":"आइटम ", "生成":"बनाएं ", "稱":"",
|
||||
"複製":"कॉपी ", "此":"यह ", "並":"और ", "全盤":"पूर्ण ", "最新":"नवीनतम ",
|
||||
"已存在":"पहले से मौजूद ", "數據庫":"डेटाबेस ", "誤報":"गलत सूचना ",
|
||||
"高":"उच्च ", "新":"नया ", "和":"और ", "可能會":"संभव ", "會":"सकता है ",
|
||||
"日誌":"लॉग ", "已":"हो चुका ", "彈窗":"विंडो ", "要":"को ", "驅動":"ड्राइवर ",
|
||||
"的":"", "顯示":"", "個":"", "嗎":"", "了":"", "行":"", "立即":"", "規則":"नियम ",
|
||||
"密鑰":"कुंजी ", "檢測":"पता लगाएं ", "製作":"निर्माता ", "運行":"चल रहा है ",
|
||||
"擴展":"एक्सटेंशन ", "套件":"किट ", "自動":"ऑटो ", "上報":"रिपोर्ट ",
|
||||
"分析":"विश्लेषण ", "自身":"स्वयं ", "其他":"अन्य ", "配置":"कॉन्फ़िगर ",
|
||||
"丟失":"खो गया ", "網站":"वेबसाइट ", "不兼容":"असंगत ", "列表":"सूची ",
|
||||
"竄改":"छेड़छाड़ ", "第三方":"थर्ड पार्टी ", "實時":"वास्तविक समय ", "查看":"देखें ",
|
||||
"指定":"निर्दिष्ट करें ", "清空":"खाली करें ", "資源回收桶":"रीसायकल बिन ", "無法":"असमर्थ ",
|
||||
"與":"के साथ ", "當":"जब ", "功能":"कार्य ", "異常":"असामान्य ", "來":"के लिए ",
|
||||
"重置":"रीसेट ", "窗口":"विंडो ", "暫存":"अस्थायी ", "自訂":"कस्टम ",
|
||||
"不支持":"समर्थित नहीं ", "共":"कुल ", "秒":"सेकंड ", "耗時":"लगा समय ",
|
||||
"沙箱":"सैंडबॉक्स ", "虛擬":"आभासी ", "隔離":"संगरोध ", "時":"",
|
||||
"建立":"बनाएं ", "環境":"वातावरण ", "化":"", "表":"",
|
||||
"達":"पहुंच गया ", "上限":"सीमा ", "區":"", "金鑰":"कुंजी ", "產品":"उत्पाद ",
|
||||
"運行庫":".NET CLR ", "殼碼":"शेलकोड ", "線程":"थ्रेड ", "注入":"इंजेक्टर ",
|
||||
"前往":"जाएं ", "檢查":"जांचें ", "可用":"उपलब्ध ", "靜態":"स्थैतिक ",
|
||||
"版本":"संस्करण ", "是":"", "剩餘":"शेष ", "遠控":"रिमोट ", " ":" "},
|
||||
|
||||
"arabic_switch": {"開啟":"تشغيل ", "關閉":"إيقاف ", "开启":"تشغيل ", "关闭":"إيقاف ", ",":", ",
|
||||
"立即刪除":"حذف الآن ", "安全模式":"الوضع الآمن ", "官方網站":"الموقع الرسمي ",
|
||||
"腳本":"برنامج نصي ", "鏈式":"سلسلة ", "記憶體":"الذاكرة ", "正在加載":"جار التحميل ", "加載":"تحميل ",
|
||||
"行為":"سلوك ", "可疑":"مشبوه ", "勒索":"فدية ", "主要":"رئيسي ",
|
||||
"許可條款":"شروط الترخيص ", "惡意":"ضار ", "開發":"المطورين ",
|
||||
"啟用":"تفعيل ", "禁用":"تعطيل ", "正在初始化中":"جار التهيئة ", "初始化":"تهيئة ",
|
||||
"正在掃描":"جار الفحص ", "正在刪除":"جار الحذف ", "正在追蹤":"جار التتبع ",
|
||||
"最小化到":"تصغير إلى ", "靈敏度":"حساسية ", "正在檢查":"جار التحقق ",
|
||||
"確定要":"هل أنت متأكد ", "引擎靈敏度":"حساسية المحرك ", "更新":"تحديث ",
|
||||
"白色主題":"أبيض", "黃色主題":"أصفر", "紅色主題":"أحمر", "語言":"اللغة ",
|
||||
"藍色主題":"أزرق", "綠色主題":"أخضر", "自訂主題":"مخصص", "黑色主題":"أسود",
|
||||
"特別感謝":"المساهمون ", "版本資訊":"معلومات الإصدار ", "通訊":"اتصال ",
|
||||
"註冊":"السجل ", "退出":"خروج ", "檔案":"ملف ", "當前":"الحالي ",
|
||||
"狀態":"الحالة ", "掃描":"فحص ", "工具":"أدوات ", "防護":"حماية ",
|
||||
"失敗":"فشل ", "請":"يرجى ", "路徑":"مسار ", "方式":"طريقة ",
|
||||
"需要":"يحتاج ", "病毒":"فيروس ", "選擇":"تحديد ", "返回":"رجوع",
|
||||
"受到":"تم الاستلام ", "錯誤":"خطأ ", "完成":"مكتمل ", "引擎":"محرك ",
|
||||
"托盤":"صينية ", "圖標":"أيقونة ", "不安全":"غير آمن ", "增強":"تحسين ",
|
||||
"系統":"النظام ", "雲端":"سحابة ", "重啟":"إعادة تشغيل ", "修復":"إصلاح ",
|
||||
"裝置":"جهاز ", "發現":"تم العثور ", "成功":"نجاح ", "清理":"تنظيف ",
|
||||
"確定":"موافق ", "執行":"تنفيذ ", "警告":"تحذير ", "使用":"استخدام ",
|
||||
"網路":"الشبكة ", "變更":"تغيير ", "總數":"الإجمالي ", "結束":"إنهاء ",
|
||||
"選項":"خيارات ", "監控":"مراقبة ", "連接":"اتصال ", "可以":"يمكن ",
|
||||
"進程":"عملية ", "攔截":"حظر ", "引導":"إقلاع ", "刪除":"حذف ",
|
||||
"分區":"قسم ", "設定":"إعدادات ", "關於":"حول ", "模式":"وضع ",
|
||||
"提高":"تحسين ", "安全":"أمان ", "顯色":"لون ", "顏色":"لون ",
|
||||
"主題":"سمة ", "回收":"استرجاع ", "無用":"غير مفيد ", "導入":"استيراد ",
|
||||
"風險":"خطر ", "介面":"واجهة ", "核心":"نواة ", "服務":"خدمات ",
|
||||
"管理":"إدارة ", "所有":"الكل ", "您":"أنت ", "取消":"إلغاء ", "未":"غير ",
|
||||
"垃圾":"مهملات ", "位元":"بايت ", "本機":"الكمبيوتر ", "停止":"إيقاف ",
|
||||
"到":"إلى ", "白名單":"القائمة البيضاء ", "軟體":"برنامج ", "正在":"",
|
||||
"增加":"إضافة ", "移除":"إزالة ", "項目":"عناصر ", "生成":"إنشاء ", "稱":"",
|
||||
"複製":"نسخ ", "此":"هذا ", "並":"و ", "全盤":"كامل ", "最新":"الأحدث ",
|
||||
"已存在":"موجود بالفعل ", "數據庫":"قاعدة البيانات ", "誤報":"إيجابي كاذب ",
|
||||
"高":"عالي ", "新":"جديد ", "和":"و ", "可能會":"محتمل ", "會":"سوف ",
|
||||
"日誌":"سجل ", "已":"بالفعل ", "彈窗":"نافذة ", "要":"إلى ", "驅動":"برنامج تشغيل ",
|
||||
"的":"", "顯示":"", "個":"", "嗎":"", "了":"", "行":"", "立即":"", "規則":"قاعدة ",
|
||||
"密鑰":"مفتاح ", "檢測":"اكتشاف ", "製作":"صانع ", "運行":"قيد التشغيل ",
|
||||
"擴展":"امتداد ", "套件":"مجموعة ", "自動":"تلقائي ", "上報":"إبلاغ ",
|
||||
"分析":"تحليل ", "自身":"نفسه ", "其他":"آخر ", "配置":"تكوين ",
|
||||
"丟失":"مفقود ", "網站":"موقع ", "不兼容":"غير متوافق ", "列表":"قائمة ",
|
||||
"竄改":"تلاعب ", "第三方":"جهة خارجية ", "實時":"الوقت الفعلي ", "查看":"عرض ",
|
||||
"指定":"تحديد ", "清空":"إفراغ ", "資源回收桶":"سلة المهملات ", "無法":"غير قادر ",
|
||||
"與":"مع ", "當":"عندما ", "功能":"وظيفة ", "異常":"شاذ ", "來":"ل ",
|
||||
"重置":"إعادة ضبط ", "窗口":"نافذة ", "暫存":"مؤقت ", "自訂":"مخصص ",
|
||||
"不支持":"غير مدعوم ", "共":"إجمالي ", "秒":"ثواني ", "耗時":"استغرق ",
|
||||
"沙箱":"صندوق الحماية ", "虛擬":"افتراضي ", "隔離":"عزل ", "時":"",
|
||||
"建立":"إنشاء ", "環境":"بيئة ", "化":"", "表":"",
|
||||
"達":"وصل ", "上限":"الحد ", "區":"", "金鑰":"مفتاح ", "產品":"منتج ",
|
||||
"運行庫":".NET CLR ", "殼碼":"رمز قشرة ", "線程":"سلسلة ", "注入":"حقن ",
|
||||
"前往":"الذهاب ", "檢查":"فحص ", "可用":"متاح ", "靜態":"ثابت ",
|
||||
"版本":"إصدار ", "是":"", "剩餘":"المتبقي ", "遠控":"عن بعد ", " ":" "},
|
||||
|
||||
"russian_switch": {"開啟":"Вкл ", "關閉":"Выкл ", "开启":"Вкл ", "关闭":"Выкл ", ",":", ",
|
||||
"立即刪除":"Удалить сейчас ", "安全模式":"Безопасный режим ", "官方網站":"Официальный сайт ",
|
||||
"腳本":"Скрипт ", "鏈式":"Цепочка ", "記憶體":"Память ", "正在加載":"Загрузка ", "加載":"Загрузить ",
|
||||
"行為":"Поведение ", "可疑":"Подозрительный ", "勒索":"Вымогатель ", "主要":"Главный ",
|
||||
"許可條款":"Условия лицензии ", "惡意":"Вредоносный ", "開發":"Разработчики ",
|
||||
"啟用":"Включить ", "禁用":"Отключить ", "正在初始化中":"Инициализация ", "初始化":"Инициализировать ",
|
||||
"正在掃描":"Сканирование ", "正在刪除":"Удаление ", "正在追蹤":"Отслеживание ",
|
||||
"最小化到":"Свернуть в ", "靈敏度":"Чувствительность ", "正在檢查":"Проверка ",
|
||||
"確定要":"Вы уверены, что ", "引擎靈敏度":"Чувствительность движка ", "更新":"Обновить ",
|
||||
"白色主題":"Белый", "黃色主題":"Желтый", "紅色主題":"Красный", "語言":"Язык ",
|
||||
"藍色主題":"Синий", "綠色主題":"Зеленый", "自訂主題":"Пользовательский", "黑色主題":"Черный",
|
||||
"特別感謝":"Участники ", "版本資訊":"Информация о версии ", "通訊":"Связь ",
|
||||
"註冊":"Реестр ", "退出":"Выход ", "檔案":"Файл ", "當前":"Текущий ",
|
||||
"狀態":"Статус ", "掃描":"Сканировать ", "工具":"Инструменты ", "防護":"Защита ",
|
||||
"失敗":"Ошибка ", "請":"Пожалуйста ", "路徑":"Путь ", "方式":"Метод ",
|
||||
"需要":"Требуется ", "病毒":"Вирус ", "選擇":"Выбрать ", "返回":"Назад",
|
||||
"受到":"Получено ", "錯誤":"Ошибка ", "完成":"Завершено ", "引擎":"Движок ",
|
||||
"托盤":"Трей ", "圖標":"Значок ", "不安全":"Небезопасно ", "增強":"Улучшить ",
|
||||
"系統":"Система ", "雲端":"Облако ", "重啟":"Перезагрузка ", "修復":"Восстановить ",
|
||||
"裝置":"Устройство ", "發現":"Найдено ", "成功":"Успех ", "清理":"Очистка ",
|
||||
"確定":"ОК ", "執行":"Выполнить ", "警告":"Предупреждение ", "使用":"Использовать ",
|
||||
"網路":"Сеть ", "變更":"Изменить ", "總數":"Всего ", "結束":"Завершить ",
|
||||
"選項":"Опции ", "監控":"Монитор ", "連接":"Подключить ", "可以":"Можно ",
|
||||
"進程":"Процесс ", "攔截":"Блокировать ", "引導":"Загрузка ", "刪除":"Удалить ",
|
||||
"分區":"Раздел ", "設定":"Настройки ", "關於":"О программе ", "模式":"Режим ",
|
||||
"提高":"Улучшить ", "安全":"Безопасность ", "顯色":"Цвет ", "顏色":"Цвет ",
|
||||
"主題":"Тема ", "回收":"Корзина ", "無用":"Бесполезно ", "導入":"Импорт ",
|
||||
"風險":"Риск ", "介面":"Интерфейс ", "核心":"Ядро ", "服務":"Службы ",
|
||||
"管理":"Управление ", "所有":"Все ", "您":"Вы ", "取消":"Отмена ", "未":"Не ",
|
||||
"垃圾":"Мусор ", "位元":"байт ", "本機":"ПК ", "停止":"Остановить ",
|
||||
"到":"К ", "白名單":"Белый список ", "軟體":"ПО ", "正在":"",
|
||||
"增加":"Добавить ", "移除":"Удалить ", "項目":"Элементы ", "生成":"Создать ", "稱":"",
|
||||
"複製":"Копировать ", "此":"Этот ", "並":"И ", "全盤":"Полный ", "最新":"Последний ",
|
||||
"已存在":"Уже существует ", "數據庫":"База данных ", "誤報":"Ложное срабатывание ",
|
||||
"高":"Высокий ", "新":"Новый ", "和":"И ", "可能會":"Возможно ", "會":"Может ",
|
||||
"日誌":"Журнал ", "已":"Уже ", "彈窗":"Окно ", "要":"Нужно ", "驅動":"Драйвер ",
|
||||
"的":"", "顯示":"", "個":"", "嗎":"", "了":"", "行":"", "立即":"", "規則":"Правило ",
|
||||
"密鑰":"Ключ ", "檢測":"Обнаружить ", "製作":"Создатель ", "運行":"Работает ",
|
||||
"擴展":"Расширение ", "套件":"Набор ", "自動":"Авто ", "上報":"Отчет ",
|
||||
"分析":"Анализ ", "自身":"Сам ", "其他":"Другое ", "配置":"Конфиг ",
|
||||
"丟失":"Утерян ", "網站":"Сайт ", "不兼容":"Несовместимо ", "列表":"Список ",
|
||||
"竄改":"Вмешательство ", "第三方":"Сторонний ", "實時":"Реальное время ", "查看":"Просмотр ",
|
||||
"指定":"Указать ", "清空":"Очистить ", "資源回收桶":"Корзина ", "無法":"Невозможно ",
|
||||
"與":"С ", "當":"Когда ", "功能":"Функция ", "異常":"Аномалия ", "來":"Для ",
|
||||
"重置":"Сброс ", "窗口":"Окно ", "暫存":"Временный ", "自訂":"Пользовательский ",
|
||||
"不支持":"Не поддерживается ", "共":"Всего ", "秒":"Секунд ", "耗時":"Заняло ",
|
||||
"沙箱":"Песочница ", "虛擬":"Виртуальный ", "隔離":"Карантин ", "時":"",
|
||||
"建立":"Создать ", "環境":"Среда ", "化":"", "表":"",
|
||||
"達":"Достигнут ", "上限":"Лимит ", "區":"", "金鑰":"Ключ ", "產品":"Продукт ",
|
||||
"運行庫":".NET CLR ", "殼碼":"Шелл-код ", "線程":"Поток ", "注入":"Инъекция ",
|
||||
"前往":"Перейти ", "檢查":"Проверить ", "可用":"Доступно ", "靜態":"Статический ",
|
||||
"版本":"Версия ", "是":"", "剩餘":"Осталось ", "遠控":"Удаленный ", " ":" "},
|
||||
|
||||
"slovenian_switch": {"開啟":"Vklop ", "關閉":"Izklop ", "开启":"Vklop ", "关闭":"Izklop ", ",":", ",
|
||||
"立即刪除":"Izbriši zdaj ", "安全模式":"Varni način ", "官方網站":"Uradna spletna stran ",
|
||||
"腳本":"Skripta ", "鏈式":"Veriga ", "記憶體":"Pomnilnik ", "正在加載":"Nalaganje ", "加載":"Naloži ",
|
||||
"行為":"Vedenje ", "可疑":"Sumljivo ", "勒索":"Izsiljevalska ", "主要":"Glavni ",
|
||||
"許可條款":"Licenčni pogoji ", "惡意":"Zlonamerno ", "開發":"Razvijalci ",
|
||||
"啟用":"Omogoči ", "禁用":"Onemogoči ", "正在初始化中":"Inicializacija ", "初始化":"Inicializiraj ",
|
||||
"正在掃描":"Skeniranje ", "正在刪除":"Brisanje ", "正在追蹤":"Sledenje ",
|
||||
"最小化到":"Minimiziraj v ", "靈敏度":"Občutljivost ", "正在檢查":"Preverjanje ",
|
||||
"確定要":"Ali ste prepričani ", "引擎靈敏度":"Občutljivost mehanizma ", "更新":"Posodobi ",
|
||||
"白色主題":"Bela", "黃色主題":"Rumena", "紅色主題":"Rdeča", "語言":"Jezik ",
|
||||
"藍色主題":"Modra", "綠色主題":"Zelena", "自訂主題":"Po meri", "黑色主題":"Črna",
|
||||
"特別感謝":"Sodelavci ", "版本資訊":"Informacije o različici ", "通訊":"Povezava ",
|
||||
"註冊":"Register ", "退出":"Izhod ", "檔案":"Datoteka ", "當前":"Trenutno ",
|
||||
"狀態":"Stanje ", "掃描":"Skeniraj ", "工具":"Orodja ", "防護":"Zaščita ",
|
||||
"失敗":"Neuspešno ", "請":"Prosim ", "路徑":"Pot ", "方式":"Način ",
|
||||
"需要":"Potrebno ", "病毒":"Virus ", "選擇":"Izberi ", "返回":"Nazaj",
|
||||
"受到":"Prejeto ", "錯誤":"Napaka ", "完成":"Končano ", "引擎":"Mehanizem ",
|
||||
"托盤":"Pladenj ", "圖標":"Ikona ", "不安全":"Nevarno ", "增強":"Izboljšaj ",
|
||||
"系統":"Sistem ", "雲端":"Oblak ", "重啟":"Ponovni zagon ", "修復":"Popravi ",
|
||||
"裝置":"Naprava ", "發現":"Najdeno ", "成功":"Uspeh ", "清理":"Čiščenje ",
|
||||
"確定":"V redu ", "執行":"Izvedi ", "警告":"Opozorilo ", "使用":"Uporabi ",
|
||||
"網路":"Omrežje ", "變更":"Spremeni ", "總數":"Skupaj ", "結束":"Konec ",
|
||||
"選項":"Možnosti ", "監控":"Nadzor ", "連接":"Poveži ", "可以":"Lahko ",
|
||||
"進程":"Proces ", "攔截":"Blokiraj ", "引導":"Zagon ", "刪除":"Izbriši ",
|
||||
"分區":"Particija ", "設定":"Nastavitve ", "關於":"O programu ", "模式":"Način ",
|
||||
"提高":"Povečaj ", "安全":"Varnost ", "顯色":"Barva ", "顏色":"Barva ",
|
||||
"主題":"Tema ", "回收":"Koš ", "無用":"Nekoristno ", "導入":"Uvozi ",
|
||||
"風險":"Tveganje ", "介面":"Vmesnik ", "核心":"Jedro ", "服務":"Storitve ",
|
||||
"管理":"Upravljaj ", "所有":"Vse ", "您":"Vi ", "取消":"Prekliči ", "未":"Ne ",
|
||||
"垃圾":"Smeti ", "位元":"bajtov ", "本機":"PC ", "停止":"Ustavi ",
|
||||
"到":"V ", "白名單":"Beli seznam ", "軟體":"Programska oprema ", "正在":"",
|
||||
"增加":"Dodaj ", "移除":"Odstrani ", "項目":"Predmeti ", "生成":"Ustvari ", "稱":"",
|
||||
"複製":"Kopiraj ", "此":"To ", "並":"In ", "全盤":"Popolno ", "最新":"Najnovejše ",
|
||||
"已存在":"Že obstaja ", "數據庫":"Podatkovna baza ", "誤報":"Lažni alarm ",
|
||||
"高":"Visoko ", "新":"Novo ", "和":"In ", "可能會":"Možno ", "會":"Bo ",
|
||||
"日誌":"Dnevnik ", "已":"Že ", "彈窗":"Okno ", "要":"V ", "驅動":"Gonilnik ",
|
||||
"的":"", "顯示":"", "個":"", "嗎":"", "了":"", "行":"", "立即":"", "規則":"Pravilo ",
|
||||
"密鑰":"Ključ ", "檢測":"Zaznaj ", "製作":"Ustvarjalec ", "運行":"Se izvaja ",
|
||||
"擴展":"Razširitev ", "套件":"Komplet ", "自動":"Samodejno ", "上報":"Prijavi ",
|
||||
"分析":"Analiza ", "自身":"Samo ", "其他":"Drugo ", "配置":"Konfiguracija ",
|
||||
"丟失":"Izgubljeno ", "網站":"Spletno mesto ", "不兼容":"Nezdružljivo ", "列表":"Seznam ",
|
||||
"竄改":"Spreminjanje ", "第三方":"Tretja oseba ", "實時":"V realnem času ", "查看":"Poglej ",
|
||||
"指定":"Določi ", "清空":"Izprazni ", "資源回收桶":"Koš ", "無法":"Ni mogoče ",
|
||||
"與":"Z ", "當":"Ko ", "功能":"Funkcija ", "異常":"Nenormalno ", "來":"Za ",
|
||||
"重置":"Ponastavi ", "窗口":"Okno ", "暫存":"Začasno ", "自訂":"Po meri ",
|
||||
"不支持":"Ni podprto ", "共":"Skupaj ", "秒":"Sekund ", "耗時":"Trajalo ",
|
||||
"沙箱":"Peskovnik ", "虛擬":"Virtualno ", "隔離":"Karantena ", "時":"",
|
||||
"建立":"Ustvari ", "環境":"Okolje ", "化":"", "表":"",
|
||||
"達":"Doseženo ", "上限":"Omejitev ", "區":"", "金鑰":"Ključ ", "產品":"Izdelek ",
|
||||
"運行庫":".NET CLR ", "殼碼":"Zlonamerna koda ", "線程":"Nit ", "注入":"Vbrizgavanje ",
|
||||
"前往":"Pojdi ", "檢查":"Preveri ", "可用":"Na voljo ", "靜態":"Statično ",
|
||||
"版本":"Različica ", "是":"", "剩餘":"Preostalo ", "遠控":"Oddaljeno ", " ":" "},
|
||||
|
||||
"custom_language_switch": {},
|
||||
|
||||
"white_switch": {
|
||||
"_white": "_black",},
|
||||
|
||||
+521
-246
@@ -1,4 +1,4 @@
|
||||
import os, yara, time, numpy, base64, requests, pefile, math
|
||||
import os, yara, time, numpy, base64, requests, pefile, math, json, re, datetime
|
||||
import ctypes, ctypes.wintypes, hashlib, onnxruntime
|
||||
|
||||
from PIL import Image
|
||||
@@ -11,14 +11,25 @@ class GUID(ctypes.Structure):
|
||||
("Data1", ctypes.wintypes.DWORD),
|
||||
("Data2", ctypes.wintypes.WORD),
|
||||
("Data3", ctypes.wintypes.WORD),
|
||||
("Data4", ctypes.c_ubyte * 8)]
|
||||
("Data4", ctypes.c_ubyte * 8)
|
||||
]
|
||||
|
||||
class WINTRUST_FILE_INFO(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cbStruct", ctypes.wintypes.DWORD),
|
||||
("pcwszFilePath", ctypes.wintypes.LPCWSTR),
|
||||
("hFile", ctypes.wintypes.HANDLE),
|
||||
("pgKnownSubject", ctypes.wintypes.LPVOID)]
|
||||
("pgKnownSubject", ctypes.wintypes.LPVOID)
|
||||
]
|
||||
|
||||
class WINTRUST_DATA_UNION(ctypes.Union):
|
||||
_fields_ = [
|
||||
("pFile", ctypes.POINTER(WINTRUST_FILE_INFO)),
|
||||
("pCatalog", ctypes.wintypes.LPVOID),
|
||||
("pBlob", ctypes.wintypes.LPVOID),
|
||||
("pSgnr", ctypes.wintypes.LPVOID),
|
||||
("pCert", ctypes.wintypes.LPVOID)
|
||||
]
|
||||
|
||||
class WINTRUST_DATA(ctypes.Structure):
|
||||
_fields_ = [
|
||||
@@ -28,19 +39,20 @@ class WINTRUST_DATA(ctypes.Structure):
|
||||
("dwUIChoice", ctypes.wintypes.DWORD),
|
||||
("fdwRevocationChecks", ctypes.wintypes.DWORD),
|
||||
("dwUnionChoice", ctypes.wintypes.DWORD),
|
||||
("pFile", ctypes.POINTER(WINTRUST_FILE_INFO)),
|
||||
("u", WINTRUST_DATA_UNION),
|
||||
("dwStateAction", ctypes.wintypes.DWORD),
|
||||
("hWVTStateData", ctypes.wintypes.HANDLE),
|
||||
("pwszURLReference", ctypes.wintypes.LPCWSTR),
|
||||
("dwProvFlags", ctypes.wintypes.DWORD),
|
||||
("dwUIContext", ctypes.wintypes.DWORD),
|
||||
("pSignatureSettings", ctypes.wintypes.LPVOID)]
|
||||
("pSignatureSettings", ctypes.wintypes.LPVOID)
|
||||
]
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class sign_scanner:
|
||||
def __init__(self):
|
||||
self.verify = GUID(0x00AAC56B, 0xCD44, 0x11D0, (0x8C, 0xC2, 0x00, 0xC0, 0x4F, 0xC2, 0x95, 0xEE))
|
||||
self.verify = GUID(0x00AAC56B, 0xCD44, 0x11D0, (ctypes.c_ubyte * 8)(0x8C, 0xC2, 0x00, 0xC0, 0x4F, 0xC2, 0x95, 0xEE))
|
||||
|
||||
def init_windll(self, path):
|
||||
for name in path:
|
||||
@@ -48,7 +60,6 @@ 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
|
||||
@@ -56,15 +67,34 @@ class sign_scanner:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def sign_verify(self, file_path):
|
||||
if os.name != 'nt':
|
||||
return False
|
||||
try:
|
||||
fi = WINTRUST_FILE_INFO(ctypes.sizeof(WINTRUST_FILE_INFO), file_path, None, None)
|
||||
data = WINTRUST_DATA(ctypes.sizeof(WINTRUST_DATA), None, None, 2, 0, 1,
|
||||
ctypes.pointer(fi), 1, None, None, 0, 0, None)
|
||||
s = self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(data))
|
||||
data.dwStateAction = 2
|
||||
self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(data))
|
||||
return s == 0
|
||||
fi = WINTRUST_FILE_INFO()
|
||||
fi.cbStruct = ctypes.sizeof(WINTRUST_FILE_INFO)
|
||||
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
|
||||
td.pSIPClientData = None
|
||||
td.dwUIChoice = 2
|
||||
td.fdwRevocationChecks = 0
|
||||
td.dwUnionChoice = 1
|
||||
td.u = wt_union
|
||||
td.dwStateAction = 0
|
||||
td.hWVTStateData = None
|
||||
td.pwszURLReference = None
|
||||
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
|
||||
|
||||
@@ -243,101 +273,214 @@ class cnn_scanner:
|
||||
####################################################################################################
|
||||
|
||||
class pe_scanner:
|
||||
API_CAT_MAPPING = {
|
||||
'ProcessControl': {
|
||||
'CreateProcessA', 'CreateProcessW', 'WinExec', 'ShellExecuteA', 'ShellExecuteW',
|
||||
'ShellExecuteExW', 'ExitProcess', 'TerminateProcess', 'OpenProcess', 'GetExitCodeProcess',
|
||||
'SetThreadPriority', 'GetThreadPriority', 'GetCurrentProcess', 'GetCurrentProcessId',
|
||||
'GetModuleFileNameA', 'GetCommandLineA', 'GetModuleFileNameW', 'GetStartupInfoW'
|
||||
},
|
||||
'Injection': {
|
||||
'VirtualAlloc', 'VirtualAllocEx', 'VirtualProtect', 'VirtualProtectEx', 'WriteProcessMemory',
|
||||
'ReadProcessMemory', 'CreateRemoteThread', 'QueueUserAPC', 'SetThreadContext', 'GetThreadContext',
|
||||
'Wow64SetThreadContext', 'NtUnmapViewOfSection', 'ZwUnmapViewOfSection', 'RtlCreateUserThread',
|
||||
'SetWindowsHookExA', 'SetWindowsHookExW', 'UnhookWindowsHookEx', 'LoadLibraryA', 'LoadLibraryW',
|
||||
'LoadLibraryExA', 'LoadLibraryExW', 'GetProcAddress', 'GetModuleHandleA', 'GetModuleHandleW',
|
||||
'FreeLibrary', 'CreateFileMappingA', 'CreateFileMappingW', 'MapViewOfFile', 'UnmapViewOfSection',
|
||||
'VirtualFree', 'VirtualQuery'
|
||||
},
|
||||
'Synchronization': {
|
||||
'WaitForSingleObject', 'WaitForSingleObjectEx', 'WaitForMultipleObjects', 'WaitForMultipleObjectsEx',
|
||||
'CreateMutexA', 'CreateMutexW', 'OpenMutexW', 'ReleaseMutex', 'CreateEventA', 'CreateEventW',
|
||||
'OpenEventW', 'SetEvent', 'ResetEvent', 'EnterCriticalSection', 'LeaveCriticalSection',
|
||||
'InitializeCriticalSection', 'DeleteCriticalSection', 'Sleep', 'SleepEx',
|
||||
'InitializeCriticalSectionAndSpinCount', 'InterlockedDecrement', 'InterlockedIncrement',
|
||||
'InterlockedExchange', 'InterlockedCompareExchange', 'SetTimer', 'KillTimer'
|
||||
},
|
||||
'MultiThreading': {
|
||||
'CreateThread', 'ResumeThread', 'SuspendThread', 'ExitThread', 'TerminateThread',
|
||||
'GetCurrentThread', 'GetCurrentThreadId', 'TlsAlloc', 'TlsSetValue', 'TlsGetValue',
|
||||
'CreateThreadpoolWork', 'SubmitThreadpoolWork', 'TlsFree'
|
||||
},
|
||||
'Network': {
|
||||
'socket', 'connect', 'send', 'recv', 'bind', 'listen', 'accept', 'gethostbyname', 'getaddrinfo',
|
||||
'WSAStartup', 'WSACleanup', 'WSAIoctl', 'WSASocketA', 'WSASocketW', 'closesocket', 'htons',
|
||||
'InternetOpenA', 'InternetOpenW', 'InternetConnectA', 'InternetConnectW', 'InternetOpenUrlA',
|
||||
'InternetOpenUrlW', 'HttpOpenRequestA', 'HttpOpenRequestW', 'HttpSendRequestA', 'HttpSendRequestW',
|
||||
'InternetReadFile', 'URLDownloadToFileA', 'URLDownloadToFileW', 'DnsQuery_A', 'DnsQuery_W'
|
||||
},
|
||||
'Encryption': {
|
||||
'CryptAcquireContextA', 'CryptAcquireContextW', 'CryptCreateHash', 'CryptHashData',
|
||||
'CryptDeriveKey', 'CryptEncrypt', 'CryptDecrypt', 'CryptDestroyKey', 'CryptDestroyHash',
|
||||
'CryptReleaseContext', 'CryptGenKey', 'CryptImportKey', 'CryptExportKey'
|
||||
},
|
||||
'DataObfuscation': {
|
||||
'RtlDecompressBuffer', 'MultiByteToWideChar', 'WideCharToMultiByte', 'Base64Decode',
|
||||
'CryptDecodeObject', 'IsDBCSLeadByte', 'CharUpperA', 'CharLowerA',
|
||||
'GetStringTypeW', 'LCMapStringW', 'IsValidCodePage', 'DecodePointer', 'EncodePointer'
|
||||
},
|
||||
'FileIO': {
|
||||
'CreateFileA', 'CreateFileW', 'WriteFile', 'ReadFile', 'DeleteFileA', 'DeleteFileW',
|
||||
'CopyFileA', 'CopyFileW', 'MoveFileA', 'MoveFileW', 'FindFirstFileA', 'FindNextFileA',
|
||||
'GetTempPathA', 'GetTempPathW', 'GetTempFileNameA', 'GetTempFileNameW', 'SetFileAttributesA',
|
||||
'SetFileAttributesW', 'DeviceIoControl', 'SetFileTime', 'GetFileSize', 'GetFileSizeEx',
|
||||
'SetFilePointer', 'FlushFileBuffers', 'ConnectNamedPipe', 'PeekNamedPipe',
|
||||
'CloseHandle', 'GetFileType', 'SetStdHandle', 'SetFilePointerEx', 'FindClose',
|
||||
'SetHandleCount', 'SetEndOfFile', 'GetFullPathNameA', 'GetFileAttributesA'
|
||||
},
|
||||
'Registry': {
|
||||
'RegOpenKeyExA', 'RegOpenKeyExW', 'RegSetValueExA', 'RegSetValueExW',
|
||||
'RegCreateKeyExA', 'RegCreateKeyExW', 'RegDeleteKeyA', 'RegDeleteKeyW',
|
||||
'RegEnumValueA', 'RegEnumValueW', 'RegQueryValueExA', 'RegQueryValueExW',
|
||||
'RegDeleteValueA', 'RegDeleteValueW', 'RegCloseKey'
|
||||
},
|
||||
'Services': {
|
||||
'OpenSCManagerA', 'OpenSCManagerW', 'CreateServiceA', 'CreateServiceW',
|
||||
'StartServiceA', 'StartServiceW', 'ControlService', 'DeleteService'
|
||||
},
|
||||
'Privileges': {
|
||||
'AdjustTokenPrivileges', 'LookupPrivilegeValueA', 'LookupPrivilegeValueW',
|
||||
'OpenProcessToken', 'NetUserAdd', 'NetLocalGroupAddMembers', 'IsAdmin',
|
||||
'GetUserNameA', 'GetUserNameW'
|
||||
},
|
||||
'Native': {
|
||||
'RtlUnwind', 'RtlVirtualUnwind', 'RtlCaptureContext', 'RtlLookupFunctionEntry',
|
||||
'NtClose', 'NtQueryInformationProcess', 'RtlAllocateHeap', 'RtlFreeHeap',
|
||||
'GetSystemTimeAsFileTime', 'GetLocalTime', 'GlobalMemoryStatus',
|
||||
'GetVersionExA', 'GetVersionExW', 'GetComputerNameA', 'GetComputerNameW',
|
||||
'GetLastError', 'GetACP', 'RaiseException', 'SetLastError', 'GetEnvironmentStringsW',
|
||||
'FreeEnvironmentStringsW', 'GetLocaleInfoA', 'lstrlenA', 'GetVersion', 'GetSystemInfo',
|
||||
'CompareStringA'
|
||||
},
|
||||
'DotNet': {
|
||||
'_CorExeMain', '_CorDllMain'
|
||||
},
|
||||
'AntiDebug': {
|
||||
'IsDebuggerPresent', 'CheckRemoteDebuggerPresent', 'OutputDebugStringA', 'OutputDebugStringW',
|
||||
'GetTickCount', 'GetTickCount64', 'QueryPerformanceCounter', 'FindWindowA', 'FindWindowW',
|
||||
'EnumWindows', 'EnumChildWindows', 'GetWindowRect', 'GetClientRect', 'SetWindowDisplayAffinity',
|
||||
'UnhandledExceptionFilter', 'SetUnhandledExceptionFilter', 'IsProcessorFeaturePresent',
|
||||
'SetErrorMode', 'GetTimeZoneInformation', 'GetDriveTypeW', 'GetDiskFreeSpaceA'
|
||||
},
|
||||
'Keylogging': {
|
||||
'GetAsyncKeyState', 'GetKeyState', 'GetKeyboardState', 'MapVirtualKeyA', 'MapVirtualKeyW',
|
||||
'ToAscii', 'ToAsciiEx', 'ToUnicode', 'ToUnicodeEx', 'GetKeyNameTextA', 'GetForegroundWindow',
|
||||
'KeybdEvent', 'SendInput', 'MapVirtualKeyExA', 'MapVirtualKeyExW', 'CallNextHookEx'
|
||||
},
|
||||
'Input': {
|
||||
'GetCursorPos', 'SetCursorPos', 'MouseEvent', 'GetDoubleClickTime',
|
||||
'GetCapture', 'SetCapture'
|
||||
},
|
||||
'ScreenCapture': {
|
||||
'BitBlt', 'StretchBlt', 'GetDC', 'GetWindowDC', 'CreateCompatibleDC', 'CreateCompatibleBitmap',
|
||||
'GdipSaveImageToFile', 'PrintWindow', 'GetDesktopWindow', 'CreateDCW', 'CreateDCA',
|
||||
'SelectObject', 'DeleteDC', 'DeleteObject', 'GetDeviceCaps', 'GetSystemMetrics'
|
||||
},
|
||||
'Graphics': {
|
||||
'Direct3DCreate9', 'D3D11CreateDevice'
|
||||
},
|
||||
'Audio': {
|
||||
'WaveInOpen', 'WaveInClose', 'WaveInStart', 'WaveInStop'
|
||||
},
|
||||
'Clipboard': {
|
||||
'OpenClipboard', 'CloseClipboard', 'GetClipboardData', 'SetClipboardData'
|
||||
},
|
||||
'Camera': {
|
||||
'CapCreateCaptureWindowA', 'CapCreateCaptureWindowW'
|
||||
},
|
||||
'Memory': {
|
||||
'HeapAlloc', 'HeapFree', 'HeapSize', 'HeapCreate', 'GetProcessHeap',
|
||||
'LocalAlloc', 'LocalFree', 'GlobalAlloc', 'GlobalFree', 'GlobalUnlock',
|
||||
'HeapDestroy', 'GlobalLock', 'SysFreeString', 'SysAllocStringLen'
|
||||
},
|
||||
'Resource': {
|
||||
'LoadResource', 'SizeofResource', 'LockResource', 'FreeResource', 'FindResourceA'
|
||||
},
|
||||
'WindowControl': {
|
||||
'ShowWindow', 'DestroyWindow', 'TranslateMessage', 'DispatchMessageA', 'GetWindow',
|
||||
'PeekMessageA', 'GetWindowLongA', 'CallWindowProcA', 'SetWindowLongA', 'GetWindowTextA',
|
||||
'ScreenToClient', 'GetActiveWindow', 'CreateWindowExA', 'DefWindowProcA', 'GetMessageA',
|
||||
'RegisterClassA', 'UnregisterClassA', 'MessageBoxA'
|
||||
},
|
||||
'COM': {
|
||||
'CoCreateInstance', 'CoUninitialize', 'CoInitialize'
|
||||
}
|
||||
}
|
||||
|
||||
TARGET_DLLS = {
|
||||
'kernel32.dll', 'user32.dll', 'gdi32.dll', 'advapi32.dll', 'shell32.dll', 'ntdll.dll', 'hal.dll',
|
||||
'ws2_32.dll', 'wsock32.dll', 'wininet.dll', 'winhttp.dll', 'urlmon.dll', 'crypt32.dll', 'bcrypt.dll',
|
||||
'psapi.dll', 'dbghelp.dll', 'imagehlp.dll', 'shlwapi.dll', 'ole32.dll', 'oleaut32.dll', 'comctl32.dll',
|
||||
'mscoree.dll', 'vbscript.dll', 'netapi32.dll', 'iphlpapi.dll', 'wtsapi32.dll', 'version.dll', 'winmm.dll'
|
||||
}
|
||||
|
||||
BASE_SCHEMA = [
|
||||
"FileEntropy", "SectionMeanEntropy", "IsExe", "IsDll", "IsDriver", "Is64Bit",
|
||||
"Machine", "Magic", "CheckSum", "ImageBase", "SizeOfImage", "SizeOfHeaders",
|
||||
"Characteristics", "DllCharacteristics", "Subsystem", "LoaderFlags",
|
||||
"MajorLinkerVersion", "MinorLinkerVersion", "SizeOfCode",
|
||||
"SizeOfInitializedData", "SizeOfUninitializedData", "AddressOfEntryPoint",
|
||||
"BaseOfCode", "SectionAlignment", "FileAlignment",
|
||||
"MajorOperatingSystemVersion", "MinorOperatingSystemVersion",
|
||||
"MajorImageVersion", "MinorImageVersion",
|
||||
"MajorSubsystemVersion", "MinorSubsystemVersion",
|
||||
"SizeOfStackReserve", "SizeOfStackCommit", "SizeOfHeapReserve", "SizeOfHeapCommit",
|
||||
"NumberOfSections", "NumberOfRvaAndSizes", "PointerToSymbolTable", "NumberOfSymbols",
|
||||
"SizeOfOptionalHeader",
|
||||
"TextSectionMaxEntropy", "TextSectionMeanEntropy", "TextSizeRatio",
|
||||
"DataSectionMaxEntropy", "DataSectionMeanEntropy", "DataSizeRatio",
|
||||
"RsrcSectionMaxEntropy", "RsrcSectionMeanEntropy", "RsrcSizeRatio",
|
||||
"SectionCount", "ExecutableSections",
|
||||
"WritableSections", "ReadableSections", "SectionException",
|
||||
"Char_00000020_Count", "Char_00000020_MeanEntropy",
|
||||
"Char_00000040_Count", "Char_00000040_MeanEntropy",
|
||||
"Char_00000080_Count", "Char_00000080_MeanEntropy",
|
||||
"Char_02000000_Count", "Char_02000000_MeanEntropy",
|
||||
"Char_20000000_Count", "Char_20000000_MeanEntropy",
|
||||
"Char_40000000_Count", "Char_40000000_MeanEntropy",
|
||||
"Char_80000000_Count", "Char_80000000_MeanEntropy",
|
||||
"IconCount", "ApiCount", "ExportCount", "DebugCount", "ExceptionCount",
|
||||
"ImportCount", "ImportFunctionCount",
|
||||
"FileDescriptionLength", "FileVersionLength", "ProductNameLength",
|
||||
"ProductVersionLength", "CompanyNameLength", "LegalCopyrightLength",
|
||||
"CommentsLength", "InternalNameLength", "LegalTrademarksLength",
|
||||
"SpecialBuildLength", "PrivateBuildLength",
|
||||
"TrustSigned", "IsDebug", "IsPatched", "IsPrivateBuild", "IsPreRelease",
|
||||
"IsSpecialBuild", "IsAdmin", "IsInstall", "HasTlsCallbacks",
|
||||
"HasInvalidTimestamp", "HasRelocationDirectory", "HasPacked", "FileTimeException"
|
||||
]
|
||||
|
||||
CAT_SCHEMA = [f"Cat_{k}" for k in sorted(API_CAT_MAPPING.keys())]
|
||||
DLL_SCHEMA = [f"Dll_{d.replace('.', '_')}" for d in sorted(TARGET_DLLS)]
|
||||
|
||||
_ALL_APIS = {api for apis in API_CAT_MAPPING.values() for api in apis}
|
||||
API_SCHEMA = [f"Api_{api}" for api in sorted(_ALL_APIS)]
|
||||
SCHEMA = BASE_SCHEMA + CAT_SCHEMA + DLL_SCHEMA + API_SCHEMA
|
||||
|
||||
_API_TO_CATS = {}
|
||||
for _cat, _apis in API_CAT_MAPPING.items():
|
||||
for _api in _apis:
|
||||
if _api not in _API_TO_CATS:
|
||||
_API_TO_CATS[_api] = []
|
||||
_API_TO_CATS[_api].append(_cat)
|
||||
|
||||
PACKERS = {
|
||||
'upx', 'aspack', 'asprotect', 'pecompact', 'upack', 'fsg', 'mew',
|
||||
'mpress', 'ezip', 'pklt', 'shrink', 'petite', 'telock',
|
||||
'themida', 'winlicense', 'tmd', 'vmp', 'enigma', 'obsidium',
|
||||
'pelock', 'exestealth', 'yoda', 'armadillo', 'zprotect',
|
||||
'sforce', 'starforce', 'qihoo', 'wisevec', 'megastop',
|
||||
}
|
||||
INSTALLER_SIGS = [b"Nullsoft.NSIS", b"Inno Setup", b"7-Zip.7zip", b"InstallShield"]
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def __init__(self):
|
||||
self.model = None
|
||||
self.input_name = None
|
||||
self.feature_order = []
|
||||
self.signer = sign_scanner()
|
||||
self.signer.init_windll(["wintrust"])
|
||||
self.SCHEMA = [
|
||||
"FileEntropy", "IsExe", "IsDll", "IsDriver", "Is64Bit", "Machine", "Magic",
|
||||
"TimeDateStamp", "CheckSum", "ImageBase", "SizeOfImage", "SizeOfHeaders",
|
||||
"Characteristics", "DllCharacteristics", "Subsystem", "LoaderFlags",
|
||||
"MajorLinkerVersion", "MinorLinkerVersion", "SizeOfCode",
|
||||
"SizeOfInitializedData", "SizeOfUninitializedData", "AddressOfEntryPoint",
|
||||
"BaseOfCode", "SectionAlignment", "FileAlignment",
|
||||
"MajorOperatingSystemVersion", "MinorOperatingSystemVersion",
|
||||
"MajorImageVersion", "MinorImageVersion",
|
||||
"MajorSubsystemVersion", "MinorSubsystemVersion",
|
||||
"SizeOfStackReserve", "SizeOfStackCommit", "SizeOfHeapReserve", "SizeOfHeapCommit",
|
||||
"NumberOfSections", "NumberOfRvaAndSizes", "PointerToSymbolTable", "NumberOfSymbols",
|
||||
"SizeOfOptionalHeader",
|
||||
"TextSection", "TextSizeRatio", "DataSection", "DataSizeRatio",
|
||||
"RsrcSection", "RsrcSizeRatio", "SectionCount", "ExecutableSections",
|
||||
"WritableSections", "ReadableSections", "SectionException",
|
||||
"IconCount", "ApiCount", "ExportCount", "DebugCount", "ExceptionCount",
|
||||
"FileDescriptionLength", "FileVersionLength", "ProductNameLength",
|
||||
"ProductVersionLength", "CompanyNameLength", "LegalCopyrightLength",
|
||||
"CommentsLength", "InternalNameLength", "LegalTrademarksLength",
|
||||
"SpecialBuildLength", "PrivateBuildLength",
|
||||
"TrustSigned", "IsDebug", "IsPatched", "IsPrivateBuild", "IsPreRelease",
|
||||
"IsSpecialBuild", "IsAdmin", "IsInstall", "HasTlsCallbacks",
|
||||
"HasInvalidTimestamp", "HasRelocationDirectory", "HasPacked", "FileTimeException",
|
||||
"_CorExeMain", "_CorDllMain",
|
||||
"GetDC", "CreateDCW", "CreateDCA", "BitBlt", "StretchBlt", "CreateCompatibleDC",
|
||||
"CreateCompatibleBitmap", "SelectObject", "DeleteDC", "DeleteObject", "GetDeviceCaps",
|
||||
"GetSystemMetrics",
|
||||
"SetWindowsHookExA", "SetWindowsHookExW", "UnhookWindowsHookEx",
|
||||
"GetAsyncKeyState", "GetKeyState", "GetKeyboardState",
|
||||
"MapVirtualKeyA", "MapVirtualKeyW", "MapVirtualKeyExA", "MapVirtualKeyExW",
|
||||
"ToAscii", "ToAsciiEx", "ToUnicode", "ToUnicodeEx", "KeybdEvent", "SendInput",
|
||||
"GetCursorPos", "SetCursorPos", "MouseEvent", "GetDoubleClickTime",
|
||||
"GetCapture", "SetCapture",
|
||||
"WaveInOpen", "WaveInClose", "WaveInStart", "WaveInStop",
|
||||
"CapCreateCaptureWindowA", "CapCreateCaptureWindowW",
|
||||
"OpenClipboard", "CloseClipboard", "GetClipboardData", "SetClipboardData",
|
||||
"GetDesktopWindow", "GetForegroundWindow", "GetWindowDC", "GetWindowRect",
|
||||
"GetClientRect", "PrintWindow", "SetWindowDisplayAffinity", "FindWindowA", "FindWindowW",
|
||||
"EnumWindows", "EnumChildWindows",
|
||||
"Direct3DCreate9", "D3D11CreateDevice", "GdipSaveImageToFile",
|
||||
"CreateProcessA", "CreateProcessW", "CreateThread", "ResumeThread", "SuspendThread",
|
||||
"OpenProcess", "TerminateProcess", "GetCurrentProcess", "GetCurrentProcessId",
|
||||
"ExitProcess", "WinExec", "ShellExecuteA", "ShellExecuteW",
|
||||
"CreateRemoteThread", "QueueUserAPC", "VirtualAlloc", "VirtualAllocEx",
|
||||
"VirtualProtect", "VirtualProtectEx", "WriteProcessMemory", "ReadProcessMemory",
|
||||
"NtUnmapViewOfSection", "SetThreadContext", "GetThreadContext", "Wow64SetThreadContext",
|
||||
"ZwUnmapViewOfSection", "CreateFileMappingA", "CreateFileMappingW", "MapViewOfFile",
|
||||
"UnmapViewOfSection",
|
||||
"LoadLibraryA", "LoadLibraryW", "LoadLibraryExA", "LoadLibraryExW",
|
||||
"GetProcAddress", "GetModuleHandleA", "GetModuleHandleW", "FreeLibrary",
|
||||
"CreateFileA", "CreateFileW", "WriteFile", "ReadFile", "DeleteFileA", "DeleteFileW",
|
||||
"CopyFileA", "CopyFileW", "MoveFileA", "MoveFileW", "FindFirstFileA", "FindNextFileA",
|
||||
"GetTempPathA", "GetTempPathW", "GetTempFileNameA", "GetTempFileNameW",
|
||||
"SetFileAttributesA", "SetFileAttributesW", "DeviceIoControl", "SetFileTime",
|
||||
"RegOpenKeyExA", "RegOpenKeyExW", "RegSetValueExA", "RegSetValueExW",
|
||||
"RegCreateKeyExA", "RegCreateKeyExW", "RegDeleteKeyA", "RegDeleteKeyW",
|
||||
"RegEnumValueA", "RegEnumValueW", "RegQueryValueExA", "RegQueryValueExW",
|
||||
"RegDeleteValueA", "RegDeleteValueW",
|
||||
"OpenSCManagerA", "OpenSCManagerW", "CreateServiceA", "CreateServiceW",
|
||||
"StartServiceA", "StartServiceW", "ControlService", "DeleteService",
|
||||
"AdjustTokenPrivileges", "LookupPrivilegeValueA", "LookupPrivilegeValueW",
|
||||
"OpenProcessToken", "NetUserAdd", "NetLocalGroupAddMembers",
|
||||
"Socket", "Connect", "Send", "Recv", "WSAStartup", "gethostbyname", "getaddrinfo",
|
||||
"WSAIoctl", "WSASocketA", "WSASocketW",
|
||||
"URLDownloadToFileA", "URLDownloadToFileW",
|
||||
"InternetOpenA", "InternetOpenW", "InternetConnectA", "InternetConnectW",
|
||||
"InternetOpenUrlA", "InternetOpenUrlW",
|
||||
"HttpOpenRequestA", "HttpOpenRequestW", "HttpSendRequestA", "HttpSendRequestW",
|
||||
"InternetReadFile", "DnsQuery_A", "DnsQuery_W",
|
||||
"IsDebuggerPresent", "CheckRemoteDebuggerPresent", "OutputDebugStringA", "OutputDebugStringW",
|
||||
"GetTickCount", "QueryPerformanceCounter", "Sleep", "GetSystemTimeAsFileTime",
|
||||
"GetLocalTime", "GlobalMemoryStatus", "GetVersionExA", "GetVersionExW",
|
||||
"GetComputerNameA", "GetComputerNameW", "GetUserNameA", "GetUserNameW",
|
||||
"CryptAcquireContextA", "CryptAcquireContextW", "CryptCreateHash", "CryptHashData",
|
||||
"CryptDeriveKey", "CryptEncrypt", "CryptDecrypt", "CryptDestroyKey", "CryptDestroyHash",
|
||||
"CryptReleaseContext", "CryptGenKey", "CryptImportKey", "CryptExportKey",
|
||||
"RtlDecompressBuffer", "ConnectNamedPipe", "PeekNamedPipe"
|
||||
]
|
||||
self.PACKERS = {
|
||||
'upx', 'aspack', 'asprotect', 'pecompact', 'upack', 'fsg', 'mew',
|
||||
'mpress', 'ezip', 'pklt', 'shrink', 'petite', 'telock',
|
||||
'themida', 'winlicense', 'tmd', 'vmp', 'enigma', 'obsidium',
|
||||
'pelock', 'exestealth', 'yoda', 'armadillo', 'zprotect',
|
||||
'sforce', 'starforce', 'qihoo', 'wisevec', 'megastop',
|
||||
}
|
||||
self.INSTALLER_SIGS = [b"Nullsoft.NSIS", b"Inno Setup", b"7-Zip.7zip", b"InstallShield"]
|
||||
self.SCHEMA_SET = set(self.SCHEMA)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def load_path(self, path, callback=None):
|
||||
for root, _, files in os.walk(path):
|
||||
@@ -345,14 +488,26 @@ class pe_scanner:
|
||||
full_path = os.path.join(root, file)
|
||||
if callback:
|
||||
callback(full_path)
|
||||
self.load_model(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) or not model_path.endswith('.onnx'):
|
||||
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")
|
||||
|
||||
model_features = []
|
||||
if os.path.exists(feat_path):
|
||||
with open(feat_path, 'r') as f:
|
||||
model_features = json.load(f)
|
||||
else:
|
||||
model_features = [re.sub(r'[^A-Za-z0-9_]+', '', f) for f in self.SCHEMA]
|
||||
|
||||
local_map = {re.sub(r'[^A-Za-z0-9_]+', '', raw_name): raw_name for raw_name in self.SCHEMA}
|
||||
self.feature_order = [local_map.get(req_feat) for req_feat in model_features]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -363,9 +518,12 @@ class pe_scanner:
|
||||
f = float(val)
|
||||
if math.isinf(f) or math.isnan(f): return 0.0
|
||||
return f
|
||||
except:
|
||||
except Exception:
|
||||
return 0.0
|
||||
|
||||
def _safe_div(self, n, d):
|
||||
return float(n) / d if d > 0 else 0.0
|
||||
|
||||
def _calc_entropy(self, data):
|
||||
if not data: return 0.0
|
||||
arr = numpy.frombuffer(data, dtype=numpy.uint8)
|
||||
@@ -376,15 +534,16 @@ class pe_scanner:
|
||||
####################################################################################################
|
||||
|
||||
def extract_features(self, file_path):
|
||||
fts = {k: 0 for k in self.SCHEMA}
|
||||
fts = {k: 0.0 for k in self.SCHEMA}
|
||||
pe = None
|
||||
try:
|
||||
fsize = os.path.getsize(file_path)
|
||||
if fsize == 0: return None
|
||||
|
||||
pe = pefile.PE(file_path, fast_load=True)
|
||||
if fsize == 0 or fsize > 268435456:
|
||||
return None
|
||||
with open(file_path, "rb") as f:
|
||||
file_bytes = f.read()
|
||||
pe = pefile.PE(data=file_bytes, fast_load=True)
|
||||
fts['TrustSigned'] = 1 if self.signer.sign_verify(file_path) else 0
|
||||
|
||||
fh = pe.FILE_HEADER
|
||||
fts['Machine'] = fh.Machine
|
||||
fts['NumberOfSections'] = fh.NumberOfSections
|
||||
@@ -393,159 +552,247 @@ class pe_scanner:
|
||||
fts['NumberOfSymbols'] = fh.NumberOfSymbols
|
||||
fts['SizeOfOptionalHeader'] = fh.SizeOfOptionalHeader
|
||||
fts['Characteristics'] = fh.Characteristics
|
||||
|
||||
curr_ts = time.time()
|
||||
curr_ts = datetime.datetime.now(datetime.timezone.utc).timestamp()
|
||||
fts['HasInvalidTimestamp'] = 1 if (fh.TimeDateStamp < 631152000 or fh.TimeDateStamp > curr_ts + 2592000) else 0
|
||||
fts['FileTimeException'] = 1 if fh.TimeDateStamp == 0 else 0
|
||||
|
||||
if hasattr(pe, 'OPTIONAL_HEADER'):
|
||||
op = pe.OPTIONAL_HEADER
|
||||
fts['Magic'] = op.Magic
|
||||
fts['MajorLinkerVersion'] = op.MajorLinkerVersion
|
||||
fts['MinorLinkerVersion'] = op.MinorLinkerVersion
|
||||
fts['SizeOfCode'] = op.SizeOfCode
|
||||
fts['SizeOfInitializedData'] = op.SizeOfInitializedData
|
||||
fts['SizeOfUninitializedData'] = op.SizeOfUninitializedData
|
||||
fts['AddressOfEntryPoint'] = op.AddressOfEntryPoint
|
||||
fts['BaseOfCode'] = op.BaseOfCode
|
||||
fts['ImageBase'] = op.ImageBase
|
||||
fts['SectionAlignment'] = op.SectionAlignment
|
||||
fts['FileAlignment'] = op.FileAlignment
|
||||
fts['MajorOperatingSystemVersion'] = op.MajorOperatingSystemVersion
|
||||
fts['MinorOperatingSystemVersion'] = op.MinorOperatingSystemVersion
|
||||
fts['MajorImageVersion'] = op.MajorImageVersion
|
||||
fts['MinorImageVersion'] = op.MinorImageVersion
|
||||
fts['MajorSubsystemVersion'] = op.MajorSubsystemVersion
|
||||
fts['MinorSubsystemVersion'] = op.MinorSubsystemVersion
|
||||
fts['SizeOfImage'] = op.SizeOfImage
|
||||
fts['SizeOfHeaders'] = op.SizeOfHeaders
|
||||
fts['CheckSum'] = op.CheckSum
|
||||
fts['Subsystem'] = op.Subsystem
|
||||
fts['DllCharacteristics'] = op.DllCharacteristics
|
||||
fts['SizeOfStackReserve'] = op.SizeOfStackReserve
|
||||
fts['SizeOfStackCommit'] = op.SizeOfStackCommit
|
||||
fts['SizeOfHeapReserve'] = op.SizeOfHeapReserve
|
||||
fts['SizeOfHeapCommit'] = op.SizeOfHeapCommit
|
||||
fts['LoaderFlags'] = op.LoaderFlags
|
||||
fts['NumberOfRvaAndSizes'] = op.NumberOfRvaAndSizes
|
||||
|
||||
fts['Is64Bit'] = 1 if fh.Machine == 0x8664 else 0
|
||||
fts['Magic'] = getattr(op, 'Magic', 0)
|
||||
fts['MajorLinkerVersion'] = getattr(op, 'MajorLinkerVersion', 0)
|
||||
fts['MinorLinkerVersion'] = getattr(op, 'MinorLinkerVersion', 0)
|
||||
fts['SizeOfCode'] = getattr(op, 'SizeOfCode', 0)
|
||||
fts['SizeOfInitializedData'] = getattr(op, 'SizeOfInitializedData', 0)
|
||||
fts['SizeOfUninitializedData'] = getattr(op, 'SizeOfUninitializedData', 0)
|
||||
fts['AddressOfEntryPoint'] = getattr(op, 'AddressOfEntryPoint', 0)
|
||||
fts['BaseOfCode'] = getattr(op, 'BaseOfCode', 0)
|
||||
fts['ImageBase'] = getattr(op, 'ImageBase', 0)
|
||||
fts['SectionAlignment'] = getattr(op, 'SectionAlignment', 0)
|
||||
fts['FileAlignment'] = getattr(op, 'FileAlignment', 0)
|
||||
fts['MajorOperatingSystemVersion'] = getattr(op, 'MajorOperatingSystemVersion', 0)
|
||||
fts['MinorOperatingSystemVersion'] = getattr(op, 'MinorOperatingSystemVersion', 0)
|
||||
fts['MajorImageVersion'] = getattr(op, 'MajorImageVersion', 0)
|
||||
fts['MinorImageVersion'] = getattr(op, 'MinorImageVersion', 0)
|
||||
fts['MajorSubsystemVersion'] = getattr(op, 'MajorSubsystemVersion', 0)
|
||||
fts['MinorSubsystemVersion'] = getattr(op, 'MinorSubsystemVersion', 0)
|
||||
fts['SizeOfImage'] = getattr(op, 'SizeOfImage', 0)
|
||||
fts['SizeOfHeaders'] = getattr(op, 'SizeOfHeaders', 0)
|
||||
fts['CheckSum'] = getattr(op, 'CheckSum', 0)
|
||||
fts['Subsystem'] = getattr(op, 'Subsystem', 0)
|
||||
fts['DllCharacteristics'] = getattr(op, 'DllCharacteristics', 0)
|
||||
fts['SizeOfStackReserve'] = getattr(op, 'SizeOfStackReserve', 0)
|
||||
fts['SizeOfStackCommit'] = getattr(op, 'SizeOfStackCommit', 0)
|
||||
fts['SizeOfHeapReserve'] = getattr(op, 'SizeOfHeapReserve', 0)
|
||||
fts['SizeOfHeapCommit'] = getattr(op, 'SizeOfHeapCommit', 0)
|
||||
fts['LoaderFlags'] = getattr(op, 'LoaderFlags', 0)
|
||||
fts['NumberOfRvaAndSizes'] = getattr(op, 'NumberOfRvaAndSizes', 0)
|
||||
fts['Is64Bit'] = 1 if fh.Machine in (0x8664, 0xAA64, 0x0200) else 0
|
||||
fts['IsExe'] = 1 if pe.is_exe() else 0
|
||||
fts['IsDll'] = 1 if pe.is_dll() else 0
|
||||
fts['IsDriver'] = 1 if pe.is_driver() else 0
|
||||
fts['IsDriver'] = 1 if fts['Subsystem'] == 1 else 0
|
||||
if hasattr(op, 'DATA_DIRECTORY'):
|
||||
reloc_idx = pefile.DIRECTORY_ENTRY.get('IMAGE_DIRECTORY_ENTRY_BASERELOC', 5)
|
||||
if len(op.DATA_DIRECTORY) > reloc_idx and op.DATA_DIRECTORY[reloc_idx].Size > 0:
|
||||
fts['HasRelocationDirectory'] = 1
|
||||
|
||||
if len(op.DATA_DIRECTORY) > 9 and op.DATA_DIRECTORY[9].VirtualAddress > 0:
|
||||
try:
|
||||
fts['FileEntropy'] = self._calc_entropy(file_bytes)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
sec_entropies = []
|
||||
text_entropies = []
|
||||
data_entropies = []
|
||||
rsrc_entropies = []
|
||||
char_flags = [0x00000020, 0x00000040, 0x00000080, 0x02000000, 0x20000000, 0x40000000, 0x80000000]
|
||||
|
||||
if hasattr(pe, 'sections'):
|
||||
fts['SectionCount'] = len(pe.sections)
|
||||
for section in pe.sections:
|
||||
try:
|
||||
name = section.Name.decode('ascii', 'ignore').strip('\x00')
|
||||
except Exception:
|
||||
name = ""
|
||||
try:
|
||||
s_data = section.get_data()
|
||||
s_entropy = self._calc_entropy(s_data)
|
||||
except Exception:
|
||||
s_data = b""
|
||||
s_entropy = 0.0
|
||||
|
||||
sec_entropies.append(s_entropy)
|
||||
if any(p in name.lower() for p in self.PACKERS): fts['HasPacked'] = 1
|
||||
if section.Characteristics & 0x20000000: fts['ExecutableSections'] += 1
|
||||
if section.Characteristics & 0x80000000: fts['WritableSections'] += 1
|
||||
if section.Characteristics & 0x40000000: fts['ReadableSections'] += 1
|
||||
if section.SizeOfRawData + section.PointerToRawData > fsize: fts['SectionException'] = 1
|
||||
|
||||
for flag in char_flags:
|
||||
if section.Characteristics & flag:
|
||||
fts[f'Char_{flag:08X}_Count'] += 1
|
||||
fts[f'Char_{flag:08X}_MeanEntropy'] += s_entropy
|
||||
|
||||
ratio = self._safe_div(section.SizeOfRawData, fsize)
|
||||
if name.startswith('.text'):
|
||||
text_entropies.append(s_entropy)
|
||||
fts['TextSizeRatio'] += ratio
|
||||
elif name.startswith('.data'):
|
||||
data_entropies.append(s_entropy)
|
||||
fts['DataSizeRatio'] += ratio
|
||||
elif name.startswith('.rsrc'):
|
||||
rsrc_entropies.append(s_entropy)
|
||||
fts['RsrcSizeRatio'] += ratio
|
||||
if b"requireAdministrator" in s_data: fts['IsAdmin'] = 1
|
||||
for sig in self.INSTALLER_SIGS:
|
||||
if sig in s_data:
|
||||
fts['IsInstall'] = 1
|
||||
break
|
||||
|
||||
fts['SectionMeanEntropy'] = self._safe_div(sum(sec_entropies), len(sec_entropies))
|
||||
fts['TextSectionMaxEntropy'] = max(text_entropies) if text_entropies else 0.0
|
||||
fts['TextSectionMeanEntropy'] = self._safe_div(sum(text_entropies), len(text_entropies))
|
||||
fts['DataSectionMaxEntropy'] = max(data_entropies) if data_entropies else 0.0
|
||||
fts['DataSectionMeanEntropy'] = self._safe_div(sum(data_entropies), len(data_entropies))
|
||||
fts['RsrcSectionMaxEntropy'] = max(rsrc_entropies) if rsrc_entropies else 0.0
|
||||
fts['RsrcSectionMeanEntropy'] = self._safe_div(sum(rsrc_entropies), len(rsrc_entropies))
|
||||
|
||||
for flag in char_flags:
|
||||
fts[f'Char_{flag:08X}_MeanEntropy'] = self._safe_div(fts[f'Char_{flag:08X}_MeanEntropy'], fts[f'Char_{flag:08X}_Count'])
|
||||
|
||||
try:
|
||||
pe.parse_data_directories(directories=[
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_TLS']
|
||||
])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_TLS') and hasattr(pe.DIRECTORY_ENTRY_TLS, 'struct'):
|
||||
if getattr(pe.DIRECTORY_ENTRY_TLS.struct, 'AddressOfCallBacks', 0) != 0:
|
||||
fts['HasTlsCallbacks'] = 1
|
||||
if len(op.DATA_DIRECTORY) > 5 and op.DATA_DIRECTORY[5].Size > 0:
|
||||
fts['HasRelocationDirectory'] = 1
|
||||
|
||||
pe.parse_sections(pe.FILE_HEADER.NumberOfSections)
|
||||
file_data = pe.get_memory_mapped_image()
|
||||
fts['FileEntropy'] = self._calc_entropy(file_data)
|
||||
fts['SectionCount'] = len(pe.sections)
|
||||
if hasattr(pe, 'VS_FIXEDFILEINFO') and len(pe.VS_FIXEDFILEINFO) > 0:
|
||||
flags = getattr(pe.VS_FIXEDFILEINFO[0], 'FileFlags', 0)
|
||||
fts['IsDebug'] = 1 if flags & 0x1 else 0
|
||||
fts['IsPreRelease'] = 1 if flags & 0x2 else 0
|
||||
fts['IsPatched'] = 1 if flags & 0x4 else 0
|
||||
fts['IsPrivateBuild'] = 1 if flags & 0x8 else 0
|
||||
fts['IsSpecialBuild'] = 1 if flags & 0x20 else 0
|
||||
|
||||
for section in pe.sections:
|
||||
try:
|
||||
name = section.Name.decode('ascii', 'ignore').strip('\x00')
|
||||
except:
|
||||
name = ""
|
||||
s_data = section.get_data()
|
||||
s_entropy = self._calc_entropy(s_data)
|
||||
|
||||
if any(p in name.lower() for p in self.PACKERS):
|
||||
fts['HasPacked'] = 1
|
||||
if section.Characteristics & 0x20000000:
|
||||
fts['ExecutableSections'] += 1
|
||||
if section.Characteristics & 0x80000000:
|
||||
fts['WritableSections'] += 1
|
||||
if section.Characteristics & 0x40000000:
|
||||
fts['ReadableSections'] += 1
|
||||
if section.SizeOfRawData + section.PointerToRawData > fsize:
|
||||
fts['SectionException'] = 1
|
||||
|
||||
ratio = section.SizeOfRawData / fsize if fsize > 0 else 0
|
||||
if name.startswith('.text'):
|
||||
fts['TextSection'] = s_entropy
|
||||
fts['TextSizeRatio'] = ratio
|
||||
elif name.startswith('.data'):
|
||||
fts['DataSection'] = s_entropy
|
||||
fts['DataSizeRatio'] = ratio
|
||||
elif name.startswith('.rsrc'):
|
||||
fts['RsrcSection'] = s_entropy
|
||||
fts['RsrcSizeRatio'] = ratio
|
||||
if b"requireAdministrator" in s_data:
|
||||
fts['IsAdmin'] = 1
|
||||
for sig in self.INSTALLER_SIGS:
|
||||
if sig in s_data:
|
||||
fts['IsInstall'] = 1
|
||||
break
|
||||
|
||||
pe.parse_data_directories(directories=[
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_RESOURCE'],
|
||||
pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_DEBUG']
|
||||
])
|
||||
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():
|
||||
try:
|
||||
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
|
||||
v = val.decode('utf-8', 'ignore') if isinstance(val, bytes) else str(val)
|
||||
length = len(v)
|
||||
if k == 'FileDescription': fts['FileDescriptionLength'] = length
|
||||
elif k == 'FileVersion': fts['FileVersionLength'] = length
|
||||
elif k == 'ProductName': fts['ProductNameLength'] = length
|
||||
elif k == 'ProductVersion': fts['ProductVersionLength'] = length
|
||||
elif k == 'CompanyName': fts['CompanyNameLength'] = length
|
||||
elif k == 'LegalCopyright': fts['LegalCopyrightLength'] = length
|
||||
elif k == 'Comments': fts['CommentsLength'] = length
|
||||
elif k == 'InternalName': fts['InternalNameLength'] = length
|
||||
elif k == 'LegalTrademarks': fts['LegalTrademarksLength'] = length
|
||||
elif k == 'SpecialBuild': fts['SpecialBuildLength'] = length
|
||||
elif k == 'PrivateBuild': fts['PrivateBuildLength'] = length
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_IMPORT'):
|
||||
fts['ImportCount'] = len(pe.DIRECTORY_ENTRY_IMPORT)
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
for imp in entry.imports:
|
||||
fts['ApiCount'] += 1
|
||||
if not imp or not hasattr(imp, 'name') or not imp.name:
|
||||
continue
|
||||
if getattr(entry, 'dll', None):
|
||||
try:
|
||||
name = imp.name.decode('ascii', 'ignore')
|
||||
if name in self.SCHEMA_SET:
|
||||
fts[name] = 1
|
||||
except:
|
||||
dll_name = entry.dll.decode('ascii', 'ignore').lower()
|
||||
if dll_name in self.TARGET_DLLS:
|
||||
fts[f"Dll_{dll_name.replace('.', '_')}"] = 1.0
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT'):
|
||||
imports = getattr(entry, 'imports', [])
|
||||
for imp in imports:
|
||||
fts['ImportFunctionCount'] += 1
|
||||
if not getattr(imp, 'name', None): continue
|
||||
try:
|
||||
func_name = imp.name.decode('ascii', 'ignore')
|
||||
if func_name in self._ALL_APIS:
|
||||
fts[f'Api_{func_name}'] += 1
|
||||
fts['ApiCount'] += 1
|
||||
for cat in self._API_TO_CATS.get(func_name, []):
|
||||
fts[f'Cat_{cat}'] += 1
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_EXPORT') and hasattr(pe.DIRECTORY_ENTRY_EXPORT, 'symbols'):
|
||||
fts['ExportCount'] = len(pe.DIRECTORY_ENTRY_EXPORT.symbols)
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_RESOURCE'):
|
||||
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
|
||||
if entry.id == 3 and hasattr(entry, 'directory'):
|
||||
fts['IconCount'] = len(entry.directory.entries)
|
||||
break
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG'):
|
||||
if getattr(entry, 'id', None) == 3 and hasattr(entry, 'directory'):
|
||||
fts['IconCount'] += len(getattr(entry.directory, 'entries', []))
|
||||
|
||||
if hasattr(pe, 'OPTIONAL_HEADER') and hasattr(pe.OPTIONAL_HEADER, 'DATA_DIRECTORY') and len(pe.OPTIONAL_HEADER.DATA_DIRECTORY) > 3:
|
||||
exc_dir = pe.OPTIONAL_HEADER.DATA_DIRECTORY[3]
|
||||
if exc_dir.Size > 0:
|
||||
fts['ExceptionCount'] = exc_dir.Size // 12 if fts['Machine'] in (0x8664, 0xAA64) else 0
|
||||
|
||||
if hasattr(pe, 'DIRECTORY_ENTRY_DEBUG'):
|
||||
fts['DebugCount'] = len(pe.DIRECTORY_ENTRY_DEBUG)
|
||||
|
||||
return {k: self._safe_float(v) for k, v in fts.items()}
|
||||
for k in fts:
|
||||
fts[k] = self._safe_float(fts[k])
|
||||
|
||||
return fts
|
||||
|
||||
except Exception:
|
||||
return None
|
||||
finally:
|
||||
if pe: pe.close()
|
||||
if pe:
|
||||
pe.close()
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def pe_scan(self, file_path):
|
||||
if not self.model:
|
||||
return False, False
|
||||
|
||||
try:
|
||||
data = self.extract_features(file_path)
|
||||
if not data:
|
||||
return False, False
|
||||
|
||||
vec = numpy.array(
|
||||
[data.get(feat, 0.0) for feat in self.SCHEMA],
|
||||
dtype=numpy.float32
|
||||
).reshape(1, -1)
|
||||
|
||||
vec_data = []
|
||||
for key in self.feature_order:
|
||||
if key:
|
||||
vec_data.append(data.get(key, 0.0))
|
||||
else:
|
||||
vec_data.append(0.0)
|
||||
|
||||
vec = numpy.array(vec_data, dtype=numpy.float32).reshape(1, -1)
|
||||
outputs = self.model.run(None, {self.input_name: vec})
|
||||
result = outputs[1]
|
||||
prob = 0.0
|
||||
if isinstance(result, list):
|
||||
prob = float(result[0].get(1, 0.0))
|
||||
|
||||
if isinstance(result, list) and len(result) > 0:
|
||||
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 prob > 0.5:
|
||||
return f"General:WinPE/Unknown.{score}!ml", score
|
||||
|
||||
return False, False
|
||||
|
||||
except Exception:
|
||||
return False, False
|
||||
|
||||
@@ -576,20 +823,27 @@ class cloud_scanner:
|
||||
base = api_host.rstrip('/')
|
||||
base_headers = {"X-API-Key": api_key, "User-Agent": "PYAS-Client/1.0"}
|
||||
|
||||
def _req(m, ep, files=None, **k):
|
||||
def _req(m, ep, files=None, req_timeout=5, **k):
|
||||
try:
|
||||
return requests.request(m, f"{base}{ep}", headers=base_headers, files=files, timeout=120, **k)
|
||||
return requests.request(m, f"{base}{ep}", headers=base_headers, files=files, timeout=req_timeout, **k)
|
||||
except:
|
||||
return None
|
||||
|
||||
r = _req("GET", f"/api/processing_status/{sha256}")
|
||||
status = r.json().get('status') if r and r.status_code == 200 else 'missing'
|
||||
r = _req("GET", f"/api/processing_status/{sha256}", req_timeout=15)
|
||||
|
||||
status = 'missing'
|
||||
if r and r.status_code == 200:
|
||||
try:
|
||||
status = r.json().get('status', 'missing')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if status in ['failed', 'error']:
|
||||
status = 'missing'
|
||||
|
||||
if status == 'missing':
|
||||
with open(file_path, 'rb') as f:
|
||||
r = _req("POST", "/api/upload", files={'file': f})
|
||||
r = _req("POST", "/api/upload", files={'file': f}, req_timeout=30)
|
||||
if not r or r.status_code != 200:
|
||||
return False, sha256
|
||||
|
||||
@@ -607,39 +861,60 @@ class cloud_scanner:
|
||||
base = api_host.rstrip('/')
|
||||
headers = {"X-API-Key": api_key, "User-Agent": "PYAS-Client/1.0"}
|
||||
|
||||
def _req(m, ep, **k):
|
||||
def _req(m, ep, req_timeout, **k):
|
||||
try:
|
||||
return requests.request(m, f"{base}{ep}", headers=headers, timeout=10, **k)
|
||||
except:
|
||||
return requests.request(m, f"{base}{ep}", headers=headers, timeout=req_timeout, **k)
|
||||
except requests.exceptions.Timeout:
|
||||
return "timeout"
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
timeout_count = 0
|
||||
for _ in range(30):
|
||||
r = _req("GET", f"/api/processing_status/{sha256}")
|
||||
st = r.json().get('status') if r else 'error'
|
||||
r = _req("GET", f"/api/processing_status/{sha256}", req_timeout=15)
|
||||
|
||||
if r == "timeout" or r is None:
|
||||
timeout_count += 1
|
||||
if timeout_count >= 3:
|
||||
return False
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
st = 'error'
|
||||
if r.status_code == 200:
|
||||
try:
|
||||
st = r.json().get('status', 'error')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if st == 'done':
|
||||
break
|
||||
if st in ['error', 'failed']:
|
||||
return False
|
||||
time.sleep(1)
|
||||
|
||||
r = _req("GET", f"/api/report/{sha256}")
|
||||
if r and r.status_code == 200:
|
||||
data = r.json().get('data', {})
|
||||
res = data.get('detection', {}).get('results', {}).get('PYAS', {})
|
||||
label = res.get('label', 'Unsupport')
|
||||
sims = data.get('similar', [])
|
||||
r = _req("GET", f"/api/report/{sha256}", req_timeout=30)
|
||||
if r and r != "timeout" and r.status_code == 200:
|
||||
try:
|
||||
data = r.json().get('data', {})
|
||||
res = data.get('detection', {}).get('results', {}).get('PYAS', {})
|
||||
label = res.get('label', 'Unsupport')
|
||||
score = res.get('score', 100)
|
||||
sims = data.get('similar', [])
|
||||
|
||||
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
|
||||
if "General" in s.get('label'):
|
||||
sim_malicious_count += 1
|
||||
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
|
||||
if "General" in s.get('label', ''):
|
||||
sim_malicious_count += 1
|
||||
|
||||
if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count):
|
||||
return f"General:WinPE/Unknown.{score}!cl"
|
||||
if is_malicious and (valid_sim_count == 0 or sim_malicious_count == valid_sim_count):
|
||||
return f"General:WinPE/Unknown.{score}!cl"
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
+1
-1
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
|
||||
|
||||
pyinstaller_versionfile.create_versionfile(
|
||||
output_file="versionfile.txt",
|
||||
version='3.4.0',
|
||||
version='3.4.2',
|
||||
company_name="PYAS Security",
|
||||
file_description="Python Antivirus Software",
|
||||
internal_name="PYAS",
|
||||
|
||||
@@ -45,7 +45,21 @@ constexpr auto PYAS_POOL_TAG = 'SAYP';
|
||||
#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
|
||||
@@ -107,17 +121,6 @@ typedef struct _RULE_NODE {
|
||||
|
||||
extern DRIVER_DATA GlobalData;
|
||||
|
||||
//
|
||||
// Rule Lists Externs (Added for Exception Path support)
|
||||
//
|
||||
extern PRULE_NODE g_RegistryBlockList;
|
||||
extern PRULE_NODE g_RegistryTrustedList;
|
||||
extern PRULE_NODE g_ProcessTrustedPaths;
|
||||
extern PRULE_NODE g_ProcessExploitable;
|
||||
extern PRULE_NODE g_FileProtectedPaths;
|
||||
extern PRULE_NODE g_FileExceptionPaths; // [Added] Exception List
|
||||
extern PRULE_NODE g_FileRansomExts;
|
||||
|
||||
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);
|
||||
@@ -138,9 +141,7 @@ VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INF
|
||||
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName);
|
||||
|
||||
BOOLEAN IsProcessTrusted(HANDLE ProcessId);
|
||||
BOOLEAN IsCriticalSystemProcess(HANDLE ProcessId);
|
||||
BOOLEAN IsTargetProtected(HANDLE ProcessId);
|
||||
BOOLEAN IsInstallerProcess(HANDLE ProcessId);
|
||||
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes);
|
||||
|
||||
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName);
|
||||
|
||||
@@ -28,28 +28,27 @@ static VOID InstanceTeardownComplete(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTA
|
||||
|
||||
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
|
||||
if (!(Flags & FLTFL_FILTER_UNLOAD_MANDATORY)) {
|
||||
ExAcquireFastMutex(&GlobalData.PortMutex);
|
||||
BOOLEAN IsConnected = (GlobalData.ClientPort != NULL);
|
||||
ExReleaseFastMutex(&GlobalData.PortMutex);
|
||||
return STATUS_FLT_DO_NOT_DETACH;
|
||||
}
|
||||
|
||||
if (IsConnected) {
|
||||
return STATUS_FLT_DO_NOT_DETACH;
|
||||
}
|
||||
if (GlobalData.ServerPort) {
|
||||
FltCloseCommunicationPort(GlobalData.ServerPort);
|
||||
GlobalData.ServerPort = NULL;
|
||||
}
|
||||
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
}
|
||||
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
UninitializeRegistryProtection();
|
||||
UninitializeProcessProtection();
|
||||
UnloadRules(); // Release JSON rules memory
|
||||
|
||||
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
|
||||
|
||||
if (GlobalData.ServerPort) {
|
||||
FltCloseCommunicationPort(GlobalData.ServerPort);
|
||||
}
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
}
|
||||
UnloadRules();
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -77,10 +76,6 @@ static VOID PortDisconnect(PVOID ConnectionCookie) {
|
||||
|
||||
ExAcquireFastMutex(&GlobalData.PortMutex);
|
||||
GlobalData.ClientPort = NULL;
|
||||
GlobalData.PyasPid = 0;
|
||||
if (GlobalData.PyasProcess) {
|
||||
GlobalData.PyasProcess = NULL;
|
||||
}
|
||||
ExReleaseFastMutex(&GlobalData.PortMutex);
|
||||
|
||||
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
|
||||
@@ -126,13 +121,12 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg
|
||||
ExInitializeRundownProtection(&GlobalData.PortRundown);
|
||||
|
||||
status = LoadRulesFromDisk(RegistryPath);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
// Optional: Fail load if rules missing, or continue with empty rules
|
||||
// return status;
|
||||
}
|
||||
|
||||
status = FltRegisterFilter(DriverObject, &FilterRegistration, &GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(status)) return status;
|
||||
if (!NT_SUCCESS(status)) {
|
||||
UnloadRules();
|
||||
return status;
|
||||
}
|
||||
|
||||
status = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
|
||||
if (NT_SUCCESS(status)) {
|
||||
@@ -143,7 +137,11 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
}
|
||||
UnloadRules();
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -151,5 +149,24 @@ extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING Reg
|
||||
InitializeRegistryProtection(DriverObject);
|
||||
PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
|
||||
return FltStartFiltering(GlobalData.FilterHandle);
|
||||
status = FltStartFiltering(GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
UninitializeRegistryProtection();
|
||||
UninitializeProcessProtection();
|
||||
|
||||
if (GlobalData.ServerPort) {
|
||||
FltCloseCommunicationPort(GlobalData.ServerPort);
|
||||
GlobalData.ServerPort = NULL;
|
||||
}
|
||||
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
}
|
||||
|
||||
UnloadRules();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -12,7 +12,6 @@ FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data,
|
||||
UNREFERENCED_PARAMETER(CompletionContext);
|
||||
|
||||
if (Data->RequestorMode == KernelMode) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
|
||||
ULONG IoControlCode = Data->Iopb->Parameters.DeviceIoControl.Common.IoControlCode;
|
||||
@@ -23,7 +22,7 @@ FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data,
|
||||
IoControlCode == IOCTL_DISK_FORMAT_TRACKS_EX) {
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
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;
|
||||
|
||||
@@ -34,7 +34,8 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_R
|
||||
|
||||
FileName = nameInfo->Name;
|
||||
if (!FileName.Buffer || FileName.Length == 0) {
|
||||
goto cleanup;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_SUCCESS_NO_CALLBACK;
|
||||
}
|
||||
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
@@ -64,7 +65,7 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_R
|
||||
if (Data->Iopb->Parameters.Create.SecurityContext->DesiredAccess &
|
||||
(FILE_WRITE_DATA | FILE_APPEND_DATA | GENERIC_WRITE | WRITE_DAC | WRITE_OWNER)) {
|
||||
|
||||
if (!Trusted && !IsInstallerProcess(Pid)) {
|
||||
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;
|
||||
@@ -178,11 +179,9 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT
|
||||
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) && !IsInstallerProcess(Pid)) {
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
SendMessageToUser(2001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
@@ -220,7 +219,6 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RE
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
|
||||
PVOID SystemBuffer = NULL;
|
||||
PMDL Mdl = Data->Iopb->Parameters.Write.MdlAddress;
|
||||
|
||||
@@ -238,11 +236,9 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RE
|
||||
|
||||
if (SystemBuffer) {
|
||||
if (CheckRansomActivity(Pid, &nameInfo->Name, SystemBuffer, WriteLength, TRUE)) {
|
||||
if (!IsInstallerProcess(Pid)) {
|
||||
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
}
|
||||
SendMessageToUser(5001, (ULONG)(ULONG_PTR)Pid, nameInfo->Name.Buffer, nameInfo->Name.Length);
|
||||
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
callbackStatus = FLT_PREOP_COMPLETE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -270,7 +266,6 @@ FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data,
|
||||
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;
|
||||
FltReleaseFileNameInformation(nameInfo);
|
||||
return FLT_PREOP_COMPLETE;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
PVOID ObRegistrationHandle = NULL;
|
||||
OB_CALLBACK_REGISTRATION ObRegistration;
|
||||
OB_OPERATION_REGISTRATION ObCallbacks[1];
|
||||
static PVOID ObRegistrationHandle = NULL;
|
||||
static OB_CALLBACK_REGISTRATION ObRegistration;
|
||||
static OB_OPERATION_REGISTRATION ObCallbacks[2];
|
||||
|
||||
static BOOLEAN IsSystemImage(PUNICODE_STRING FullImageName) {
|
||||
if (!FullImageName || !FullImageName->Buffer) return FALSE;
|
||||
@@ -35,7 +35,7 @@ VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INF
|
||||
}
|
||||
|
||||
static BOOLEAN IsBlacklistedAdminTool(HANDLE ProcessId) {
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FALSE;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
|
||||
|
||||
NTSTATUS status;
|
||||
BOOLEAN isBlacklisted = FALSE;
|
||||
@@ -75,22 +75,19 @@ static OB_PREOP_CALLBACK_STATUS PreOpenProcess(PVOID RegistrationContext, POB_PR
|
||||
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;
|
||||
|
||||
BOOLEAN bProtected = IsTargetProtected(TargetPid);
|
||||
if (IsTargetProtected(TargetPid)) {
|
||||
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
|
||||
|
||||
if (bProtected) {
|
||||
BOOLEAN bIsInstaller = IsInstallerProcess(SourcePid);
|
||||
|
||||
if (bIsInstaller) {
|
||||
if (bIsTrusted) {
|
||||
if (IsBlacklistedAdminTool(SourcePid)) {
|
||||
bIsInstaller = FALSE;
|
||||
bIsTrusted = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsInstaller) return OB_PREOP_SUCCESS;
|
||||
if (bIsTrusted) return OB_PREOP_SUCCESS;
|
||||
|
||||
ACCESS_MASK DenyMask = PROCESS_TERMINATE |
|
||||
PROCESS_VM_OPERATION |
|
||||
@@ -114,6 +111,53 @@ static OB_PREOP_CALLBACK_STATUS PreOpenProcess(PVOID RegistrationContext, POB_PR
|
||||
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)) {
|
||||
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 &= ~DenyMask;
|
||||
OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess &= ~DenyMask;
|
||||
|
||||
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
|
||||
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= ~DenyMask;
|
||||
OperationInformation->Parameters->DuplicateHandleInformation.OriginalDesiredAccess &= ~DenyMask;
|
||||
}
|
||||
}
|
||||
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS InitializeProcessProtection() {
|
||||
static UNICODE_STRING Altitude = { 0 };
|
||||
RtlInitUnicodeString(&Altitude, L"320000");
|
||||
@@ -123,8 +167,13 @@ NTSTATUS InitializeProcessProtection() {
|
||||
ObCallbacks[0].PreOperation = PreOpenProcess;
|
||||
ObCallbacks[0].PostOperation = NULL;
|
||||
|
||||
ObCallbacks[1].ObjectType = PsThreadType;
|
||||
ObCallbacks[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
ObCallbacks[1].PreOperation = PreOpenThread;
|
||||
ObCallbacks[1].PostOperation = NULL;
|
||||
|
||||
ObRegistration.Version = OB_FLT_REGISTRATION_VERSION;
|
||||
ObRegistration.OperationRegistrationCount = 1;
|
||||
ObRegistration.OperationRegistrationCount = 2;
|
||||
ObRegistration.Altitude = Altitude;
|
||||
ObRegistration.RegistrationContext = NULL;
|
||||
ObRegistration.OperationRegistration = ObCallbacks;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
LARGE_INTEGER Cookie;
|
||||
static LARGE_INTEGER Cookie;
|
||||
|
||||
static BOOLEAN IsBamRegistryPath(PCUNICODE_STRING KeyName) {
|
||||
if (!KeyName || !KeyName->Buffer) return FALSE;
|
||||
@@ -108,7 +108,7 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
|
||||
|
||||
if (CheckRegistryRule(&FullPath)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
|
||||
if (KeyName) {
|
||||
if (CheckRegistryRule(KeyName)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, KeyName->Buffer, KeyName->Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
@@ -166,22 +166,9 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
|
||||
|
||||
if (CheckRegistryRule(&FullPath)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
|
||||
BOOLEAN IsCriticalReg = FALSE;
|
||||
if (WildcardMatch(L"*\\MACHINE\\SAM*", FullPath.Buffer, FullPath.Length) ||
|
||||
WildcardMatch(L"*\\MACHINE\\SECURITY*", FullPath.Buffer, FullPath.Length) ||
|
||||
WildcardMatch(L"*\\MACHINE\\SYSTEM*", FullPath.Buffer, FullPath.Length) ||
|
||||
WildcardMatch(L"*\\MACHINE\\BCD00000000*", FullPath.Buffer, FullPath.Length)) {
|
||||
if (IsCriticalSystemProcess(Pid)) {
|
||||
IsCriticalReg = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsCriticalReg) {
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
PyasFree(Buffer);
|
||||
@@ -222,7 +209,7 @@ static NTSTATUS RegistryCallback(PVOID CallbackContext, PVOID Argument1, PVOID A
|
||||
|
||||
if (CheckRegistryRule(&FullPath)) {
|
||||
HANDLE Pid = PsGetCurrentProcessId();
|
||||
if (!IsProcessTrusted(Pid) && !IsInstallerProcess(Pid)) {
|
||||
if (!IsProcessTrusted(Pid)) {
|
||||
SendMessageToUser(3001, (ULONG)(ULONG_PTR)Pid, FullPath.Buffer, FullPath.Length);
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
@@ -9,31 +9,30 @@ constexpr auto TRUST_CACHE_SIZE = 128;
|
||||
constexpr auto TRUST_CACHE_TTL_SEC = 300;
|
||||
|
||||
typedef struct _RANSOM_TRACKER {
|
||||
HANDLE ProcessId;
|
||||
PEPROCESS Process;
|
||||
ULONG ActivityCount;
|
||||
LARGE_INTEGER LastActivityTime;
|
||||
} RANSOM_TRACKER, * PRANSOM_TRACKER;
|
||||
|
||||
typedef struct _TRUST_CACHE_ENTRY {
|
||||
HANDLE ProcessId;
|
||||
PEPROCESS Process;
|
||||
BOOLEAN IsTrusted;
|
||||
LARGE_INTEGER CacheTime;
|
||||
} TRUST_CACHE_ENTRY, * PTRUST_CACHE_ENTRY;
|
||||
|
||||
RANSOM_TRACKER RansomTrackers[MAX_TRACKERS];
|
||||
TRUST_CACHE_ENTRY TrustCache[TRUST_CACHE_SIZE];
|
||||
FAST_MUTEX TrustCacheLock;
|
||||
|
||||
static RANSOM_TRACKER RansomTrackers[MAX_TRACKERS];
|
||||
static TRUST_CACHE_ENTRY TrustCache[TRUST_CACHE_SIZE];
|
||||
static FAST_MUTEX TrustCacheLock;
|
||||
static BOOLEAN g_CacheInitialized = FALSE;
|
||||
|
||||
ERESOURCE RuleLock;
|
||||
PRULE_NODE g_RegistryBlockList = NULL;
|
||||
PRULE_NODE g_RegistryTrustedList = NULL;
|
||||
PRULE_NODE g_ProcessTrustedPaths = NULL;
|
||||
PRULE_NODE g_ProcessExploitable = NULL;
|
||||
PRULE_NODE g_FileProtectedPaths = NULL;
|
||||
PRULE_NODE g_FileExceptionPaths = NULL;
|
||||
PRULE_NODE g_FileRansomExts = NULL;
|
||||
static ERESOURCE RuleLock;
|
||||
static PRULE_NODE g_RegistryBlockList = NULL;
|
||||
static PRULE_NODE g_RegistryTrustedList = NULL;
|
||||
static PRULE_NODE g_ProcessTrustedPaths = NULL;
|
||||
static PRULE_NODE g_ProcessExploitable = NULL;
|
||||
static PRULE_NODE g_FileProtectedPaths = NULL;
|
||||
static PRULE_NODE g_FileExceptionPaths = NULL;
|
||||
static PRULE_NODE g_FileRansomExts = NULL;
|
||||
|
||||
const PCWSTR Helper_NaturallyCompressedExtensions[] = {
|
||||
L".zip", L".7z", L".rar", L".tar", L".gz",
|
||||
@@ -423,7 +422,7 @@ static VOID InitTrustCache() {
|
||||
static BOOLEAN IsWindowsSystemApp(PCWSTR Buffer, USHORT Length) {
|
||||
if (WildcardMatch(L"*\\Windows\\SystemApps\\*", Buffer, Length)) return TRUE;
|
||||
if (WildcardMatch(L"*\\Windows\\ImmersiveControlPanel\\*", Buffer, Length)) return TRUE;
|
||||
if (WildcardMatch(L"*\\explorer.exe", Buffer, Length)) return TRUE;
|
||||
if (WildcardMatch(L"*\\Windows\\explorer.exe", Buffer, Length)) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -431,15 +430,25 @@ BOOLEAN IsProcessTrusted(HANDLE ProcessId) {
|
||||
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
|
||||
if (ProcessId == (HANDLE)4) return TRUE;
|
||||
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FALSE;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
|
||||
|
||||
PEPROCESS Process = NULL;
|
||||
if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (g_CacheInitialized) {
|
||||
ExAcquireFastMutex(&TrustCacheLock);
|
||||
ULONG Hash = ((ULONG)(ULONG_PTR)ProcessId) & (TRUST_CACHE_SIZE - 1);
|
||||
if (TrustCache[Hash].ProcessId == ProcessId) {
|
||||
BOOLEAN cachedResult = TrustCache[Hash].IsTrusted;
|
||||
ExReleaseFastMutex(&TrustCacheLock);
|
||||
return cachedResult;
|
||||
if (TrustCache[Hash].Process == Process) {
|
||||
LARGE_INTEGER Now;
|
||||
KeQuerySystemTime(&Now);
|
||||
if ((Now.QuadPart - TrustCache[Hash].CacheTime.QuadPart) < (TRUST_CACHE_TTL_SEC * 10000000LL)) {
|
||||
BOOLEAN cachedResult = TrustCache[Hash].IsTrusted;
|
||||
ExReleaseFastMutex(&TrustCacheLock);
|
||||
ObDereferenceObject(Process);
|
||||
return cachedResult;
|
||||
}
|
||||
}
|
||||
ExReleaseFastMutex(&TrustCacheLock);
|
||||
}
|
||||
@@ -452,7 +461,6 @@ BOOLEAN IsProcessTrusted(HANDLE ProcessId) {
|
||||
BOOLEAN isTrusted = FALSE;
|
||||
|
||||
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
|
||||
|
||||
ExAcquireResourceSharedLite(&RuleLock, TRUE);
|
||||
|
||||
PRULE_NODE Node = g_ProcessExploitable;
|
||||
@@ -487,27 +495,17 @@ cleanup:
|
||||
if (g_CacheInitialized) {
|
||||
ExAcquireFastMutex(&TrustCacheLock);
|
||||
ULONG Hash = ((ULONG)(ULONG_PTR)ProcessId) & (TRUST_CACHE_SIZE - 1);
|
||||
TrustCache[Hash].ProcessId = ProcessId;
|
||||
TrustCache[Hash].Process = Process;
|
||||
TrustCache[Hash].IsTrusted = isTrusted;
|
||||
KeQuerySystemTime(&TrustCache[Hash].CacheTime);
|
||||
ExReleaseFastMutex(&TrustCacheLock);
|
||||
}
|
||||
|
||||
if (imageFileName) ExFreePool(imageFileName);
|
||||
ObDereferenceObject(Process);
|
||||
return isTrusted;
|
||||
}
|
||||
|
||||
BOOLEAN IsCriticalSystemProcess(HANDLE ProcessId) {
|
||||
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
|
||||
if (ProcessId == (HANDLE)4) return TRUE;
|
||||
|
||||
return IsProcessTrusted(ProcessId);
|
||||
}
|
||||
|
||||
BOOLEAN IsInstallerProcess(HANDLE ProcessId) {
|
||||
return IsProcessTrusted(ProcessId);
|
||||
}
|
||||
|
||||
BOOLEAN IsTargetProtected(HANDLE ProcessId) {
|
||||
if ((ULONG)(ULONG_PTR)ProcessId == GlobalData.PyasPid) return TRUE;
|
||||
return FALSE;
|
||||
@@ -585,7 +583,7 @@ BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName) {
|
||||
while (ExNode) {
|
||||
if (WildcardMatch(ExNode->Pattern.Buffer, FileName->Buffer, FileName->Length)) {
|
||||
ExReleaseResourceLite(&RuleLock);
|
||||
return FALSE;
|
||||
return FALSE;
|
||||
}
|
||||
ExNode = ExNode->Next;
|
||||
}
|
||||
@@ -654,7 +652,7 @@ static BOOLEAN IsNoisyRansomPath(PCUNICODE_STRING FileName) {
|
||||
}
|
||||
|
||||
static BOOLEAN IsExplorerProcess(HANDLE ProcessId) {
|
||||
if (KeGetCurrentIrql() > APC_LEVEL) return FALSE;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL) return FALSE;
|
||||
PUNICODE_STRING imageFileName = NULL;
|
||||
NTSTATUS status = GetProcessImageName(ProcessId, &imageFileName);
|
||||
BOOLEAN isExplorer = FALSE;
|
||||
@@ -685,6 +683,11 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PEPROCESS Process = NULL;
|
||||
if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process))) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
LARGE_INTEGER Now = { 0 };
|
||||
KeQuerySystemTime(&Now);
|
||||
BOOLEAN Result = FALSE;
|
||||
@@ -702,13 +705,13 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
|
||||
LruSlot = Current;
|
||||
}
|
||||
|
||||
if (Current->ProcessId == ProcessId) {
|
||||
if (Current->Process == Process) {
|
||||
Tracker = Current;
|
||||
break;
|
||||
}
|
||||
|
||||
BOOLEAN IsExpired = FALSE;
|
||||
if (Current->ProcessId != NULL) {
|
||||
if (Current->Process != NULL) {
|
||||
LARGE_INTEGER Diff;
|
||||
Diff.QuadPart = Now.QuadPart - Current->LastActivityTime.QuadPart;
|
||||
if (Diff.QuadPart > (RANSOM_TIME_WINDOW_MS * 10000LL)) {
|
||||
@@ -716,7 +719,7 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
|
||||
}
|
||||
}
|
||||
|
||||
if ((Current->ProcessId == NULL || IsExpired) && !CandidateSlot) {
|
||||
if ((Current->Process == NULL || IsExpired) && !CandidateSlot) {
|
||||
CandidateSlot = Current;
|
||||
}
|
||||
}
|
||||
@@ -729,7 +732,7 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
|
||||
Tracker = LruSlot;
|
||||
}
|
||||
|
||||
Tracker->ProcessId = ProcessId;
|
||||
Tracker->Process = Process;
|
||||
Tracker->ActivityCount = 0;
|
||||
Tracker->LastActivityTime = Now;
|
||||
}
|
||||
@@ -750,6 +753,7 @@ BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID Wr
|
||||
Result = TRUE;
|
||||
}
|
||||
ExReleaseFastMutex(&GlobalData.TrackerMutex);
|
||||
ObDereferenceObject(Process);
|
||||
return Result;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
"*\\Classes\\cmdfile\\*",
|
||||
"*\\Classes\\comfile\\*",
|
||||
|
||||
"*\\mouclass",
|
||||
"*\\kbdclass",
|
||||
"*\\DefaultIcon",
|
||||
"*\\DisableAntiSpyware",
|
||||
"*\\DisableWindowsUpdateAccess",
|
||||
@@ -212,6 +214,6 @@
|
||||
".mp4", ".avi", ".mov", ".wmv", ".mkv",
|
||||
".aux", ".cur", ".mui", ".ttf", ".efi",
|
||||
".cpp", ".h", ".c", ".hpp", ".cs", ".java", ".py", ".go", ".rs",
|
||||
".php", ".html", ".css", ".json", ".xml", ".sql", ".db"
|
||||
".php", ".html", ".css", ".xml", ".sql", ".db"
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user