Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cef48771b | ||
|
|
a45b10b5c3 | ||
|
|
7627d4e5d7 | ||
|
|
5c88711094 | ||
|
|
a7fdd41530 | ||
|
|
c315b322cd | ||
|
|
181bfc5b44 | ||
|
|
d9ea2b522a | ||
|
|
dc498949c7 | ||
|
|
f804a5b7f0 | ||
|
|
c3ef1da373 | ||
|
|
b0a3482f25 | ||
|
|
018b497575 | ||
|
|
130b040de1 | ||
|
|
bc0a62224a | ||
|
|
77525fc7c5 | ||
|
|
a304a2f02d | ||
|
|
9456659544 | ||
|
|
3ec340e0da | ||
|
|
f1f35badcb | ||
|
|
045160c482 | ||
|
|
559c493997 | ||
|
|
936475f5bf | ||
|
|
e97f8eed54 |
No files matched your search
@@ -588,9 +588,15 @@ class ModelPredictor:
|
||||
expected_dim = self.sess.get_inputs()[0].shape[1]
|
||||
current_dim = len(self.model_features)
|
||||
if current_dim != expected_dim:
|
||||
raise ValueError(f"Dimension mismatch! Model expects {expected_dim} features, but feature file lists {current_dim}.")
|
||||
raise ValueError(f"Dimension error: Model expects {expected_dim}, but feature file has {current_dim}")
|
||||
|
||||
self.feat_map = {feat: i for i, feat in enumerate(self.model_features)}
|
||||
|
||||
self.dll_hash_dim = 512
|
||||
self.api_hash_dim = 4096
|
||||
self.dll_hash_pad = 3
|
||||
self.api_hash_pad = 4
|
||||
self._parse_hash_dims()
|
||||
|
||||
def _load_features(self, path):
|
||||
if not os.path.exists(path):
|
||||
@@ -599,6 +605,31 @@ class ModelPredictor:
|
||||
with open(path, 'r') as f:
|
||||
return json.load(f)
|
||||
|
||||
def _parse_hash_dims(self):
|
||||
max_dll = -1
|
||||
max_api = -1
|
||||
|
||||
for feat in self.model_features:
|
||||
if feat.startswith("DllHash_"):
|
||||
try:
|
||||
val_str = feat.split("_")[1]
|
||||
max_dll = max(max_dll, int(val_str))
|
||||
self.dll_hash_pad = len(val_str)
|
||||
except Exception:
|
||||
pass
|
||||
elif feat.startswith("ApiHash_"):
|
||||
try:
|
||||
val_str = feat.split("_")[1]
|
||||
max_api = max(max_api, int(val_str))
|
||||
self.api_hash_pad = len(val_str)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if max_dll >= 0:
|
||||
self.dll_hash_dim = max_dll + 1
|
||||
if max_api >= 0:
|
||||
self.api_hash_dim = max_api + 1
|
||||
|
||||
def predict(self, raw_data):
|
||||
vec = np.zeros((1, len(self.model_features)), dtype=np.float32)
|
||||
base = raw_data.get('Base', {})
|
||||
@@ -610,14 +641,14 @@ class ModelPredictor:
|
||||
vec[0, self.feat_map[k]] = v
|
||||
|
||||
for d in dlls:
|
||||
h = zlib.crc32(d.encode('utf-8', 'ignore')) % 256
|
||||
feat_name = f"DllHash_{h:03d}"
|
||||
h = zlib.crc32(d.encode('utf-8', 'ignore')) % self.dll_hash_dim
|
||||
feat_name = f"DllHash_{h:0{self.dll_hash_pad}d}"
|
||||
if feat_name in self.feat_map:
|
||||
vec[0, self.feat_map[feat_name]] += 1.0
|
||||
|
||||
for a in apis:
|
||||
h = zlib.crc32(a.encode('utf-8', 'ignore')) % 1024
|
||||
feat_name = f"ApiHash_{h:04d}"
|
||||
h = zlib.crc32(a.encode('utf-8', 'ignore')) % self.api_hash_dim
|
||||
feat_name = f"ApiHash_{h:0{self.api_hash_pad}d}"
|
||||
if feat_name in self.feat_map:
|
||||
vec[0, self.feat_map[feat_name]] += 1.0
|
||||
|
||||
@@ -633,7 +664,7 @@ class ModelPredictor:
|
||||
elif isinstance(result, np.ndarray):
|
||||
if result.ndim == 2 and result.shape[1] > 1:
|
||||
return float(result[0][1])
|
||||
|
||||
|
||||
return 0.0
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -25,8 +25,8 @@ FEATURE_FILE = "features.json"
|
||||
|
||||
TEST_SIZE = 0.0001
|
||||
RANDOM_SEED = 42
|
||||
DLL_HASH_DIM = 256
|
||||
API_HASH_DIM = 1024
|
||||
DLL_HASH_DIM = 512
|
||||
API_HASH_DIM = 4096
|
||||
|
||||
EXCLUDE_FEATURES = {
|
||||
'label', 'filehash',
|
||||
|
||||
@@ -25,8 +25,8 @@ FEATURE_FILE = "features.json"
|
||||
|
||||
TEST_SIZE = 0.0001
|
||||
RANDOM_SEED = 42
|
||||
DLL_HASH_DIM = 256
|
||||
API_HASH_DIM = 1024
|
||||
DLL_HASH_DIM = 512
|
||||
API_HASH_DIM = 4096
|
||||
|
||||
EXCLUDE_FEATURES = {
|
||||
'label', 'filehash',
|
||||
@@ -211,7 +211,7 @@ def train_process(X, y, feature_names):
|
||||
params,
|
||||
train_data,
|
||||
valid_sets=[valid_data],
|
||||
num_boost_round=500,
|
||||
num_boost_round=300,
|
||||
callbacks=[
|
||||
lgb.log_evaluation(period=50),
|
||||
lgb.reset_parameter(learning_rate=_calculate_dynamic_lr)
|
||||
|
||||
+77
-11
@@ -179,7 +179,13 @@ window.AppI18n = {
|
||||
"Network Block": "網路攔截",
|
||||
"Driver Block": "驅動攔截",
|
||||
"System Restart": "系統重啟",
|
||||
"Clean Junk": "清理垃圾"
|
||||
"Clean Junk": "清理垃圾",
|
||||
"protect_suspend": "進程暫停",
|
||||
"protect_suspend_desc": "此選項可以暫停進程等待掃描",
|
||||
"opt_autostart": "開機啟動",
|
||||
"opt_autostart_desc": "此選項可以設定開機自動執行",
|
||||
"opt_ext_filter": "副檔名過濾",
|
||||
"opt_ext_filter_desc": "此選項僅掃描指定檔案副檔名",
|
||||
},
|
||||
"simplified_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -360,7 +366,13 @@ window.AppI18n = {
|
||||
"Network Block": "网络拦截",
|
||||
"Driver Block": "驱动拦截",
|
||||
"System Restart": "系统重启",
|
||||
"Clean Junk": "清理垃圾"
|
||||
"Clean Junk": "清理垃圾",
|
||||
"protect_suspend": "进程暂停",
|
||||
"protect_suspend_desc": "此选项可以暂停进程等待扫描",
|
||||
"opt_autostart": "开机启动",
|
||||
"opt_autostart_desc": "此选项可以设定开机自动运行",
|
||||
"opt_ext_filter": "后缀名过滤",
|
||||
"opt_ext_filter_desc": "此选项仅扫描指定文件后缀名",
|
||||
},
|
||||
"english_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -541,7 +553,13 @@ window.AppI18n = {
|
||||
"Network Block": "Network Block",
|
||||
"Driver Block": "Driver Block",
|
||||
"System Restart": "System Restart",
|
||||
"Clean Junk": "Clean Junk"
|
||||
"Clean Junk": "Clean Junk",
|
||||
"protect_suspend": "Process Suspend",
|
||||
"protect_suspend_desc": "Suspend processes while waiting for scanning",
|
||||
"opt_autostart": "Auto Start",
|
||||
"opt_autostart_desc": "Run automatically at system startup",
|
||||
"opt_ext_filter": "Suffix Filter",
|
||||
"opt_ext_filter_desc": "This option only scans specified file extensions",
|
||||
},
|
||||
"japanese_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -722,7 +740,13 @@ window.AppI18n = {
|
||||
"Network Block": "ネットワークブロック",
|
||||
"Driver Block": "ドライバーブロック",
|
||||
"System Restart": "システムの再起動",
|
||||
"Clean Junk": "ジャンククリーンアップ"
|
||||
"Clean Junk": "ジャンククリーンアップ",
|
||||
"protect_suspend": "プロセスの一時停止",
|
||||
"protect_suspend_desc": "スキャン待ちの間プロセスを一時停止します",
|
||||
"opt_autostart": "自動起動",
|
||||
"opt_autostart_desc": "システム起動時に自動的に実行します",
|
||||
"opt_ext_filter": "拡張子フィルタ",
|
||||
"opt_ext_filter_desc": "このオプションは指定された拡張子のファイルのみをスキャンします",
|
||||
},
|
||||
"korean_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -903,7 +927,13 @@ window.AppI18n = {
|
||||
"Network Block": "네트워크 차단",
|
||||
"Driver Block": "드라이버 차단",
|
||||
"System Restart": "시스템 재시작",
|
||||
"Clean Junk": "정크 정리"
|
||||
"Clean Junk": "정크 정리",
|
||||
"protect_suspend": "프로세스 일시 중지",
|
||||
"protect_suspend_desc": "스캔을 기다리는 동안 프로세스를 일시 중지합니다",
|
||||
"opt_autostart": "자동 시작",
|
||||
"opt_autostart_desc": "시스템 시작 시 자동으로 실행합니다",
|
||||
"opt_ext_filter": "확장자 필터",
|
||||
"opt_ext_filter_desc": "이 옵션은 지정된 파일 확장자만 스캔합니다",
|
||||
},
|
||||
"french_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -1084,7 +1114,13 @@ window.AppI18n = {
|
||||
"Network Block": "Réseau bloqué",
|
||||
"Driver Block": "Pilote bloqué",
|
||||
"System Restart": "Redémarrage système",
|
||||
"Clean Junk": "Nettoyage"
|
||||
"Clean Junk": "Nettoyage",
|
||||
"protect_suspend": "Suspendre processus",
|
||||
"protect_suspend_desc": "Suspend les processus en attente d'analyse",
|
||||
"opt_autostart": "Démarrage auto",
|
||||
"opt_autostart_desc": "Exécuter automatiquement au démarrage",
|
||||
"opt_ext_filter": "Filtre d'extension",
|
||||
"opt_ext_filter_desc": "Cette option analyse uniquement les extensions de fichiers spécifiées",
|
||||
},
|
||||
"spanish_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -1265,7 +1301,13 @@ window.AppI18n = {
|
||||
"Network Block": "Red bloqueada",
|
||||
"Driver Block": "Controlador bloqueado",
|
||||
"System Restart": "Reinicio del sistema",
|
||||
"Clean Junk": "Limpieza de basura"
|
||||
"Clean Junk": "Limpieza de basura",
|
||||
"protect_suspend": "Suspender procesos",
|
||||
"protect_suspend_desc": "Suspender procesos mientras espera el escaneo",
|
||||
"opt_autostart": "Inicio automático",
|
||||
"opt_autostart_desc": "Ejecutar automáticamente al inicio",
|
||||
"opt_ext_filter": "Filtro de extensión",
|
||||
"opt_ext_filter_desc": "Esta opción solo escanea extensiones de archivo especificadas",
|
||||
},
|
||||
"hindi_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -1446,7 +1488,13 @@ window.AppI18n = {
|
||||
"Network Block": "नेटवर्क अवरुद्ध",
|
||||
"Driver Block": "ड्राइवर अवरुद्ध",
|
||||
"System Restart": "सिस्टम पुनरारंभ",
|
||||
"Clean Junk": "कचरा साफ़ करें"
|
||||
"Clean Junk": "कचरा साफ़ करें",
|
||||
"protect_suspend": "प्रक्रिया रोकें",
|
||||
"protect_suspend_desc": "स्कैन की प्रतीक्षा करते समय प्रक्रियाओं को रोकें",
|
||||
"opt_autostart": "स्वतः प्रारंभ",
|
||||
"opt_autostart_desc": "सिस्टम स्टार्टअप पर स्वचालित रूप से चलाएं",
|
||||
"opt_ext_filter": "एक्सटेंशन फ़िल्टर",
|
||||
"opt_ext_filter_desc": "यह विकल्प केवल निर्दिष्ट फ़ाइल एक्सटेंशन को स्कैन करता है",
|
||||
},
|
||||
"arabic_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -1627,7 +1675,13 @@ window.AppI18n = {
|
||||
"Network Block": "حظر شبكة",
|
||||
"Driver Block": "حظر محرك",
|
||||
"System Restart": "إعادة تشغيل النظام",
|
||||
"Clean Junk": "تنظيف المخلفات"
|
||||
"Clean Junk": "تنظيف المخلفات",
|
||||
"protect_suspend": "تعليق العمليات",
|
||||
"protect_suspend_desc": "تعليق العمليات أثناء انتظار الفحص",
|
||||
"opt_autostart": "بدء تلقائي",
|
||||
"opt_autostart_desc": "التشغيل تلقائيًا عند بدء التشغيل",
|
||||
"opt_ext_filter": "تصفية الامتداد",
|
||||
"opt_ext_filter_desc": "يقوم هذا الخيار بفحص امتدادات الملفات المحددة فقط",
|
||||
},
|
||||
"russian_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -1808,7 +1862,13 @@ window.AppI18n = {
|
||||
"Network Block": "Блокировка сети",
|
||||
"Driver Block": "Блокировка драйвера",
|
||||
"System Restart": "Перезапуск системы",
|
||||
"Clean Junk": "Очистка мусора"
|
||||
"Clean Junk": "Очистка мусора",
|
||||
"protect_suspend": "Приостановка процессов",
|
||||
"protect_suspend_desc": "Приостановка процессов в ожидании сканирования",
|
||||
"opt_autostart": "Автозапуск",
|
||||
"opt_autostart_desc": "Автоматический запуск при старте системы",
|
||||
"opt_ext_filter": "Фильтр расширений",
|
||||
"opt_ext_filter_desc": "Этот параметр сканирует только указанные расширения файлов",
|
||||
},
|
||||
"slovenian_switch": {
|
||||
"app_name": "PYAS Security",
|
||||
@@ -1989,7 +2049,13 @@ window.AppI18n = {
|
||||
"Network Block": "Omrežje blokirano",
|
||||
"Driver Block": "Gonilnik blokiran",
|
||||
"System Restart": "Ponovni zagon sistema",
|
||||
"Clean Junk": "Čiščenje nesnage"
|
||||
"Clean Junk": "Čiščenje nesnage",
|
||||
"protect_suspend": "Začasna ustavitev",
|
||||
"protect_suspend_desc": "Zaustavite procese med čakanjem na skeniranje",
|
||||
"opt_autostart": "Samodejni zagon",
|
||||
"opt_autostart_desc": "Samodejni zagon ob zagonu sistema",
|
||||
"opt_ext_filter": "Filter končnic",
|
||||
"opt_ext_filter_desc": "Ta možnost skenira samo določene končnice datotek",
|
||||
}
|
||||
},
|
||||
langMap: {
|
||||
|
||||
+37
-26
@@ -85,6 +85,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
startup: [{key: 'type', label: 'col_type', flex: 0.5, isI18n: true}, {key: 'name', label: 'col_name', flex: 1.5}, {key: 'status', label: 'col_status', flex: 0.5, isI18n: true}, {key: 'path', label: 'col_path', flex: 3}],
|
||||
virus: [{key: 'label', label: 'col_type', flex: 1, isI18n: true}, {key: 'path', label: 'col_path', flex: 3}],
|
||||
pathOnly: [{key: 'path', label: 'col_path', flex: 1}],
|
||||
fileList: [{key: 'time_str', label: 'col_date', flex: 1.5}, {key: 'path', label: 'col_path', flex: 5}],
|
||||
junk: [{key: 'path', label: 'col_path', flex: 3}, {key: 'sizeStr', label: 'col_size', flex: 1}],
|
||||
popup: [{key: 'exe', label: 'col_prog', flex: 1}, {key: 'class', label: 'col_class', flex: 1}, {key: 'title', label: 'col_title', flex: 2}],
|
||||
repair: [{key: 'display', label: 'col_repair', flex: 1, isI18n: true}, {key: 'path', label: 'col_path', flex: 3}],
|
||||
@@ -446,23 +447,23 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
};
|
||||
|
||||
const checkVirusListEmpty = () => {
|
||||
updateCustomSelectUI('scan_method_select', 'none');
|
||||
if (appState.virusResults.length === 0) {
|
||||
appState.virusMap.clear();
|
||||
changeScanSelectMode('scan');
|
||||
const title = document.querySelector('#scan_window .section-title');
|
||||
const text = document.getElementById('progress_text');
|
||||
if (title) {
|
||||
title.setAttribute('data-i18n', 'scan_virus');
|
||||
title.textContent = getMsg('scan_virus');
|
||||
updateCustomSelectUI('scan_method_select', 'none');
|
||||
if (appState.virusResults.length === 0) {
|
||||
appState.virusMap.clear();
|
||||
changeScanSelectMode('scan');
|
||||
const title = document.querySelector('#scan_window .section-title');
|
||||
const text = document.getElementById('progress_text');
|
||||
if (title) {
|
||||
title.setAttribute('data-i18n', 'scan_virus');
|
||||
title.textContent = getMsg('scan_virus');
|
||||
}
|
||||
if (text) {
|
||||
text.setAttribute('data-i18n', 'scan_virus_desc');
|
||||
text.removeAttribute('data-dynamic-msg');
|
||||
text.textContent = getMsg('scan_virus_desc');
|
||||
}
|
||||
}
|
||||
if (text) {
|
||||
text.setAttribute('data-i18n', 'scan_virus_desc');
|
||||
text.removeAttribute('data-dynamic-msg');
|
||||
text.textContent = getMsg('scan_virus_desc');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const triggerScan = (method) => {
|
||||
if (!window.pywebview) return;
|
||||
@@ -594,9 +595,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const refreshConfigLists = () => {
|
||||
if (!window.pywebview) return;
|
||||
window.pywebview.api.get_config().then(cfg => {
|
||||
renderDataGrid('#whitelist_window', (cfg.white_list || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
|
||||
renderDataGrid('#quarantine_window', (cfg.quarantine || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
|
||||
renderDataGrid('#custom_protect_window', (cfg.custom_rule || []).map(i => ({path: i.file || i})), cols.pathOnly, 'path');
|
||||
const formatTime = (ts) => {
|
||||
if (!ts) return '-';
|
||||
const d = new Date(ts * 1000);
|
||||
const pad = n => n.toString().padStart(2, '0');
|
||||
return `[${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}]`;
|
||||
};
|
||||
const mapList = (arr) => (arr || []).map(i => ({
|
||||
path: i.file || (typeof i === 'string' ? i : ''),
|
||||
time_str: formatTime(i.time)
|
||||
}));
|
||||
renderDataGrid('#whitelist_window', mapList(cfg.white_list), cols.fileList, 'path');
|
||||
renderDataGrid('#quarantine_window', mapList(cfg.quarantine), cols.fileList, 'path');
|
||||
renderDataGrid('#custom_protect_window', mapList(cfg.custom_rule), cols.fileList, 'path');
|
||||
const popupRules = (cfg.block_list || []).map(item => ({ exe: item.exe || '*', class: item.class || '*', title: item.title || '*', value: item.exe || item.title }));
|
||||
renderDataGrid('#popup_window', popupRules, cols.popup, 'value');
|
||||
});
|
||||
@@ -770,7 +781,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
|
||||
toggle.addEventListener('change', async (e) => {
|
||||
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
|
||||
const switchMap = ["process_switch", "suspend_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "suffix_switch", "autostart_switch", "context_switch"];
|
||||
const key = switchMap[index];
|
||||
if (window.pywebview && key) {
|
||||
toggle.disabled = true;
|
||||
@@ -1054,9 +1065,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
renderDataGrid('#startup_window', [], cols.startup, 'id', false);
|
||||
renderDataGrid('#junk_window', [], cols.junk, 'path', true);
|
||||
renderDataGrid('#repair_window', [], cols.repair, 'id', true);
|
||||
renderDataGrid('#whitelist_window', [], cols.pathOnly, 'path', false);
|
||||
renderDataGrid('#quarantine_window', [], cols.pathOnly, 'path', false);
|
||||
renderDataGrid('#custom_protect_window', [], cols.pathOnly, 'path', false);
|
||||
renderDataGrid('#whitelist_window', [], cols.fileList, 'path', false);
|
||||
renderDataGrid('#quarantine_window', [], cols.fileList, 'path', false);
|
||||
renderDataGrid('#custom_protect_window', [], cols.fileList, 'path', false);
|
||||
renderDataGrid('#popup_window', [], cols.popup, 'value', false);
|
||||
renderDataGrid('#log_export_window', [], cols.log, 'id', true);
|
||||
renderDataGrid('#netmon_window', [], cols.netmon, 'pid', false);
|
||||
@@ -1071,7 +1082,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
updateCustomSelectUI('theme_select', theme);
|
||||
updateCustomSelectUI('lang_select', lang);
|
||||
|
||||
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
|
||||
const switchMap = ["process_switch", "suspend_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "suffix_switch", "autostart_switch", "context_switch"];
|
||||
document.querySelectorAll('.toggle-switch input').forEach((toggle, index) => {
|
||||
if (appState.firstLaunch) {
|
||||
toggle.checked = false;
|
||||
@@ -1091,8 +1102,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
window.onEngineReady = async () => {
|
||||
if (!appState.firstLaunch) return;
|
||||
await window.pywebview.api.update_config("first_launch", false);
|
||||
const switchMap = ["process_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "context_switch"];
|
||||
const seq = ["cloud_switch", "process_switch", "document_switch", "system_switch", "network_switch", "driver_switch"];
|
||||
const switchMap = ["process_switch", "suspend_switch", "document_switch", "system_switch", "driver_switch", "network_switch", "sensitive_switch", "extension_switch", "cloud_switch", "suffix_switch", "autostart_switch", "context_switch"];
|
||||
const seq = ["cloud_switch", "autostart_switch", "suffix_switch", "process_switch", "suspend_switch", "document_switch", "system_switch", "network_switch", "driver_switch", "context_switch"];
|
||||
|
||||
for (const key of seq) {
|
||||
const index = switchMap.indexOf(key);
|
||||
|
||||
@@ -109,6 +109,12 @@
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>進程暫停</h2><p data-i18n>此選項可以暫停進程等待掃描</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>檔案防護</h2><p data-i18n>此選項可以隔離惡意檔案建立</p>
|
||||
@@ -215,6 +221,18 @@
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>副檔名過濾</h2><p data-i18n>此選項僅掃描指定檔案副檔名</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>開機啟動</h2><p data-i18n>此選項可以設定開機自動執行</p>
|
||||
</div>
|
||||
<label class="toggle-switch"><input type="checkbox" checked><span class="slider"></span></label>
|
||||
</div>
|
||||
<div class="list-item">
|
||||
<div class="item-info">
|
||||
<h2 data-i18n>選單掃描</h2><p data-i18n>此選項可以啟用右鍵掃描功能</p>
|
||||
@@ -284,34 +302,64 @@
|
||||
</div>
|
||||
<div class="license-widget">
|
||||
<textarea class="license-text" readonly>87owo Publicly Available Source Code License Agreement
|
||||
Last updated: 2025-12-19
|
||||
|
||||
1. License and Ownership
|
||||
- This software and its source code are source available for public view.
|
||||
- Access or execution of certain features may require an activation key.
|
||||
- 87owo reserves all legal rights, no intellectual property is transferred.
|
||||
- All project archives, source code, and binaries remain fully proprietary.
|
||||
Last Updated: 2026-06-09
|
||||
IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE LICENSE APPROVED BY THE OPEN SOURCE INITIATIVE (OSI). BY DOWNLOADING, ACCESSING, OR USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THESE TERMS.
|
||||
|
||||
2. Use and Prohibitions
|
||||
- You may view, download, and modify the code for local and personal use.
|
||||
- Redistribution of any original or modified code requires prior consent.
|
||||
- Commercial hosting or SaaS deployment is prohibited without permission.
|
||||
- Reverse engineering, license removal, or illegal use is strictly barred.
|
||||
1. Definitions and Interpretation
|
||||
|
||||
3. Data and Privacy
|
||||
- This project may collect operational data for optimization and analysis.
|
||||
- All collected data is strictly anonymized to ensure the user's privacy.
|
||||
- Transmission of information is encrypted to prevent unauthorized access.
|
||||
- 1.1. Author: Refers to 87owo (PYAS Security).
|
||||
- 1.2. Software: Refers to the source code, binaries, project archives, assets, and documentation associated with this repository or distribution.
|
||||
- 1.3. Personal Local Use: Means viewing, downloading, executing, and modifying the Software strictly on local hardware owned or directly controlled by an individual for private, non-commercial, non-production, and non-distribution purposes.
|
||||
- 1.4. Enterprise Use: Includes enterprise internal deployment, multi-user institutional use, or utilization within any corporate environment, which is strictly excluded from Personal Local Use.
|
||||
|
||||
4. Updates and Terms
|
||||
- New releases may include critical patches, enhancements, or UI changes.
|
||||
- Continued use of these services constitutes acceptance of updated terms.
|
||||
- The author reserves the right to modify these license terms at any time.
|
||||
2. Intellectual Property and Ownership
|
||||
|
||||
5. Warranty and Liability
|
||||
- All services and assets are provided "as is" without any form of warranty.
|
||||
- 87owo is not liable for any direct or indirect damages from software use.
|
||||
- Users assume all risks regarding performance, data, and system integrity.
|
||||
- 2.1. Retention of Rights: The Author reserves all legal rights, titles, and interests in and to the Software, including but not limited to all copyrights, trademarks, patents, and trade secrets.
|
||||
- 2.2. No Transfer: No intellectual property rights or ownership stakes are transferred under this Agreement. All project components remain fully proprietary.
|
||||
- 2.3. Activation Keys: Access to or execution of certain premium features, modules, or binary distributions within the Software may require a valid, authorized activation key issued solely by the Author.
|
||||
|
||||
3. Grant of License and Permitted Use
|
||||
|
||||
Subject to your strict compliance with all terms of this Agreement, the Author hereby grants you a limited, non-exclusive, world-wide, royalty-free, revocable, and non-transferable license to:
|
||||
- 3.1. Code Inspection: View and download the source code from official authorized repositories.
|
||||
- 3.2. Local Modification: Modify the source code strictly for the purposes of Personal Local Use.
|
||||
- 3.3. Local Execution: Execute the Software locally for personal evaluation and private deployment.
|
||||
|
||||
4. Prohibitions and Restrictions
|
||||
|
||||
Except as explicitly granted in Section 3, you shall not, and shall not permit any third party to:
|
||||
- 4.1. Unauthorized Redistribution: Copy, redistribute, sublicense, republish, mirror, or publicly disclose any original or modified versions of the source code, binaries, or assets.
|
||||
- 4.2. Commercial and SaaS Deployment: Host, deploy, embed, or utilize the Software for commercial purposes, including but not limited to Software-as-a-Service (SaaS), commercial web hosting, paid remote execution, cloud infrastructure integration, or enterprise internal utility.
|
||||
- 4.3. Reverse Engineering: Reverse engineer, decompile, decrypt, or disassemble any binary components of the Software, except and only to the extent that such restriction is explicitly prohibited by applicable mandatory laws.
|
||||
- 4.4. Notice Preservation: Remove, obscure, deface, or alter any copyright notices, trademarks, hyperlinked terms, or license files included within the Software distribution.
|
||||
|
||||
5. Prior Authorization and Application Channels
|
||||
|
||||
- 5.1. Authorization Requirement: Any activity exceeding the scope of Personal Local Use (including but not limited to public redistribution, independent forks not targeting upstream contributions, commercial deployment, or enterprise use) requires prior explicit written consent from the Author.
|
||||
- 5.2. Official Application Channels: Users seeking such authorization must submit a formal request through one of the following official channels:
|
||||
- Channel A (GitHub Fork): Initiating a standard Fork on the official GitHub repository for collaborative and upstream contributions.
|
||||
- Channel B (GitHub Issues): Submitting a public issue utilizing the designated authorization request template.
|
||||
- Channel C (Official Email): Sending a detailed inquiry to the official organizational email address associated with the PYAS Security project.
|
||||
- 5.3. Request Content: Any application must explicitly state the specific use case, intended target audience, scope of deployment, and whether any commercial monetization is involved.
|
||||
|
||||
6. Updates, Amendments, and Scope of Terms
|
||||
|
||||
- 6.1. New Releases: The Author may deliver updates, critical security patches, feature enhancements, or user interface modifications. Continued use of the Software constitutes acceptance of the latest distributed version.
|
||||
- 6.2. Non-Retroactivity: The Author reserves the right to amend this License Agreement at any time. Amendments apply strictly to subsequent versions or new downloads. Copies lawfully acquired prior to such modification remain governed by the exact version of the License under which they were originally obtained.
|
||||
|
||||
7. Telemetry, Data, and Privacy
|
||||
|
||||
- 7.1. Operational Data Collection: The Software may collect anonymized technical, operational, and usage data strictly for security analysis, performance optimization, and bug reporting.
|
||||
- 7.2. Cloud Sample Submission: To enhance threat detection and malware analysis, the Software may automatically or manually transmit suspicious executable files and software samples to the Author's cloud infrastructure for deep analysis.
|
||||
- 7.3. Scope of Collected Extensions: Sample collection targets potential executable and system binary formats, which explicitly include, but are not limited to, files with the following extensions: `.exe`, `.dll`, `.sys`, `.ocx`, `.scr`, `.efi`, `.acm`, `.ax`, `.cpl`, `.drv`, `.com`, `.mui`, `.pyd`, `.wfx`, `.api`, `.awx`, `.rll`, and `.winmd`.
|
||||
- 7.4. Public Disclosure of Threat Intelligence: You explicitly acknowledge and agree that the metadata, behavioral reports, and threat intelligence data generated from the cloud analysis of submitted samples may be publicly disclosed, published, or shared with the cybersecurity community for collaborative defense purposes.
|
||||
- 7.5. Opt-Out Mechanism: Cloud-based telemetry and sample submission are fully optional. Users who do not wish to participate in sample collection or cloud-assisted analysis may disable the corresponding cloud services at any time via the toggle switches provided within the Software’s settings interface.
|
||||
|
||||
8. Warranty Disclaimer and Limitation of Liability
|
||||
|
||||
- 8.1. Disclaimer of Warranty: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE AND ALL RELATED ASSETS ARE PROVIDED ON AN "AS IS" AND "AS AVAILABLE" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
- 8.2. Limitation of Liability: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL THE AUTHOR, COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE. USERS ASSUME ALL RISKS REGARDING PERFORMANCE, DATA INTEGRITY, AND SYSTEM STABILITY.
|
||||
|
||||
© 2020-2026 87owo (PYAS Security). All rights reserved.</textarea>
|
||||
</div>
|
||||
|
||||
+53
-23
@@ -1,31 +1,61 @@
|
||||
87owo Publicly Available Source Code License Agreement
|
||||
Last updated: 2025-12-19
|
||||
|
||||
1. License and Ownership
|
||||
- This software and its source code are source available for public view.
|
||||
- Access or execution of certain features may require an activation key.
|
||||
- 87owo reserves all legal rights, no intellectual property is transferred.
|
||||
- All project archives, source code, and binaries remain fully proprietary.
|
||||
Last Updated: 2026-06-09
|
||||
IMPORTANT NOTICE: THIS LICENSE IS "SOURCE AVAILABLE" AND IS NOT AN OPEN SOURCE LICENSE APPROVED BY THE OPEN SOURCE INITIATIVE (OSI). BY DOWNLOADING, ACCESSING, OR USING THE SOFTWARE, YOU AGREE TO BE BOUND BY THESE TERMS.
|
||||
|
||||
2. Use and Prohibitions
|
||||
- You may view, download, and modify the code for local and personal use.
|
||||
- Redistribution of any original or modified code requires prior consent.
|
||||
- Commercial hosting or SaaS deployment is prohibited without permission.
|
||||
- Reverse engineering, license removal, or illegal use is strictly barred.
|
||||
1. Definitions and Interpretation
|
||||
|
||||
3. Data and Privacy
|
||||
- This project may collect operational data for optimization and analysis.
|
||||
- All collected data is strictly anonymized to ensure the user's privacy.
|
||||
- Transmission of information is encrypted to prevent unauthorized access.
|
||||
- 1.1. Author: Refers to 87owo (PYAS Security).
|
||||
- 1.2. Software: Refers to the source code, binaries, project archives, assets, and documentation associated with this repository or distribution.
|
||||
- 1.3. Personal Local Use: Means viewing, downloading, executing, and modifying the Software strictly on local hardware owned or directly controlled by an individual for private, non-commercial, non-production, and non-distribution purposes.
|
||||
- 1.4. Enterprise Use: Includes enterprise internal deployment, multi-user institutional use, or utilization within any corporate environment, which is strictly excluded from Personal Local Use.
|
||||
|
||||
4. Updates and Terms
|
||||
- New releases may include critical patches, enhancements, or UI changes.
|
||||
- Continued use of these services constitutes acceptance of updated terms.
|
||||
- The author reserves the right to modify these license terms at any time.
|
||||
2. Intellectual Property and Ownership
|
||||
|
||||
5. Warranty and Liability
|
||||
- All services and assets are provided "as is" without any form of warranty.
|
||||
- 87owo is not liable for any direct or indirect damages from software use.
|
||||
- Users assume all risks regarding performance, data, and system integrity.
|
||||
- 2.1. Retention of Rights: The Author reserves all legal rights, titles, and interests in and to the Software, including but not limited to all copyrights, trademarks, patents, and trade secrets.
|
||||
- 2.2. No Transfer: No intellectual property rights or ownership stakes are transferred under this Agreement. All project components remain fully proprietary.
|
||||
- 2.3. Activation Keys: Access to or execution of certain premium features, modules, or binary distributions within the Software may require a valid, authorized activation key issued solely by the Author.
|
||||
|
||||
3. Grant of License and Permitted Use
|
||||
|
||||
Subject to your strict compliance with all terms of this Agreement, the Author hereby grants you a limited, non-exclusive, world-wide, royalty-free, revocable, and non-transferable license to:
|
||||
- 3.1. Code Inspection: View and download the source code from official authorized repositories.
|
||||
- 3.2. Local Modification: Modify the source code strictly for the purposes of Personal Local Use.
|
||||
- 3.3. Local Execution: Execute the Software locally for personal evaluation and private deployment.
|
||||
|
||||
4. Prohibitions and Restrictions
|
||||
|
||||
Except as explicitly granted in Section 3, you shall not, and shall not permit any third party to:
|
||||
- 4.1. Unauthorized Redistribution: Copy, redistribute, sublicense, republish, mirror, or publicly disclose any original or modified versions of the source code, binaries, or assets.
|
||||
- 4.2. Commercial and SaaS Deployment: Host, deploy, embed, or utilize the Software for commercial purposes, including but not limited to Software-as-a-Service (SaaS), commercial web hosting, paid remote execution, cloud infrastructure integration, or enterprise internal utility.
|
||||
- 4.3. Reverse Engineering: Reverse engineer, decompile, decrypt, or disassemble any binary components of the Software, except and only to the extent that such restriction is explicitly prohibited by applicable mandatory laws.
|
||||
- 4.4. Notice Preservation: Remove, obscure, deface, or alter any copyright notices, trademarks, hyperlinked terms, or license files included within the Software distribution.
|
||||
|
||||
5. Prior Authorization and Application Channels
|
||||
|
||||
- 5.1. Authorization Requirement: Any activity exceeding the scope of Personal Local Use (including but not limited to public redistribution, independent forks not targeting upstream contributions, commercial deployment, or enterprise use) requires prior explicit written consent from the Author.
|
||||
- 5.2. Official Application Channels: Users seeking such authorization must submit a formal request through one of the following official channels:
|
||||
- Channel A (GitHub Fork): Initiating a standard Fork on the official GitHub repository for collaborative and upstream contributions.
|
||||
- Channel B (GitHub Issues): Submitting a public issue utilizing the designated authorization request template.
|
||||
- Channel C (Official Email): Sending a detailed inquiry to the official organizational email address associated with the PYAS Security project.
|
||||
- 5.3. Request Content: Any application must explicitly state the specific use case, intended target audience, scope of deployment, and whether any commercial monetization is involved.
|
||||
|
||||
6. Updates, Amendments, and Scope of Terms
|
||||
|
||||
- 6.1. New Releases: The Author may deliver updates, critical security patches, feature enhancements, or user interface modifications. Continued use of the Software constitutes acceptance of the latest distributed version.
|
||||
- 6.2. Non-Retroactivity: The Author reserves the right to amend this License Agreement at any time. Amendments apply strictly to subsequent versions or new downloads. Copies lawfully acquired prior to such modification remain governed by the exact version of the License under which they were originally obtained.
|
||||
|
||||
7. Telemetry, Data, and Privacy
|
||||
|
||||
- 7.1. Operational Data Collection: The Software may collect anonymized technical, operational, and usage data strictly for security analysis, performance optimization, and bug reporting.
|
||||
- 7.2. Cloud Sample Submission: To enhance threat detection and malware analysis, the Software may automatically or manually transmit suspicious executable files and software samples to the Author's cloud infrastructure for deep analysis.
|
||||
- 7.3. Scope of Collected Extensions: Sample collection targets potential executable and system binary formats, which explicitly include, but are not limited to, files with the following extensions: `.exe`, `.dll`, `.sys`, `.ocx`, `.scr`, `.efi`, `.acm`, `.ax`, `.cpl`, `.drv`, `.com`, `.mui`, `.pyd`, `.wfx`, `.api`, `.awx`, `.rll`, and `.winmd`.
|
||||
- 7.4. Public Disclosure of Threat Intelligence: You explicitly acknowledge and agree that the metadata, behavioral reports, and threat intelligence data generated from the cloud analysis of submitted samples may be publicly disclosed, published, or shared with the cybersecurity community for collaborative defense purposes.
|
||||
- 7.5. Opt-Out Mechanism: Cloud-based telemetry and sample submission are fully optional. Users who do not wish to participate in sample collection or cloud-assisted analysis may disable the corresponding cloud services at any time via the toggle switches provided within the Software’s settings interface.
|
||||
|
||||
8. Warranty Disclaimer and Limitation of Liability
|
||||
|
||||
- 8.1. Disclaimer of Warranty: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE AND ALL RELATED ASSETS ARE PROVIDED ON AN "AS IS" AND "AS AVAILABLE" BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||||
- 8.2. Limitation of Liability: TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL THE AUTHOR, COPYRIGHT HOLDERS, OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE. USERS ASSUME ALL RISKS REGARDING PERFORMANCE, DATA INTEGRITY, AND SYSTEM STABILITY.
|
||||
|
||||
© 2020-2026 87owo (PYAS Security). All rights reserved.
|
||||
@@ -1,8 +1,8 @@
|
||||
import os, re, gc, io, csv, sys, time, json, uuid, stat, queue, msvcrt, winreg
|
||||
import shutil, hashlib, platform, threading, subprocess, pefile, pystray
|
||||
import socket, requests, webview, webbrowser, ctypes, ctypes.wintypes
|
||||
import os, re, gc, io, csv, sys, time, json, uuid, stat, queue, msvcrt
|
||||
import shutil, hashlib, platform, threading, subprocess, pefile, winreg
|
||||
import pystray, requests, webview, webbrowser, ctypes, ctypes.wintypes
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
from PIL import Image
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from http.server import SimpleHTTPRequestHandler
|
||||
from socketserver import TCPServer
|
||||
@@ -36,12 +36,6 @@ class MIB_TCPROW_OWNER_PID(ctypes.Structure):
|
||||
("dwOwningPid", ctypes.wintypes.DWORD)
|
||||
]
|
||||
|
||||
class MIB_TCPTABLE_OWNER_PID(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("dwNumEntries", ctypes.wintypes.DWORD),
|
||||
("table", MIB_TCPROW_OWNER_PID * 1)
|
||||
]
|
||||
|
||||
class FILE_NOTIFY_INFORMATION(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("NextEntryOffset", ctypes.wintypes.DWORD),
|
||||
@@ -50,17 +44,6 @@ class FILE_NOTIFY_INFORMATION(ctypes.Structure):
|
||||
("FileName", ctypes.wintypes.WCHAR * 1024)
|
||||
]
|
||||
|
||||
class SERVICE_STATUS(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("dwServiceType", ctypes.wintypes.DWORD),
|
||||
("dwCurrentState", ctypes.wintypes.DWORD),
|
||||
("dwControlsAccepted", ctypes.wintypes.DWORD),
|
||||
("dwWin32ExitCode", ctypes.wintypes.DWORD),
|
||||
("dwServiceSpecificExitCode", ctypes.wintypes.DWORD),
|
||||
("dwCheckPoint", ctypes.wintypes.DWORD),
|
||||
("dwWaitHint", ctypes.wintypes.DWORD)
|
||||
]
|
||||
|
||||
class PROCESS_BASIC_INFORMATION(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("Reserved1", ctypes.wintypes.LPVOID),
|
||||
@@ -137,6 +120,20 @@ class MEMORY_BASIC_INFORMATION(ctypes.Structure):
|
||||
("Type", ctypes.wintypes.DWORD)
|
||||
]
|
||||
|
||||
class POINT(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("x", ctypes.c_long),
|
||||
("y", ctypes.c_long)
|
||||
]
|
||||
|
||||
class RECT(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("left", ctypes.c_long),
|
||||
("top", ctypes.c_long),
|
||||
("right", ctypes.c_long),
|
||||
("bottom", ctypes.c_long)
|
||||
]
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class WindowAPI:
|
||||
@@ -232,13 +229,12 @@ class WindowAPI:
|
||||
self.file_config = os.path.join(self.path_config, "PYAS", "Config.json")
|
||||
self.file_log = os.path.join(self.path_config, "PYAS", "Report.json")
|
||||
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")
|
||||
self.path_protect = os.path.join(self.path_pyas, "Plugins", "Filter")
|
||||
self.path_drivers = os.path.join(self.path_protect, "PYAS_Driver.sys")
|
||||
|
||||
def init_windll(self):
|
||||
for name in ["ntdll", "Psapi", "user32", "kernel32", "advapi32", "iphlpapi", "shell32", "fltlib"]:
|
||||
for name in ["ntdll", "Psapi", "user32", "kernel32", "iphlpapi", "shell32", "fltlib"]:
|
||||
try:
|
||||
setattr(self, name.lower(), ctypes.WinDLL(name, use_last_error=True))
|
||||
except Exception as e:
|
||||
@@ -251,20 +247,19 @@ class WindowAPI:
|
||||
self.user32.SendMessageTimeoutW.argtypes = [ctypes.wintypes.HWND, ctypes.wintypes.UINT, ctypes.wintypes.WPARAM, ctypes.c_void_p, ctypes.wintypes.UINT, ctypes.wintypes.UINT, ctypes.c_void_p]
|
||||
self.user32.SendMessageTimeoutW.restype = ctypes.wintypes.LPARAM
|
||||
|
||||
self.advapi32.OpenSCManagerW.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.wintypes.DWORD]
|
||||
self.advapi32.OpenSCManagerW.restype = ctypes.wintypes.HANDLE
|
||||
self.advapi32.CreateServiceW.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.wintypes.DWORD, ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.POINTER(ctypes.wintypes.DWORD), ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_wchar_p]
|
||||
self.advapi32.CreateServiceW.restype = ctypes.wintypes.HANDLE
|
||||
self.advapi32.OpenServiceW.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_wchar_p, ctypes.wintypes.DWORD]
|
||||
self.advapi32.OpenServiceW.restype = ctypes.wintypes.HANDLE
|
||||
self.advapi32.StartServiceW.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.POINTER(ctypes.c_wchar_p)]
|
||||
self.advapi32.StartServiceW.restype = ctypes.wintypes.BOOL
|
||||
self.advapi32.ControlService.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD, ctypes.POINTER(SERVICE_STATUS)]
|
||||
self.advapi32.ControlService.restype = ctypes.wintypes.BOOL
|
||||
self.advapi32.DeleteService.argtypes = [ctypes.wintypes.HANDLE]
|
||||
self.advapi32.DeleteService.restype = ctypes.wintypes.BOOL
|
||||
self.advapi32.CloseServiceHandle.argtypes = [ctypes.wintypes.HANDLE]
|
||||
self.advapi32.CloseServiceHandle.restype = ctypes.wintypes.BOOL
|
||||
self.user32.WindowFromPoint.argtypes = [POINT]
|
||||
self.user32.WindowFromPoint.restype = ctypes.wintypes.HWND
|
||||
self.user32.GetAncestor.argtypes = [ctypes.wintypes.HWND, ctypes.c_uint]
|
||||
self.user32.GetAncestor.restype = ctypes.wintypes.HWND
|
||||
self.user32.GetCursorPos.argtypes = [ctypes.POINTER(POINT)]
|
||||
self.user32.GetCursorPos.restype = ctypes.wintypes.BOOL
|
||||
self.user32.GetWindowRect.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(RECT)]
|
||||
self.user32.GetWindowRect.restype = ctypes.wintypes.BOOL
|
||||
self.user32.SetWindowPos.argtypes = [ctypes.wintypes.HWND, ctypes.wintypes.HWND, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.wintypes.UINT]
|
||||
self.user32.SetWindowPos.restype = ctypes.wintypes.BOOL
|
||||
self.user32.SetLayeredWindowAttributes.argtypes = [ctypes.wintypes.HWND, ctypes.wintypes.DWORD, ctypes.c_byte, ctypes.wintypes.DWORD]
|
||||
self.user32.SetLayeredWindowAttributes.restype = ctypes.wintypes.BOOL
|
||||
self.user32.CreateWindowExW.restype = ctypes.wintypes.HWND
|
||||
|
||||
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
|
||||
@@ -311,11 +306,9 @@ class WindowAPI:
|
||||
self.kernel32.GetProcessIoCounters.restype = ctypes.wintypes.BOOL
|
||||
self.kernel32.VirtualQueryEx.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_void_p, ctypes.POINTER(MEMORY_BASIC_INFORMATION), ctypes.c_size_t]
|
||||
self.kernel32.VirtualQueryEx.restype = ctypes.c_size_t
|
||||
self.kernel32.SetProcessWorkingSetSize.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_size_t, ctypes.c_size_t]
|
||||
self.kernel32.SetProcessWorkingSetSize.restype = ctypes.wintypes.BOOL
|
||||
|
||||
self.psapi.EnumProcessModulesEx.argtypes = [ctypes.wintypes.HANDLE, ctypes.POINTER(ctypes.wintypes.HMODULE), ctypes.wintypes.DWORD, ctypes.POINTER(ctypes.wintypes.DWORD), ctypes.wintypes.DWORD]
|
||||
self.psapi.EnumProcessModulesEx.restype = ctypes.wintypes.BOOL
|
||||
self.psapi.GetModuleFileNameExW.argtypes = [ctypes.wintypes.HANDLE, ctypes.wintypes.HMODULE, ctypes.c_wchar_p, ctypes.wintypes.DWORD]
|
||||
self.psapi.GetModuleFileNameExW.restype = ctypes.wintypes.DWORD
|
||||
self.psapi.GetMappedFileNameW.argtypes = [ctypes.wintypes.HANDLE, ctypes.c_void_p, ctypes.wintypes.LPWSTR, ctypes.wintypes.DWORD]
|
||||
self.psapi.GetMappedFileNameW.restype = ctypes.wintypes.DWORD
|
||||
|
||||
@@ -346,6 +339,7 @@ class WindowAPI:
|
||||
self.cloud_pending = set()
|
||||
self.last_io_counters = {}
|
||||
self.last_io_time = time.time()
|
||||
self.suspended_procs = set()
|
||||
|
||||
self.lock_driver = threading.RLock()
|
||||
self.lock_update = threading.RLock()
|
||||
@@ -355,16 +349,17 @@ class WindowAPI:
|
||||
self.lock_io = threading.RLock()
|
||||
|
||||
self.pyas_default = {
|
||||
"version": "3.5.9",
|
||||
"version": "3.6.1",
|
||||
"api_host": "https://pyas-security.com/",
|
||||
"api_key": "fBRZxYS1UxykM-qzNOlKOEl63WILzlvgNMn6QfsG6FXCAAIktCrOPTAfY5_hEyuZ",
|
||||
"suffix": [".exe", ".dll", ".sys", ".ocx", ".scr", ".efi", ".acm", ".ax", ".cpl", ".drv", ".com", ".mui", ".pyd", ".wfx", ".api", ".awx", ".rll", ".winmd", ".ax"],
|
||||
"suffix": [".exe", ".dll", ".sys", ".ocx", ".scr", ".efi", ".acm", ".ax", ".cpl", ".drv", ".com", ".mui", ".pyd", ".wfx", ".api", ".awx", ".rll", ".winmd"],
|
||||
"block": [2001, 3001, 5001, 6001],
|
||||
"size": 100 * 1024 * 1024,
|
||||
"size": 256 * 1024 * 1024,
|
||||
"language": "english_switch",
|
||||
"theme": "system_switch",
|
||||
"first_launch": True,
|
||||
"process_switch": False,
|
||||
"suspend_switch": True,
|
||||
"document_switch": False,
|
||||
"system_switch": False,
|
||||
"driver_switch": False,
|
||||
@@ -372,7 +367,9 @@ class WindowAPI:
|
||||
"extension_switch": False,
|
||||
"sensitive_switch": False,
|
||||
"cloud_switch": False,
|
||||
"context_switch": False,
|
||||
"suffix_switch": True,
|
||||
"autostart_switch": True,
|
||||
"context_switch": True,
|
||||
"custom_rule": [],
|
||||
"white_list": [],
|
||||
"quarantine": [],
|
||||
@@ -491,7 +488,6 @@ class WindowAPI:
|
||||
def update_config(self, key, value):
|
||||
with self.lock_update:
|
||||
with self.lock_config:
|
||||
|
||||
old_value = self.pyas_config.get(key)
|
||||
if old_value == value:
|
||||
return value
|
||||
@@ -518,9 +514,10 @@ class WindowAPI:
|
||||
self.start_daemon_thread(self.pipe_server_thread)
|
||||
else:
|
||||
success = False
|
||||
|
||||
elif key == "context_switch":
|
||||
self.register_context_menu(True)
|
||||
elif key == "autostart_switch":
|
||||
self.manage_autostart(True)
|
||||
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "Feature Start", detail=str(e), success=False)
|
||||
@@ -530,6 +527,25 @@ class WindowAPI:
|
||||
success = self.stop_system_driver()
|
||||
elif key == "context_switch":
|
||||
self.register_context_menu(False)
|
||||
elif key == "document_switch":
|
||||
with self.lock_file_ops:
|
||||
if getattr(self, 'h_dir_file', None):
|
||||
try:
|
||||
self.kernel32.CloseHandle(self.h_dir_file)
|
||||
except Exception:
|
||||
pass
|
||||
self.h_dir_file = None
|
||||
elif key == "autostart_switch":
|
||||
self.manage_autostart(False)
|
||||
elif key == "suspend_switch":
|
||||
with self.lock_proc:
|
||||
if hasattr(self, 'suspended_procs'):
|
||||
for h in list(self.suspended_procs):
|
||||
try:
|
||||
self.ntdll.NtResumeProcess(h)
|
||||
except Exception:
|
||||
pass
|
||||
self.suspended_procs.clear()
|
||||
|
||||
if success:
|
||||
with self.lock_config:
|
||||
@@ -660,6 +676,20 @@ class WindowAPI:
|
||||
pass
|
||||
return False
|
||||
|
||||
def manage_autostart(self, enable):
|
||||
try:
|
||||
task_name = "PYAS_Security_ATS"
|
||||
if enable:
|
||||
cmd = f"$Action = New-ScheduledTaskAction -Execute '{self.file_pyas}' -Argument '-hide'; $Trigger = New-ScheduledTaskTrigger -AtLogOn; $Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries; Register-ScheduledTask -TaskName '{task_name}' -Action $Action -Trigger $Trigger -Settings $Settings -RunLevel Highest -Force"
|
||||
subprocess.run(["powershell", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", cmd], creationflags=0x08000000)
|
||||
else:
|
||||
subprocess.run(["schtasks", "/Delete", "/TN", task_name, "/F"], creationflags=0x08000000)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "manage_autostart", detail=str(e), success=False)
|
||||
return False
|
||||
|
||||
def manage_named_list(self, list_key, files, action="add", lock_func=None):
|
||||
if list_key == "quarantine" and lock_func is None:
|
||||
lock_func = self.lock_file
|
||||
@@ -680,25 +710,24 @@ class WindowAPI:
|
||||
acted_items = []
|
||||
with self.lock_config:
|
||||
target_list = self.pyas_config.setdefault(list_key, [])
|
||||
|
||||
if action == "add":
|
||||
for path in norm_paths:
|
||||
path_case = os.path.normcase(path)
|
||||
exists = False
|
||||
|
||||
for item in target_list:
|
||||
if isinstance(item, dict):
|
||||
val = item.get("file", "")
|
||||
np = self.norm_path(val, must_exist=False)
|
||||
if np and os.path.normcase(np) == path_case:
|
||||
exists = True
|
||||
break
|
||||
val = item.get("file", "") if isinstance(item, dict) else item
|
||||
np = self.norm_path(val, must_exist=False)
|
||||
|
||||
if np and os.path.normcase(np) == path_case:
|
||||
exists = True
|
||||
break
|
||||
|
||||
if not exists:
|
||||
if lock_func:
|
||||
lock_func(path, True)
|
||||
|
||||
target_list.append({"file": path})
|
||||
target_list.append({"file": path, "time": time.time()})
|
||||
acted_items.append(path)
|
||||
if list_key == "white_list":
|
||||
self.sync_driver_whitelist(path, True)
|
||||
@@ -706,7 +735,6 @@ class WindowAPI:
|
||||
elif action == "remove":
|
||||
norm_paths_case = {os.path.normcase(p) for p in norm_paths}
|
||||
new_list = []
|
||||
|
||||
for item in target_list:
|
||||
val = item.get("file", "") if isinstance(item, dict) else item
|
||||
if val:
|
||||
@@ -855,6 +883,12 @@ class WindowAPI:
|
||||
"spanish_switch": "Abrir PYAS", "hindi_switch": "PYAS खोलें", "arabic_switch": "فتح PYAS",
|
||||
"russian_switch": "Открыть PYAS", "slovenian_switch": "Odpri PYAS"
|
||||
},
|
||||
"optimize_mem": {
|
||||
"traditional_switch": "一鍵加速", "simplified_switch": "一键加速", "english_switch": "Memory Boost",
|
||||
"japanese_switch": "メモリ最適化", "korean_switch": "메모리 최적화", "french_switch": "Optimiser",
|
||||
"spanish_switch": "Optimizar", "hindi_switch": "मेमोरी बूस्ट", "arabic_switch": "تسريع",
|
||||
"russian_switch": "Ускорение", "slovenian_switch": "Optimizacija"
|
||||
},
|
||||
"check_update": {
|
||||
"traditional_switch": "檢查更新", "simplified_switch": "检查更新", "english_switch": "Check Update",
|
||||
"japanese_switch": "更新を確認", "korean_switch": "업데이트 확인", "french_switch": "Vérifier la mise à jour",
|
||||
@@ -876,6 +910,7 @@ class WindowAPI:
|
||||
|
||||
menu = pystray.Menu(
|
||||
pystray.MenuItem(lambda item: self.get_tray_text("open_ui"), self.restore_from_tray, default=True),
|
||||
pystray.MenuItem(lambda item: self.get_tray_text("optimize_mem"), self.optimize_memory),
|
||||
pystray.MenuItem(lambda item: self.get_tray_text("check_update"), self.tray_check_update),
|
||||
pystray.MenuItem(lambda item: self.get_tray_text("exit_app"), self.close)
|
||||
)
|
||||
@@ -962,6 +997,36 @@ class WindowAPI:
|
||||
|
||||
self.stop_system_driver()
|
||||
|
||||
with self.lock_file_ops:
|
||||
if getattr(self, 'h_dir_file', None):
|
||||
try:
|
||||
self.kernel32.CloseHandle(self.h_dir_file)
|
||||
except Exception:
|
||||
pass
|
||||
self.h_dir_file = None
|
||||
|
||||
if hasattr(self, 'virus_lock'):
|
||||
for file_path, (fd, lock_size) in list(self.virus_lock.items()):
|
||||
try:
|
||||
msvcrt.locking(fd, msvcrt.LK_UNLCK, lock_size)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
os.close(fd)
|
||||
except Exception:
|
||||
pass
|
||||
self.virus_lock.clear()
|
||||
|
||||
with self.lock_proc:
|
||||
if hasattr(self, 'suspended_procs'):
|
||||
for h in list(self.suspended_procs):
|
||||
try:
|
||||
self.ntdll.NtResumeProcess(h)
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception:
|
||||
pass
|
||||
self.suspended_procs.clear()
|
||||
|
||||
if hasattr(self, 'h_mutex') and self.h_mutex:
|
||||
try:
|
||||
self.kernel32.CloseHandle(self.h_mutex)
|
||||
@@ -970,6 +1035,19 @@ class WindowAPI:
|
||||
|
||||
if self._window:
|
||||
self._window.destroy()
|
||||
|
||||
with self.lock_logs:
|
||||
if getattr(self, 'logs_dirty', False):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(self.file_log), exist_ok=True)
|
||||
with open(self.file_log, "w", encoding="utf-8") as f:
|
||||
json.dump(self.logs_data, f, indent=4, ensure_ascii=False)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
with self.lock_config:
|
||||
pass
|
||||
|
||||
os._exit(0)
|
||||
|
||||
def init_ui_ready(self):
|
||||
@@ -1094,18 +1172,43 @@ class WindowAPI:
|
||||
return []
|
||||
|
||||
def open_file_location(self, file_path):
|
||||
if file_path:
|
||||
expanded_path = os.path.expandvars(file_path).strip('"').strip("'")
|
||||
if not file_path:
|
||||
return False
|
||||
|
||||
if os.path.exists(expanded_path):
|
||||
try:
|
||||
clean_path = os.path.normpath(expanded_path)
|
||||
subprocess.Popen(f'explorer /select,"{clean_path}"')
|
||||
expanded_path = os.path.expandvars(file_path).strip('"').strip("'")
|
||||
reg_prefixes = ("HKLM", "HKCU", "HKCR", "HKU", "HKCC", "HKEY_")
|
||||
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
if expanded_path.upper().startswith(reg_prefixes):
|
||||
try:
|
||||
full_path = expanded_path
|
||||
if full_path.startswith("HKLM"):
|
||||
full_path = full_path.replace("HKLM", "HKEY_LOCAL_MACHINE", 1)
|
||||
elif full_path.startswith("HKCU"):
|
||||
full_path = full_path.replace("HKCU", "HKEY_CURRENT_USER", 1)
|
||||
elif full_path.startswith("HKCR"):
|
||||
full_path = full_path.replace("HKCR", "HKEY_CLASSES_ROOT", 1)
|
||||
elif full_path.startswith("HKU"):
|
||||
full_path = full_path.replace("HKU", "HKEY_USERS", 1)
|
||||
elif full_path.startswith("HKCC"):
|
||||
full_path = full_path.replace("HKCC", "HKEY_CURRENT_CONFIG", 1)
|
||||
|
||||
subprocess.run(["taskkill", "/F", "/IM", "regedit.exe"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, creationflags=0x08000000)
|
||||
self._reg_write(winreg.HKEY_CURRENT_USER, r"Software\Microsoft\Windows\CurrentVersion\Applets\Regedit", "LastKey", winreg.REG_SZ, full_path)
|
||||
subprocess.Popen("regedit.exe")
|
||||
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
if os.path.exists(expanded_path):
|
||||
try:
|
||||
clean_path = os.path.normpath(expanded_path)
|
||||
subprocess.Popen(f'explorer /select,"{clean_path}"')
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
def open_website(self):
|
||||
@@ -1436,7 +1539,7 @@ class WindowAPI:
|
||||
|
||||
def _traverse_delete(self, path):
|
||||
deleted = 0
|
||||
if not os.path.exists(path):
|
||||
if not path or not os.path.exists(path):
|
||||
return deleted
|
||||
|
||||
try:
|
||||
@@ -1448,6 +1551,9 @@ class WindowAPI:
|
||||
file = os.path.join(path, fd)
|
||||
try:
|
||||
if os.path.isdir(file):
|
||||
if self._is_reparse_point(file):
|
||||
continue
|
||||
|
||||
deleted += self._traverse_delete(file)
|
||||
try:
|
||||
os.rmdir(file)
|
||||
@@ -1576,6 +1682,68 @@ class WindowAPI:
|
||||
self.write_log("WARN", "clean_system_junk", detail=str(e), operate=True, success=False)
|
||||
return 0
|
||||
|
||||
def optimize_memory(self, icon=None, item=None):
|
||||
def _optimize():
|
||||
try:
|
||||
hwnd = self.user32.GetForegroundWindow()
|
||||
fg_pid = ctypes.wintypes.DWORD(0)
|
||||
if hwnd:
|
||||
self.user32.GetWindowThreadProcessId(hwnd, ctypes.byref(fg_pid))
|
||||
|
||||
pids = self.get_process_list_pids()
|
||||
optimized_count = 0
|
||||
|
||||
skip_set = {
|
||||
"dwm.exe", "explorer.exe", "csrss.exe", "smss.exe", "winlogon.exe",
|
||||
"lsass.exe", "services.exe", "svchost.exe", "wininit.exe", "audiodg.exe",
|
||||
"spoolsv.exe", "sihost.exe", "fontdrvhost.exe", "taskmgr.exe"
|
||||
}
|
||||
|
||||
for pid in pids:
|
||||
if pid <= 4 or pid == self.pid_pyas or pid == fg_pid.value:
|
||||
continue
|
||||
|
||||
name, _ = self.get_exe_info(pid)
|
||||
if name and name.lower() in skip_set:
|
||||
continue
|
||||
|
||||
h = self.kernel32.OpenProcess(0x0100, False, pid)
|
||||
if h:
|
||||
try:
|
||||
if self.kernel32.SetProcessWorkingSetSize(h, ctypes.c_size_t(-1), ctypes.c_size_t(-1)):
|
||||
optimized_count += 1
|
||||
finally:
|
||||
self.kernel32.CloseHandle(h)
|
||||
|
||||
title = self._loc({
|
||||
"traditional_switch": "一鍵加速", "simplified_switch": "一键加速", "english_switch": "Memory Boost",
|
||||
"japanese_switch": "メモリ最適化", "korean_switch": "메모리 최적화", "french_switch": "Optimiser",
|
||||
"spanish_switch": "Optimizar", "hindi_switch": "मेमोरी बूस्ट", "arabic_switch": "تسريع",
|
||||
"russian_switch": "Ускорение", "slovenian_switch": "Optimizacija"
|
||||
})
|
||||
|
||||
msg = self._loc({
|
||||
"traditional_switch": f"已釋放 {optimized_count} 個背景進程的記憶體",
|
||||
"simplified_switch": f"已释放 {optimized_count} 个后台进程的内存",
|
||||
"english_switch": f"Freed memory for {optimized_count} background processes",
|
||||
"japanese_switch": f"{optimized_count} 個のバックグラウンドプロセスのメモリを解放しました",
|
||||
"korean_switch": f"{optimized_count}개 백그라운드 프로세스 메모리 확보",
|
||||
"french_switch": f"Mémoire libérée pour {optimized_count} processus",
|
||||
"spanish_switch": f"Memoria liberada para {optimized_count} procesos",
|
||||
"hindi_switch": f"{optimized_count} पृष्ठभूमि प्रक्रियाओं के लिए मेमोरी मुक्त की गई",
|
||||
"arabic_switch": f"تم تحرير الذاكرة لـ {optimized_count} من العمليات في الخلفية",
|
||||
"russian_switch": f"Освобождена память {optimized_count} фоновых процессов",
|
||||
"slovenian_switch": f"Sproščen pomnilnik za {optimized_count} procesov v ozadju"
|
||||
})
|
||||
|
||||
self.show_notification(title, msg)
|
||||
self.write_log("INFO", "Memory Boost", detail=f"Optimized {optimized_count} processes", operate=True)
|
||||
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "optimize_memory", detail=str(e), success=False)
|
||||
|
||||
threading.Thread(target=_optimize, daemon=True).start()
|
||||
|
||||
def get_startup_list(self):
|
||||
items = []
|
||||
|
||||
@@ -1745,7 +1913,12 @@ class WindowAPI:
|
||||
with self.lock_config:
|
||||
first_launch = self.pyas_config.get("first_launch", True)
|
||||
driver_enabled = self.pyas_config.get("driver_switch", False)
|
||||
context_enabled = self.pyas_config.get("context_switch", True)
|
||||
autostart_enabled = self.pyas_config.get("autostart_switch", True)
|
||||
|
||||
self.register_context_menu(context_enabled)
|
||||
self.manage_autostart(autostart_enabled)
|
||||
|
||||
if not first_launch:
|
||||
with self.lock_config:
|
||||
if self.pyas_config.get("process_switch"):
|
||||
@@ -1773,17 +1946,30 @@ class WindowAPI:
|
||||
def yield_files(self, targets):
|
||||
if isinstance(targets, str):
|
||||
if os.path.isdir(targets):
|
||||
for root, _, files in os.walk(targets):
|
||||
for root, dirs, files in os.walk(targets):
|
||||
dirs[:] = [d for d in dirs if not self._is_reparse_point(os.path.join(root, d))]
|
||||
for f in files:
|
||||
yield self.norm_path(os.path.join(root, f))
|
||||
yield self.norm_path(os.path.join(root, f)), False
|
||||
|
||||
elif os.path.isfile(targets):
|
||||
yield self.norm_path(targets)
|
||||
yield self.norm_path(targets), True
|
||||
|
||||
elif isinstance(targets, (list, tuple, set)):
|
||||
for t in targets:
|
||||
yield from self.yield_files(t)
|
||||
|
||||
def _is_reparse_point(self, path):
|
||||
try:
|
||||
if os.path.islink(path):
|
||||
return True
|
||||
if hasattr(os.path, 'isjunction') and os.path.isjunction(path):
|
||||
return True
|
||||
|
||||
st = os.lstat(path)
|
||||
return bool(getattr(st, 'st_file_attributes', 0) & 0x400)
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
def scan_engine(self, file_path):
|
||||
with self.lock_config:
|
||||
ext_switch = self.pyas_config.get("extension_switch", False)
|
||||
@@ -1865,7 +2051,7 @@ class WindowAPI:
|
||||
def scan_worker(self, targets):
|
||||
last_update = 0.0
|
||||
try:
|
||||
for file_path in self.yield_files(targets):
|
||||
for file_path, is_explicit in self.yield_files(targets):
|
||||
with self.lock_virus:
|
||||
if not self.scan_running:
|
||||
break
|
||||
@@ -1895,9 +2081,10 @@ class WindowAPI:
|
||||
with self.lock_virus:
|
||||
self.scan_count += 1
|
||||
with self.lock_config:
|
||||
ext_filter = self.pyas_config.get("suffix_switch", True)
|
||||
suffix = self.pyas_config.get("suffix", [])
|
||||
|
||||
if os.path.splitext(norm_path)[-1].lower() not in suffix:
|
||||
if not is_explicit and ext_filter and os.path.splitext(norm_path)[-1].lower() not in suffix:
|
||||
continue
|
||||
|
||||
result = self.safe_scan_engine(norm_path)
|
||||
@@ -1971,8 +2158,39 @@ class WindowAPI:
|
||||
if os.path.exists(self.path_config):
|
||||
targets.append(self.path_config)
|
||||
|
||||
buf = ctypes.create_unicode_buffer(1024)
|
||||
max_address = 0x7FFFFFFFFFFF if ctypes.sizeof(ctypes.c_void_p) == 8 else 0x7FFFFFFF
|
||||
system_dir = self.path_system.lower()
|
||||
|
||||
for proc in self.get_process_list():
|
||||
if proc["path"] and proc["path"] != "None": targets.append(proc["path"])
|
||||
if proc["path"] and proc["path"] != "None":
|
||||
targets.append(proc["path"])
|
||||
|
||||
pid = proc["pid"]
|
||||
if pid <= 4:
|
||||
continue
|
||||
|
||||
h_process = self.kernel32.OpenProcess(0x1000, False, pid)
|
||||
if h_process:
|
||||
try:
|
||||
address = 0
|
||||
mbi = MEMORY_BASIC_INFORMATION()
|
||||
while address < max_address and self.kernel32.VirtualQueryEx(h_process, ctypes.c_void_p(address), ctypes.byref(mbi), ctypes.sizeof(mbi)):
|
||||
if mbi.State == 0x1000 and mbi.Type == 0x1000000:
|
||||
if self.psapi.GetMappedFileNameW(h_process, ctypes.c_void_p(address), buf, 1024):
|
||||
raw_path = buf.value
|
||||
if raw_path.startswith("\\"):
|
||||
raw_path = self.device_path_to_drive(raw_path)
|
||||
|
||||
file_path = self.norm_path(raw_path)
|
||||
if file_path and not file_path.lower().startswith(system_dir):
|
||||
targets.append(file_path)
|
||||
|
||||
if mbi.RegionSize == 0:
|
||||
break
|
||||
address += mbi.RegionSize
|
||||
finally:
|
||||
self.kernel32.CloseHandle(h_process)
|
||||
|
||||
self.start_scan(list(set(targets)))
|
||||
|
||||
@@ -2124,7 +2342,7 @@ class WindowAPI:
|
||||
if not self.pyas_config.get("cloud_switch", True):
|
||||
return False
|
||||
|
||||
api_host, api_key, max_size = self.pyas_config.get("api_host"), self.pyas_config.get("api_key"), self.pyas_config.get("size", 100 * 1024 * 1024)
|
||||
api_host, api_key, max_size = self.pyas_config.get("api_host"), self.pyas_config.get("api_key"), self.pyas_config.get("size", 256 * 1024 * 1024)
|
||||
|
||||
if not os.path.exists(file_path) or not os.path.isfile(file_path):
|
||||
return False
|
||||
@@ -2581,24 +2799,42 @@ class WindowAPI:
|
||||
for pid in new_pids:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if h:
|
||||
self.ntdll.NtSuspendProcess(h)
|
||||
with self.lock_config:
|
||||
should_suspend = self.pyas_config.get("suspend_switch", True)
|
||||
|
||||
if should_suspend:
|
||||
self.ntdll.NtSuspendProcess(h)
|
||||
with self.lock_proc:
|
||||
self.suspended_procs.add(h)
|
||||
try:
|
||||
self.proc_pool.submit(self.handle_new_process, pid, h)
|
||||
self.proc_pool.submit(self.handle_new_process, pid, h, should_suspend)
|
||||
|
||||
except Exception:
|
||||
self.ntdll.NtResumeProcess(h)
|
||||
self.kernel32.CloseHandle(h)
|
||||
if should_suspend:
|
||||
self.ntdll.NtResumeProcess(h)
|
||||
with self.lock_proc:
|
||||
self.suspended_procs.discard(h)
|
||||
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "protect_proc_thread", detail=str(e), success=False)
|
||||
|
||||
def handle_new_process(self, pid, h=None):
|
||||
def handle_new_process(self, pid, h=None, suspended=None):
|
||||
if suspended is None:
|
||||
with self.lock_config:
|
||||
suspended = self.pyas_config.get("suspend_switch", True)
|
||||
|
||||
if not h:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if not h:
|
||||
return
|
||||
|
||||
self.ntdll.NtSuspendProcess(h)
|
||||
if suspended:
|
||||
self.ntdll.NtSuspendProcess(h)
|
||||
with self.lock_proc:
|
||||
if not hasattr(self, 'suspended_procs'):
|
||||
self.suspended_procs = set()
|
||||
self.suspended_procs.add(h)
|
||||
try:
|
||||
cmdline = self.get_process_cmdline(h)
|
||||
process_file = self.get_process_file(h)
|
||||
@@ -2615,13 +2851,16 @@ class WindowAPI:
|
||||
all_targets.append(np)
|
||||
|
||||
with self.lock_config:
|
||||
ext_filter = self.pyas_config.get("suffix_switch", True)
|
||||
suffix = self.pyas_config.get("suffix", [])
|
||||
|
||||
scan_targets = []
|
||||
for file_path in all_targets:
|
||||
if not file_path or not os.path.isfile(file_path):
|
||||
continue
|
||||
if os.path.splitext(file_path)[-1].lower() not in suffix or self.is_in_whitelist(file_path):
|
||||
if ext_filter and os.path.splitext(file_path)[-1].lower() not in suffix:
|
||||
continue
|
||||
if self.is_in_whitelist(file_path):
|
||||
continue
|
||||
if process_file and process_file.lower().endswith("explorer.exe") and "/select" in cmdline.lower():
|
||||
continue
|
||||
@@ -2648,8 +2887,17 @@ class WindowAPI:
|
||||
self.write_log("BLOCK", "Process DLL Block", pid=pid, source=hidden_virus_path, file_hash=self.calc_file_hash(hidden_virus_path))
|
||||
|
||||
finally:
|
||||
self.ntdll.NtResumeProcess(h)
|
||||
self.kernel32.CloseHandle(h)
|
||||
if suspended:
|
||||
try:
|
||||
self.ntdll.NtResumeProcess(h)
|
||||
except Exception:
|
||||
pass
|
||||
with self.lock_proc:
|
||||
self.suspended_procs.discard(h)
|
||||
try:
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def scan_process_memory(self, pid, h_process):
|
||||
scanned_paths = set()
|
||||
@@ -2661,6 +2909,7 @@ class WindowAPI:
|
||||
max_address = 0x7FFFFFFFFFFF if ctypes.sizeof(ctypes.c_void_p) == 8 else 0x7FFFFFFF
|
||||
|
||||
with self.lock_config:
|
||||
ext_filter = self.pyas_config.get("suffix_switch", True)
|
||||
suffix = self.pyas_config.get("suffix", [])
|
||||
|
||||
try:
|
||||
@@ -2681,7 +2930,7 @@ class WindowAPI:
|
||||
if not file_path_lower.startswith(system_dir) and not self.is_in_whitelist(file_path):
|
||||
ext = os.path.splitext(file_path_lower)[-1]
|
||||
|
||||
if ext != ".exe" and ext in suffix:
|
||||
if ext != ".exe" and (not ext_filter or ext in suffix):
|
||||
if self.safe_scan_engine(file_path):
|
||||
return file_path
|
||||
|
||||
@@ -2697,10 +2946,17 @@ class WindowAPI:
|
||||
return None
|
||||
|
||||
def protect_file_thread(self):
|
||||
with self.lock_file_ops:
|
||||
if getattr(self, 'h_dir_file', None):
|
||||
return
|
||||
|
||||
hDir = self.kernel32.CreateFileW(self.path_user, 0x0001, 0x00000007, None, 3, 0x02000000, None)
|
||||
if not hDir or hDir == -1:
|
||||
return
|
||||
|
||||
with self.lock_file_ops:
|
||||
self.h_dir_file = hDir
|
||||
|
||||
try:
|
||||
buffer = ctypes.create_string_buffer(262144)
|
||||
temp_prefix = os.path.normcase(self.path_temp)
|
||||
@@ -2709,12 +2965,13 @@ class WindowAPI:
|
||||
|
||||
while True:
|
||||
with self.lock_config:
|
||||
if not self.pyas_config.get("document_switch", False): break
|
||||
if not self.pyas_config.get("document_switch", False):
|
||||
break
|
||||
try:
|
||||
bytes_returned = ctypes.wintypes.DWORD()
|
||||
res = self.kernel32.ReadDirectoryChangesW(hDir, buffer, ctypes.sizeof(buffer), True, 0x0000001F, ctypes.byref(bytes_returned), None, None)
|
||||
res = self.kernel32.ReadDirectoryChangesW(self.h_dir_file, buffer, ctypes.sizeof(buffer), True, 0x0000001F, ctypes.byref(bytes_returned), None, None)
|
||||
if not res or bytes_returned.value == 0:
|
||||
continue
|
||||
break
|
||||
|
||||
offset = 0
|
||||
while True:
|
||||
@@ -2729,9 +2986,10 @@ class WindowAPI:
|
||||
|
||||
if not (norm_path.startswith(temp_prefix) and norm_path[len(temp_prefix):].startswith("_mei")):
|
||||
with self.lock_config:
|
||||
ext_filter = self.pyas_config.get("suffix_switch", True)
|
||||
suffix = self.pyas_config.get("suffix", [])
|
||||
|
||||
if os.path.splitext(file_path)[-1].lower() in suffix:
|
||||
if not ext_filter or os.path.splitext(file_path)[-1].lower() in suffix:
|
||||
self.protect_pool.submit(self.handle_new_file, file_path)
|
||||
|
||||
if notify.NextEntryOffset == 0:
|
||||
@@ -2739,10 +2997,16 @@ class WindowAPI:
|
||||
|
||||
offset += notify.NextEntryOffset
|
||||
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "protect_file_thread", detail=str(e), success=False)
|
||||
except Exception:
|
||||
break
|
||||
finally:
|
||||
self.kernel32.CloseHandle(hDir)
|
||||
with self.lock_file_ops:
|
||||
if getattr(self, 'h_dir_file', None):
|
||||
try:
|
||||
self.kernel32.CloseHandle(self.h_dir_file)
|
||||
except Exception:
|
||||
pass
|
||||
self.h_dir_file = None
|
||||
|
||||
def handle_new_file(self, file_path):
|
||||
try:
|
||||
@@ -2849,34 +3113,74 @@ class WindowAPI:
|
||||
pass
|
||||
|
||||
def capture_popup_window(self):
|
||||
overlay = self.user32.CreateWindowExW(0x000800A8, "STATIC", "", 0x90000004, 0, 0, 0, 0, None, None, None, None)
|
||||
self.user32.SetLayeredWindowAttributes(overlay, 0, 120, 2)
|
||||
|
||||
target_hwnd = None
|
||||
last_hwnd = None
|
||||
msg = ctypes.wintypes.MSG()
|
||||
|
||||
while self.user32.GetAsyncKeyState(0x01) & 0x8000:
|
||||
time.sleep(0.01)
|
||||
|
||||
try:
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 5:
|
||||
while time.time() - start_time < 30:
|
||||
if self.user32.GetAsyncKeyState(0x1B) & 0x8000:
|
||||
break
|
||||
|
||||
hwnd = self.user32.GetForegroundWindow()
|
||||
if not hwnd:
|
||||
time.sleep(0.5)
|
||||
continue
|
||||
pt = POINT()
|
||||
self.user32.GetCursorPos(ctypes.byref(pt))
|
||||
|
||||
hwnd = self.user32.WindowFromPoint(pt)
|
||||
root_hwnd = self.user32.GetAncestor(hwnd, 2)
|
||||
if not root_hwnd:
|
||||
root_hwnd = hwnd
|
||||
|
||||
pid = ctypes.c_ulong(0)
|
||||
self.user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid))
|
||||
if pid.value != self.pid_pyas and pid.value > 4:
|
||||
if root_hwnd and root_hwnd != overlay:
|
||||
if root_hwnd != last_hwnd:
|
||||
last_hwnd = root_hwnd
|
||||
rect = RECT()
|
||||
self.user32.GetWindowRect(root_hwnd, ctypes.byref(rect))
|
||||
self.user32.SetWindowPos(
|
||||
overlay, -1,
|
||||
rect.left, rect.top,
|
||||
rect.right - rect.left, rect.bottom - rect.top,
|
||||
0x0050
|
||||
)
|
||||
|
||||
if self.user32.GetAsyncKeyState(0x01) & 0x8000:
|
||||
target_hwnd = root_hwnd
|
||||
break
|
||||
|
||||
length = self.user32.GetWindowTextLengthW(hwnd)
|
||||
title, class_name = ctypes.create_unicode_buffer(length + 1), ctypes.create_unicode_buffer(256)
|
||||
self.user32.GetWindowTextW(hwnd, title, length + 1)
|
||||
self.user32.GetClassNameW(hwnd, class_name, 256)
|
||||
while self.user32.PeekMessageW(ctypes.byref(msg), 0, 0, 0, 1):
|
||||
self.user32.TranslateMessage(ctypes.byref(msg))
|
||||
self.user32.DispatchMessageW(ctypes.byref(msg))
|
||||
|
||||
proc_name, _ = self.get_exe_info(pid.value)
|
||||
t_str, c_str = str(title.value), str(class_name.value)
|
||||
time.sleep(0.02)
|
||||
finally:
|
||||
self.user32.DestroyWindow(overlay)
|
||||
|
||||
if proc_name and not any(item.get("exe") == proc_name or item.get("class") == c_str for item in self.pass_windows):
|
||||
return {"exe": proc_name, "class": c_str, "title": t_str}
|
||||
if not target_hwnd:
|
||||
return None
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
except Exception as e:
|
||||
self.write_log("WARN", "capture_popup_window", detail=str(e), success=False)
|
||||
pid = ctypes.c_ulong(0)
|
||||
self.user32.GetWindowThreadProcessId(target_hwnd, ctypes.byref(pid))
|
||||
|
||||
if pid.value != self.pid_pyas and pid.value > 4:
|
||||
length = self.user32.GetWindowTextLengthW(target_hwnd)
|
||||
title = ctypes.create_unicode_buffer(length + 1)
|
||||
class_name = ctypes.create_unicode_buffer(256)
|
||||
|
||||
self.user32.GetWindowTextW(target_hwnd, title, length + 1)
|
||||
self.user32.GetClassNameW(target_hwnd, class_name, 256)
|
||||
|
||||
proc_name, _ = self.get_exe_info(pid.value)
|
||||
t_str, c_str = str(title.value), str(class_name.value)
|
||||
|
||||
if proc_name and not any(item.get("exe") == proc_name or item.get("class") == c_str for item in self.pass_windows):
|
||||
return {"exe": proc_name, "class": c_str, "title": t_str}
|
||||
|
||||
return None
|
||||
|
||||
def add_popup_rule(self, rule):
|
||||
|
||||
+79
-28
@@ -1,5 +1,5 @@
|
||||
import os, re, yara, time, math, json, zlib, numpy, base64, requests, datetime
|
||||
import ctypes, ctypes.wintypes, hashlib, pefile, threading, onnxruntime
|
||||
import os, re, yara, time, math, json, zlib, mmap, numpy, datetime, requests
|
||||
import ctypes, ctypes.wintypes, pefile, threading, onnxruntime
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@@ -205,6 +205,10 @@ class pe_scanner:
|
||||
self.model = None
|
||||
self.input_name = None
|
||||
self.feature_order = []
|
||||
self.dll_hash_dim = 512
|
||||
self.api_hash_dim = 4096
|
||||
self.dll_hash_pad = 3
|
||||
self.api_hash_pad = 4
|
||||
self.signer = sign_scanner()
|
||||
self.signer.init_windll(["wintrust"])
|
||||
|
||||
@@ -232,16 +236,43 @@ class pe_scanner:
|
||||
with open(feat_path, 'r', encoding='utf-8') as f:
|
||||
self.feature_order = json.load(f)
|
||||
|
||||
self._parse_hash_dims()
|
||||
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _parse_hash_dims(self):
|
||||
max_dll = -1
|
||||
max_api = -1
|
||||
|
||||
for feat in self.feature_order:
|
||||
if feat.startswith("DllHash_"):
|
||||
try:
|
||||
val_str = feat.split("_")[1]
|
||||
max_dll = max(max_dll, int(val_str))
|
||||
self.dll_hash_pad = len(val_str)
|
||||
except Exception:
|
||||
pass
|
||||
elif feat.startswith("ApiHash_"):
|
||||
try:
|
||||
val_str = feat.split("_")[1]
|
||||
max_api = max(max_api, int(val_str))
|
||||
self.api_hash_pad = len(val_str)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if max_dll >= 0:
|
||||
self.dll_hash_dim = max_dll + 1
|
||||
if max_api >= 0:
|
||||
self.api_hash_dim = max_api + 1
|
||||
|
||||
def _safe_float(self, val):
|
||||
try:
|
||||
f = float(val)
|
||||
if math.isinf(f) or math.isnan(f):
|
||||
return 0.0
|
||||
return f
|
||||
|
||||
return f
|
||||
except Exception:
|
||||
return 0.0
|
||||
|
||||
@@ -260,23 +291,31 @@ class pe_scanner:
|
||||
res = {"StringCount": 0.0, "StringMeanLength": 0.0, "StringEntropy": 0.0}
|
||||
res.update(dict.fromkeys(self._STRING_KEYS, 0.0))
|
||||
|
||||
matches = self._STRING_PATTERN.findall(file_bytes)
|
||||
if not matches:
|
||||
return res
|
||||
total_len = 0
|
||||
count = 0
|
||||
global_counts = numpy.zeros(256, dtype=numpy.int64)
|
||||
mv = memoryview(file_bytes)
|
||||
|
||||
combined_data = b''.join(matches)
|
||||
count = len(matches)
|
||||
total_len = len(combined_data)
|
||||
for m in self._STRING_PATTERN.finditer(file_bytes):
|
||||
count += 1
|
||||
start, end = m.span()
|
||||
total_len += (end - start)
|
||||
arr = numpy.frombuffer(mv[start:end], dtype=numpy.uint8)
|
||||
global_counts += numpy.bincount(arr, minlength=256)
|
||||
|
||||
if count == 0 or total_len == 0:
|
||||
return res
|
||||
|
||||
res["StringCount"] = float(count)
|
||||
res["StringMeanLength"] = float(total_len) / count
|
||||
res["StringEntropy"] = self._calc_entropy(combined_data)
|
||||
|
||||
arr = numpy.frombuffer(combined_data, dtype=numpy.uint8)
|
||||
counts = numpy.bincount(arr, minlength=127)
|
||||
char_counts = counts[0x20:0x7F]
|
||||
counts_nonzero = global_counts[global_counts > 0]
|
||||
if len(counts_nonzero) > 0:
|
||||
res["StringEntropy"] = float(numpy.log2(total_len) - numpy.sum(counts_nonzero * numpy.log2(counts_nonzero)) / total_len)
|
||||
|
||||
valid_chars = global_counts[0x20:0x7F]
|
||||
res.update(dict(zip(self._STRING_KEYS, valid_chars.astype(numpy.float64) / total_len)))
|
||||
|
||||
res.update(dict(zip(self._STRING_KEYS, char_counts.astype(numpy.float64) / total_len)))
|
||||
return res
|
||||
|
||||
def _extract_histograms(self, file_bytes):
|
||||
@@ -305,6 +344,7 @@ class pe_scanner:
|
||||
byte_bins = w_counts.reshape(16, 16).sum(axis=1)
|
||||
idx_start = ent_bin * 16
|
||||
ent_hist[idx_start : idx_start+16] += byte_bins
|
||||
|
||||
else:
|
||||
num_windows = (sz - window) // step + 1
|
||||
batch_windows = 10000
|
||||
@@ -445,6 +485,7 @@ class pe_scanner:
|
||||
|
||||
if getattr(struct, 'GuardCFFunctionTable', 0) != 0:
|
||||
cfg["HasCFG"] = 1.0
|
||||
|
||||
if getattr(struct, 'SEHandlerTable', 0) != 0:
|
||||
cfg["HasSEHTable"] = 1.0
|
||||
|
||||
@@ -479,7 +520,6 @@ class pe_scanner:
|
||||
dw_length = int.from_bytes(file_bytes[curr:curr+4], 'little')
|
||||
if dw_length < 8:
|
||||
break
|
||||
|
||||
count += 1
|
||||
curr += (dw_length + 7) & ~7
|
||||
|
||||
@@ -488,18 +528,21 @@ class pe_scanner:
|
||||
|
||||
def extract_features(self, file_path):
|
||||
pe = None
|
||||
mm = None
|
||||
f = None
|
||||
|
||||
try:
|
||||
fsize = os.path.getsize(file_path)
|
||||
if fsize == 0 or fsize > 536870912:
|
||||
return None
|
||||
|
||||
with open(file_path, "rb") as f:
|
||||
file_bytes = f.read()
|
||||
f = open(file_path, "rb")
|
||||
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
|
||||
|
||||
base = {}
|
||||
dlls = set()
|
||||
apis = set()
|
||||
pe = pefile.PE(data=file_bytes, fast_load=True)
|
||||
pe = pefile.PE(data=mm, fast_load=True)
|
||||
|
||||
try:
|
||||
pe.parse_rich_header()
|
||||
@@ -515,7 +558,7 @@ class pe_scanner:
|
||||
pass
|
||||
|
||||
base['TrustSigned'] = 1.0 if self.signer.sign_verify(file_path) else 0.0
|
||||
base['FileEntropy'] = self._calc_entropy(file_bytes)
|
||||
base['FileEntropy'] = self._calc_entropy(mm)
|
||||
base['FileSize'] = float(fsize)
|
||||
|
||||
fh = pe.FILE_HEADER
|
||||
@@ -580,6 +623,7 @@ class pe_scanner:
|
||||
try:
|
||||
s_data = section.get_data()
|
||||
s_entropy = self._calc_entropy(s_data)
|
||||
|
||||
except Exception:
|
||||
s_entropy = 0.0
|
||||
|
||||
@@ -631,9 +675,9 @@ class pe_scanner:
|
||||
if hasattr(pe, 'FileInfo'):
|
||||
for fileinfo_list in pe.FileInfo:
|
||||
for fileinfo in fileinfo_list:
|
||||
|
||||
if getattr(fileinfo, 'name', '') in ('StringFileInfo', b'StringFileInfo'):
|
||||
for st in getattr(fileinfo, 'StringTable', []):
|
||||
|
||||
for key, val in st.entries.items():
|
||||
try:
|
||||
k = key.decode('utf-8', 'ignore') if isinstance(key, bytes) else str(key)
|
||||
@@ -690,6 +734,7 @@ class pe_scanner:
|
||||
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
|
||||
if getattr(entry, 'id', None) == 3 and hasattr(entry, 'directory'):
|
||||
icon_count += len(getattr(entry.directory, 'entries', []))
|
||||
|
||||
base['IconCount'] = float(icon_count)
|
||||
else:
|
||||
base['IconCount'] = 0.0
|
||||
@@ -699,14 +744,14 @@ class pe_scanner:
|
||||
else:
|
||||
base['DebugCount'] = 0.0
|
||||
|
||||
base.update(self._extract_strings(file_bytes))
|
||||
base.update(self._extract_histograms(file_bytes))
|
||||
base.update(self._extract_strings(mm))
|
||||
base.update(self._extract_histograms(mm))
|
||||
base.update(self._extract_overlay_features(pe, fsize))
|
||||
base.update(self._extract_rich_header(pe))
|
||||
base.update(self._extract_ep_anomalies(pe))
|
||||
base.update(self._extract_advanced_resources(pe))
|
||||
base.update(self._extract_load_config(pe))
|
||||
base.update(self._extract_security_directory(pe, file_bytes, fsize))
|
||||
base.update(self._extract_security_directory(pe, mm, fsize))
|
||||
|
||||
for k in base:
|
||||
base[k] = self._safe_float(base[k])
|
||||
@@ -718,10 +763,15 @@ class pe_scanner:
|
||||
finally:
|
||||
if pe:
|
||||
pe.close()
|
||||
if mm:
|
||||
mm.close()
|
||||
if f:
|
||||
f.close()
|
||||
|
||||
def pe_scan(self, file_path, enhanced_mode=False):
|
||||
if not self.model or not self.feature_order:
|
||||
return False, False
|
||||
|
||||
try:
|
||||
raw_data = self.extract_features(file_path)
|
||||
if not raw_data:
|
||||
@@ -742,8 +792,8 @@ class pe_scanner:
|
||||
if old_fn in feat_map:
|
||||
vec[0, feat_map[old_fn]] = 1.0
|
||||
|
||||
h = zlib.crc32(d.encode('utf-8', 'ignore')) % 256
|
||||
new_fn = f"DllHash_{h:03d}"
|
||||
h = zlib.crc32(d.encode('utf-8', 'ignore')) % self.dll_hash_dim
|
||||
new_fn = f"DllHash_{h:0{self.dll_hash_pad}d}"
|
||||
if new_fn in feat_map:
|
||||
vec[0, feat_map[new_fn]] += 1.0
|
||||
|
||||
@@ -752,12 +802,14 @@ class pe_scanner:
|
||||
if old_fn in feat_map:
|
||||
vec[0, feat_map[old_fn]] = 1.0
|
||||
|
||||
h = zlib.crc32(a.encode('utf-8', 'ignore')) % 1024
|
||||
new_fn = f"ApiHash_{h:04d}"
|
||||
h = zlib.crc32(a.encode('utf-8', 'ignore')) % self.api_hash_dim
|
||||
new_fn = f"ApiHash_{h:0{self.api_hash_pad}d}"
|
||||
if new_fn in feat_map:
|
||||
vec[0, feat_map[new_fn]] += 1.0
|
||||
|
||||
outputs = self.model.run(None, {self.input_name: vec})
|
||||
|
||||
del raw_data, vec
|
||||
result = outputs[1]
|
||||
prob = 0.0
|
||||
|
||||
@@ -771,7 +823,6 @@ class pe_scanner:
|
||||
prob = float(result[0][1])
|
||||
|
||||
score = int(prob * 100)
|
||||
|
||||
if score >= 80:
|
||||
return f"General:WinPE/Malware.{score}!ml", score
|
||||
elif enhanced_mode and score >= 50:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#define AppId "{{a7d7bac3-93b8-4630-8308-c7a56bf7fdf4}"
|
||||
#define AppName "PYAS"
|
||||
#define AppVersion "3.5.9.0"
|
||||
#define AppVersion "3.6.1.0"
|
||||
#define AppPublisher "PYAS Security"
|
||||
#define AppURL "https://github.com/87owo/PYAS"
|
||||
#define AppExeName "PYAS.exe"
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
|
||||
|
||||
pyinstaller_versionfile.create_versionfile(
|
||||
output_file="versionfile.txt",
|
||||
version='3.5.9.0',
|
||||
version='3.6.1.0',
|
||||
company_name="PYAS Security",
|
||||
file_description="PYAS Security Antivirus",
|
||||
internal_name="PYAS",
|
||||
|
||||
+175
-170
@@ -1,171 +1,176 @@
|
||||
#pragma once
|
||||
#include <fltKernel.h>
|
||||
#include <ntddk.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <ntddscsi.h>
|
||||
#include <ntstrsafe.h>
|
||||
|
||||
constexpr auto PYAS_PORT_NAME = L"\\PYAS_Output_Pipe";
|
||||
constexpr auto MAX_PATH_LEN = 1024;
|
||||
constexpr auto PYAS_POOL_TAG = 'SAYP';
|
||||
|
||||
#ifndef PROCESS_TERMINATE
|
||||
#define PROCESS_TERMINATE (0x0001)
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_THREAD
|
||||
#define PROCESS_CREATE_THREAD (0x0002)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_OPERATION
|
||||
#define PROCESS_VM_OPERATION (0x0008)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_READ
|
||||
#define PROCESS_VM_READ (0x0010)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_WRITE
|
||||
#define PROCESS_VM_WRITE (0x0020)
|
||||
#endif
|
||||
#ifndef PROCESS_DUP_HANDLE
|
||||
#define PROCESS_DUP_HANDLE (0x0040)
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_PROCESS
|
||||
#define PROCESS_CREATE_PROCESS (0x0080)
|
||||
#endif
|
||||
#ifndef PROCESS_SET_INFORMATION
|
||||
#define PROCESS_SET_INFORMATION (0x0200)
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_INFORMATION
|
||||
#define PROCESS_QUERY_INFORMATION (0x0400)
|
||||
#endif
|
||||
#ifndef PROCESS_SUSPEND_RESUME
|
||||
#define PROCESS_SUSPEND_RESUME (0x0800)
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
|
||||
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
|
||||
#endif
|
||||
#ifndef PROCESS_SET_QUOTA
|
||||
#define PROCESS_SET_QUOTA (0x0100)
|
||||
#endif
|
||||
#ifndef THREAD_TERMINATE
|
||||
#define THREAD_TERMINATE (0x0001)
|
||||
#endif
|
||||
#ifndef THREAD_SUSPEND_RESUME
|
||||
#define THREAD_SUSPEND_RESUME (0x0002)
|
||||
#endif
|
||||
#ifndef THREAD_SET_CONTEXT
|
||||
#define THREAD_SET_CONTEXT (0x0010)
|
||||
#endif
|
||||
#ifndef THREAD_SET_INFORMATION
|
||||
#define THREAD_SET_INFORMATION (0x0020)
|
||||
#endif
|
||||
#ifndef THREAD_SET_THREAD_TOKEN
|
||||
#define THREAD_SET_THREAD_TOKEN (0x0080)
|
||||
#endif
|
||||
#ifndef POOL_FLAG_NON_PAGED
|
||||
#define POOL_FLAG_NON_PAGED 0x0000000000000040UI64
|
||||
#endif
|
||||
|
||||
#ifndef FSCTL_MANAGE_BYPASS_IO
|
||||
#define FSCTL_MANAGE_BYPASS_IO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 188, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
typedef enum _FS_BPIO_OPERATIONS {
|
||||
FS_BPIO_OP_ENABLE = 1,
|
||||
FS_BPIO_OP_DISABLE = 2,
|
||||
FS_BPIO_OP_QUERY = 3,
|
||||
FS_BPIO_OP_VOLUME_STACK_PAUSE = 4,
|
||||
FS_BPIO_OP_VOLUME_STACK_RESUME = 5,
|
||||
FS_BPIO_OP_STREAM_PAUSE = 6,
|
||||
FS_BPIO_OP_STREAM_RESUME = 7,
|
||||
FS_BPIO_OP_GET_INFO = 8
|
||||
} FS_BPIO_OPERATIONS;
|
||||
|
||||
typedef struct _FS_BPIO_INPUT {
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
ULONG InFlags;
|
||||
ULONGLONG Reserved1;
|
||||
ULONGLONG Reserved2;
|
||||
} FS_BPIO_INPUT, * PFS_BPIO_INPUT;
|
||||
|
||||
typedef struct _FS_BPIO_OUTPUT {
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
ULONG OutFlags;
|
||||
ULONGLONG Reserved1;
|
||||
ULONGLONG Reserved2;
|
||||
NTSTATUS Status;
|
||||
} FS_BPIO_OUTPUT, * PFS_BPIO_OUTPUT;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct _PYAS_MESSAGE {
|
||||
ULONG MessageCode;
|
||||
ULONG ProcessId;
|
||||
WCHAR Path[MAX_PATH_LEN];
|
||||
} PYAS_MESSAGE, * PPYAS_MESSAGE;
|
||||
|
||||
typedef struct _PYAS_USER_MESSAGE {
|
||||
ULONG Command;
|
||||
WCHAR Path[MAX_PATH_LEN];
|
||||
} PYAS_USER_MESSAGE, * PPYAS_USER_MESSAGE;
|
||||
|
||||
typedef struct _DRIVER_DATA {
|
||||
PDRIVER_OBJECT DriverObject;
|
||||
PFLT_FILTER FilterHandle;
|
||||
PFLT_PORT ServerPort;
|
||||
PFLT_PORT ClientPort;
|
||||
PEPROCESS PyasProcess;
|
||||
ULONG PyasPid;
|
||||
LARGE_INTEGER Cookie;
|
||||
KSPIN_LOCK PortMutex;
|
||||
KSPIN_LOCK TrackerMutex;
|
||||
EX_RUNDOWN_REF PortRundown;
|
||||
} DRIVER_DATA, * PDRIVER_DATA;
|
||||
|
||||
typedef struct _RULE_NODE {
|
||||
struct _RULE_NODE* Next;
|
||||
UNICODE_STRING Pattern;
|
||||
} RULE_NODE, * PRULE_NODE;
|
||||
|
||||
extern DRIVER_DATA GlobalData;
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
|
||||
NTSTATUS InitializeProcessProtection();
|
||||
VOID UninitializeProcessProtection();
|
||||
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject);
|
||||
VOID UninitializeRegistryProtection();
|
||||
|
||||
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath);
|
||||
VOID UnloadRules();
|
||||
|
||||
VOID InitializeRulesEngine();
|
||||
VOID UninitializeRulesEngine();
|
||||
|
||||
VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr);
|
||||
VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr);
|
||||
|
||||
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo);
|
||||
|
||||
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName);
|
||||
|
||||
BOOLEAN IsProcessTrusted(HANDLE ProcessId);
|
||||
BOOLEAN IsTargetProtected(HANDLE ProcessId);
|
||||
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes);
|
||||
|
||||
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName);
|
||||
BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName);
|
||||
BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName);
|
||||
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite);
|
||||
|
||||
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize);
|
||||
|
||||
FORCEINLINE PVOID PyasAllocate(SIZE_T Size) {
|
||||
return ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, PYAS_POOL_TAG);
|
||||
}
|
||||
|
||||
FORCEINLINE VOID PyasFree(PVOID Ptr) {
|
||||
if (Ptr) ExFreePoolWithTag(Ptr, PYAS_POOL_TAG);
|
||||
#pragma once
|
||||
#include <fltKernel.h>
|
||||
#include <ntddk.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <ntddscsi.h>
|
||||
#include <ntstrsafe.h>
|
||||
|
||||
constexpr auto PYAS_PORT_NAME = L"\\PYAS_Output_Pipe";
|
||||
constexpr auto MAX_PATH_LEN = 1024;
|
||||
constexpr auto PYAS_POOL_TAG = 'SAYP';
|
||||
|
||||
#ifndef PROCESS_TERMINATE
|
||||
#define PROCESS_TERMINATE (0x0001)
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_THREAD
|
||||
#define PROCESS_CREATE_THREAD (0x0002)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_OPERATION
|
||||
#define PROCESS_VM_OPERATION (0x0008)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_READ
|
||||
#define PROCESS_VM_READ (0x0010)
|
||||
#endif
|
||||
#ifndef PROCESS_VM_WRITE
|
||||
#define PROCESS_VM_WRITE (0x0020)
|
||||
#endif
|
||||
#ifndef PROCESS_DUP_HANDLE
|
||||
#define PROCESS_DUP_HANDLE (0x0040)
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_PROCESS
|
||||
#define PROCESS_CREATE_PROCESS (0x0080)
|
||||
#endif
|
||||
#ifndef PROCESS_SET_INFORMATION
|
||||
#define PROCESS_SET_INFORMATION (0x0200)
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_INFORMATION
|
||||
#define PROCESS_QUERY_INFORMATION (0x0400)
|
||||
#endif
|
||||
#ifndef PROCESS_SUSPEND_RESUME
|
||||
#define PROCESS_SUSPEND_RESUME (0x0800)
|
||||
#endif
|
||||
#ifndef PROCESS_QUERY_LIMITED_INFORMATION
|
||||
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
|
||||
#endif
|
||||
#ifndef PROCESS_SET_QUOTA
|
||||
#define PROCESS_SET_QUOTA (0x0100)
|
||||
#endif
|
||||
#ifndef THREAD_TERMINATE
|
||||
#define THREAD_TERMINATE (0x0001)
|
||||
#endif
|
||||
#ifndef THREAD_SUSPEND_RESUME
|
||||
#define THREAD_SUSPEND_RESUME (0x0002)
|
||||
#endif
|
||||
#ifndef THREAD_SET_CONTEXT
|
||||
#define THREAD_SET_CONTEXT (0x0010)
|
||||
#endif
|
||||
#ifndef THREAD_SET_INFORMATION
|
||||
#define THREAD_SET_INFORMATION (0x0020)
|
||||
#endif
|
||||
#ifndef THREAD_SET_THREAD_TOKEN
|
||||
#define THREAD_SET_THREAD_TOKEN (0x0080)
|
||||
#endif
|
||||
#ifndef POOL_FLAG_NON_PAGED
|
||||
#define POOL_FLAG_NON_PAGED 0x0000000000000040UI64
|
||||
#endif
|
||||
|
||||
#ifndef FSCTL_MANAGE_BYPASS_IO
|
||||
#define FSCTL_MANAGE_BYPASS_IO CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 188, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
|
||||
typedef enum _FS_BPIO_OPERATIONS {
|
||||
FS_BPIO_OP_ENABLE = 1,
|
||||
FS_BPIO_OP_DISABLE = 2,
|
||||
FS_BPIO_OP_QUERY = 3,
|
||||
FS_BPIO_OP_VOLUME_STACK_PAUSE = 4,
|
||||
FS_BPIO_OP_VOLUME_STACK_RESUME = 5,
|
||||
FS_BPIO_OP_STREAM_PAUSE = 6,
|
||||
FS_BPIO_OP_STREAM_RESUME = 7,
|
||||
FS_BPIO_OP_GET_INFO = 8
|
||||
} FS_BPIO_OPERATIONS;
|
||||
|
||||
typedef struct _FS_BPIO_INPUT {
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
ULONG InFlags;
|
||||
ULONGLONG Reserved1;
|
||||
ULONGLONG Reserved2;
|
||||
} FS_BPIO_INPUT, * PFS_BPIO_INPUT;
|
||||
|
||||
typedef struct _FS_BPIO_OUTPUT {
|
||||
FS_BPIO_OPERATIONS Operation;
|
||||
ULONG OutFlags;
|
||||
ULONGLONG Reserved1;
|
||||
ULONGLONG Reserved2;
|
||||
NTSTATUS Status;
|
||||
} FS_BPIO_OUTPUT, * PFS_BPIO_OUTPUT;
|
||||
|
||||
#endif
|
||||
|
||||
typedef struct _PYAS_MESSAGE {
|
||||
ULONG MessageCode;
|
||||
ULONG ProcessId;
|
||||
WCHAR Path[MAX_PATH_LEN];
|
||||
} PYAS_MESSAGE, * PPYAS_MESSAGE;
|
||||
|
||||
typedef struct _PYAS_USER_MESSAGE {
|
||||
ULONG Command;
|
||||
WCHAR Path[MAX_PATH_LEN];
|
||||
} PYAS_USER_MESSAGE, * PPYAS_USER_MESSAGE;
|
||||
|
||||
typedef struct _DRIVER_DATA {
|
||||
PDRIVER_OBJECT DriverObject;
|
||||
PFLT_FILTER FilterHandle;
|
||||
PFLT_PORT ServerPort;
|
||||
PFLT_PORT ClientPort;
|
||||
PEPROCESS PyasProcess;
|
||||
ULONG PyasPid;
|
||||
LARGE_INTEGER Cookie;
|
||||
KSPIN_LOCK PortMutex;
|
||||
KSPIN_LOCK TrackerMutex;
|
||||
KSPIN_LOCK PidLock;
|
||||
BOOLEAN Initialized;
|
||||
EX_RUNDOWN_REF PortRundown;
|
||||
} DRIVER_DATA, * PDRIVER_DATA;
|
||||
|
||||
typedef struct _RULE_NODE {
|
||||
struct _RULE_NODE* Next;
|
||||
UNICODE_STRING Pattern;
|
||||
} RULE_NODE, * PRULE_NODE;
|
||||
|
||||
extern DRIVER_DATA GlobalData;
|
||||
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreCreate(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreWrite(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_PreSetInfo(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_FileSystemControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectFile_SetSecurity(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
FLT_PREOP_CALLBACK_STATUS ProtectBoot_PreDeviceControl(PFLT_CALLBACK_DATA Data, PCFLT_RELATED_OBJECTS FltObjects, PVOID* CompletionContext);
|
||||
|
||||
NTSTATUS InitializeProcessProtection();
|
||||
VOID UninitializeProcessProtection();
|
||||
NTSTATUS InitializeRegistryProtection(PDRIVER_OBJECT DriverObject);
|
||||
VOID UninitializeRegistryProtection();
|
||||
|
||||
NTSTATUS LoadRulesFromDisk(PUNICODE_STRING RegistryPath);
|
||||
VOID UnloadRules();
|
||||
|
||||
VOID InitializeRulesEngine();
|
||||
VOID UninitializeRulesEngine();
|
||||
|
||||
VOID AddDynamicWhitelist(PUNICODE_STRING RuleStr);
|
||||
VOID RemoveDynamicWhitelist(PUNICODE_STRING RuleStr);
|
||||
|
||||
VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo);
|
||||
|
||||
NTSTATUS GetProcessImageName(HANDLE ProcessId, PUNICODE_STRING* ImageName);
|
||||
|
||||
ULONG SafeGetPyasPid();
|
||||
VOID SafeSetPyasPid(ULONG Pid);
|
||||
|
||||
BOOLEAN IsProcessTrusted(HANDLE ProcessId);
|
||||
BOOLEAN IsTargetProtected(HANDLE ProcessId);
|
||||
BOOLEAN WildcardMatch(PCWSTR Pattern, PCWSTR String, USHORT StringLengthBytes);
|
||||
|
||||
BOOLEAN CheckRegistryRule(PCUNICODE_STRING KeyName);
|
||||
BOOLEAN CheckFileExtensionRule(PCUNICODE_STRING FileName);
|
||||
BOOLEAN CheckProtectedPathRule(PCUNICODE_STRING FileName);
|
||||
BOOLEAN CheckRansomActivity(HANDLE ProcessId, PUNICODE_STRING FileName, PVOID WriteBuffer, ULONG WriteLength, BOOLEAN IsWrite);
|
||||
|
||||
NTSTATUS SendMessageToUser(ULONG Code, ULONG Pid, PWCHAR Path, USHORT PathSize);
|
||||
|
||||
FORCEINLINE PVOID PyasAllocate(SIZE_T Size) {
|
||||
return ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, PYAS_POOL_TAG);
|
||||
}
|
||||
|
||||
FORCEINLINE VOID PyasFree(PVOID Ptr) {
|
||||
if (Ptr) ExFreePoolWithTag(Ptr, PYAS_POOL_TAG);
|
||||
}
|
||||
+223
-219
@@ -1,220 +1,224 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
DRIVER_DATA GlobalData;
|
||||
static BOOLEAN g_ImageNotifyRegistered = FALSE;
|
||||
|
||||
static NTSTATUS InstanceSetup(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_SETUP_FLAGS Flags, DEVICE_TYPE VolumeDeviceType, FLT_FILESYSTEM_TYPE VolumeFilesystemType) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
UNREFERENCED_PARAMETER(VolumeDeviceType);
|
||||
UNREFERENCED_PARAMETER(VolumeFilesystemType);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS InstanceQueryTeardown(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID InstanceTeardownStart(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static VOID InstanceTeardownComplete(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
|
||||
if (GlobalData.ServerPort) {
|
||||
FltCloseCommunicationPort(GlobalData.ServerPort);
|
||||
GlobalData.ServerPort = NULL;
|
||||
}
|
||||
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
}
|
||||
|
||||
if (g_ImageNotifyRegistered) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ImageNotifyRegistered = FALSE;
|
||||
}
|
||||
|
||||
UninitializeRegistryProtection();
|
||||
UninitializeProcessProtection();
|
||||
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS PortMessage(PVOID PortCookie, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG ReturnOutputBufferLength) {
|
||||
UNREFERENCED_PARAMETER(PortCookie);
|
||||
UNREFERENCED_PARAMETER(OutputBuffer);
|
||||
UNREFERENCED_PARAMETER(OutputBufferLength);
|
||||
|
||||
if (InputBuffer && InputBufferLength >= sizeof(PYAS_USER_MESSAGE)) {
|
||||
PPYAS_USER_MESSAGE msg = (PPYAS_USER_MESSAGE)InputBuffer;
|
||||
if (msg->Command == 1 || msg->Command == 2) {
|
||||
msg->Path[MAX_PATH_LEN - 1] = L'\0';
|
||||
UNICODE_STRING us;
|
||||
RtlInitUnicodeString(&us, msg->Path);
|
||||
if (us.Length > 0) {
|
||||
if (msg->Command == 1) {
|
||||
AddDynamicWhitelist(&us);
|
||||
}
|
||||
else {
|
||||
RemoveDynamicWhitelist(&us);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ReturnOutputBufferLength) {
|
||||
*ReturnOutputBufferLength = 0;
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS PortConnect(PFLT_PORT ClientPort, PVOID ServerPortCookie, PVOID ConnectionContext, ULONG SizeOfContext, PVOID* ConnectionPortCookie) {
|
||||
UNREFERENCED_PARAMETER(ServerPortCookie);
|
||||
UNREFERENCED_PARAMETER(ConnectionContext);
|
||||
UNREFERENCED_PARAMETER(SizeOfContext);
|
||||
UNREFERENCED_PARAMETER(ConnectionPortCookie);
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
|
||||
|
||||
GlobalData.ClientPort = ClientPort;
|
||||
GlobalData.PyasPid = (ULONG)(ULONG_PTR)PsGetCurrentProcessId();
|
||||
GlobalData.PyasProcess = PsGetCurrentProcess();
|
||||
|
||||
ExReInitializeRundownProtection(&GlobalData.PortRundown);
|
||||
|
||||
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID PortDisconnect(PVOID ConnectionCookie) {
|
||||
UNREFERENCED_PARAMETER(ConnectionCookie);
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
|
||||
|
||||
GlobalData.ClientPort = NULL;
|
||||
GlobalData.PyasPid = 0;
|
||||
GlobalData.PyasProcess = NULL;
|
||||
|
||||
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
|
||||
|
||||
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
|
||||
}
|
||||
|
||||
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
|
||||
{ IRP_MJ_CREATE, 0, ProtectFile_PreCreate, NULL },
|
||||
{ IRP_MJ_WRITE, 0, ProtectFile_PreWrite, NULL },
|
||||
{ IRP_MJ_SET_INFORMATION, 0, ProtectFile_PreSetInfo, NULL },
|
||||
{ IRP_MJ_SET_SECURITY, 0, ProtectFile_SetSecurity, NULL },
|
||||
{ IRP_MJ_FILE_SYSTEM_CONTROL, 0, ProtectFile_FileSystemControl, NULL },
|
||||
{ IRP_MJ_DEVICE_CONTROL, 0, ProtectBoot_PreDeviceControl, NULL },
|
||||
{ IRP_MJ_OPERATION_END }
|
||||
};
|
||||
|
||||
CONST FLT_REGISTRATION FilterRegistration = {
|
||||
sizeof(FLT_REGISTRATION),
|
||||
FLT_REGISTRATION_VERSION,
|
||||
0,
|
||||
NULL,
|
||||
Callbacks,
|
||||
DriverUnload,
|
||||
InstanceSetup,
|
||||
InstanceQueryTeardown,
|
||||
InstanceTeardownStart,
|
||||
InstanceTeardownComplete,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
PSECURITY_DESCRIPTOR sd = NULL;
|
||||
OBJECT_ATTRIBUTES oa = { 0 };
|
||||
UNICODE_STRING name = { 0 };
|
||||
|
||||
RtlZeroMemory(&GlobalData, sizeof(GlobalData));
|
||||
GlobalData.DriverObject = DriverObject;
|
||||
KeInitializeSpinLock(&GlobalData.PortMutex);
|
||||
KeInitializeSpinLock(&GlobalData.TrackerMutex);
|
||||
|
||||
ExInitializeRundownProtection(&GlobalData.PortRundown);
|
||||
|
||||
InitializeRulesEngine();
|
||||
|
||||
status = LoadRulesFromDisk(RegistryPath);
|
||||
|
||||
status = FltRegisterFilter(DriverObject, &FilterRegistration, &GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
return status;
|
||||
}
|
||||
|
||||
status = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
|
||||
if (NT_SUCCESS(status)) {
|
||||
RtlInitUnicodeString(&name, PYAS_PORT_NAME);
|
||||
InitializeObjectAttributes(&oa, &name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, sd);
|
||||
status = FltCreateCommunicationPort(GlobalData.FilterHandle, &GlobalData.ServerPort, &oa, NULL, PortConnect, PortDisconnect, PortMessage, 1);
|
||||
FltFreeSecurityDescriptor(sd);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
return status;
|
||||
}
|
||||
|
||||
InitializeProcessProtection();
|
||||
|
||||
InitializeRegistryProtection(DriverObject);
|
||||
|
||||
NTSTATUS imageNotifyStatus = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
if (NT_SUCCESS(imageNotifyStatus)) {
|
||||
g_ImageNotifyRegistered = TRUE;
|
||||
}
|
||||
|
||||
status = FltStartFiltering(GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
if (g_ImageNotifyRegistered) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ImageNotifyRegistered = FALSE;
|
||||
}
|
||||
|
||||
UninitializeRegistryProtection();
|
||||
UninitializeProcessProtection();
|
||||
|
||||
if (GlobalData.ServerPort) {
|
||||
FltCloseCommunicationPort(GlobalData.ServerPort);
|
||||
GlobalData.ServerPort = NULL;
|
||||
}
|
||||
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
}
|
||||
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
}
|
||||
|
||||
return status;
|
||||
#include "DriverCommon.h"
|
||||
|
||||
DRIVER_DATA GlobalData;
|
||||
static BOOLEAN g_ImageNotifyRegistered = FALSE;
|
||||
|
||||
static NTSTATUS InstanceSetup(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_SETUP_FLAGS Flags, DEVICE_TYPE VolumeDeviceType, FLT_FILESYSTEM_TYPE VolumeFilesystemType) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
UNREFERENCED_PARAMETER(VolumeDeviceType);
|
||||
UNREFERENCED_PARAMETER(VolumeFilesystemType);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS InstanceQueryTeardown(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID InstanceTeardownStart(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static VOID InstanceTeardownComplete(PCFLT_RELATED_OBJECTS FltObjects, FLT_INSTANCE_TEARDOWN_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(FltObjects);
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
}
|
||||
|
||||
static NTSTATUS DriverUnload(FLT_FILTER_UNLOAD_FLAGS Flags) {
|
||||
UNREFERENCED_PARAMETER(Flags);
|
||||
|
||||
if (GlobalData.ServerPort) {
|
||||
FltCloseCommunicationPort(GlobalData.ServerPort);
|
||||
GlobalData.ServerPort = NULL;
|
||||
}
|
||||
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
}
|
||||
|
||||
if (g_ImageNotifyRegistered) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ImageNotifyRegistered = FALSE;
|
||||
}
|
||||
|
||||
UninitializeRegistryProtection();
|
||||
UninitializeProcessProtection();
|
||||
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
|
||||
GlobalData.Initialized = FALSE;
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS PortMessage(PVOID PortCookie, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG ReturnOutputBufferLength) {
|
||||
UNREFERENCED_PARAMETER(PortCookie);
|
||||
UNREFERENCED_PARAMETER(OutputBuffer);
|
||||
UNREFERENCED_PARAMETER(OutputBufferLength);
|
||||
|
||||
if (InputBuffer && InputBufferLength >= sizeof(PYAS_USER_MESSAGE)) {
|
||||
PPYAS_USER_MESSAGE msg = (PPYAS_USER_MESSAGE)InputBuffer;
|
||||
if (msg->Command == 1 || msg->Command == 2) {
|
||||
msg->Path[MAX_PATH_LEN - 1] = L'\0';
|
||||
UNICODE_STRING us;
|
||||
RtlInitUnicodeString(&us, msg->Path);
|
||||
if (us.Length > 0) {
|
||||
if (msg->Command == 1) {
|
||||
AddDynamicWhitelist(&us);
|
||||
}
|
||||
else {
|
||||
RemoveDynamicWhitelist(&us);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ReturnOutputBufferLength) {
|
||||
*ReturnOutputBufferLength = 0;
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS PortConnect(PFLT_PORT ClientPort, PVOID ServerPortCookie, PVOID ConnectionContext, ULONG SizeOfContext, PVOID* ConnectionPortCookie) {
|
||||
UNREFERENCED_PARAMETER(ServerPortCookie);
|
||||
UNREFERENCED_PARAMETER(ConnectionContext);
|
||||
UNREFERENCED_PARAMETER(SizeOfContext);
|
||||
UNREFERENCED_PARAMETER(ConnectionPortCookie);
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
|
||||
|
||||
GlobalData.ClientPort = ClientPort;
|
||||
SafeSetPyasPid((ULONG)(ULONG_PTR)PsGetCurrentProcessId());
|
||||
GlobalData.PyasProcess = PsGetCurrentProcess();
|
||||
|
||||
ExReInitializeRundownProtection(&GlobalData.PortRundown);
|
||||
|
||||
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
|
||||
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID PortDisconnect(PVOID ConnectionCookie) {
|
||||
UNREFERENCED_PARAMETER(ConnectionCookie);
|
||||
|
||||
KIRQL OldIrql;
|
||||
KeAcquireSpinLock(&GlobalData.PortMutex, &OldIrql);
|
||||
|
||||
GlobalData.ClientPort = NULL;
|
||||
SafeSetPyasPid(0);
|
||||
GlobalData.PyasProcess = NULL;
|
||||
|
||||
KeReleaseSpinLock(&GlobalData.PortMutex, OldIrql);
|
||||
|
||||
ExWaitForRundownProtectionRelease(&GlobalData.PortRundown);
|
||||
}
|
||||
|
||||
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
|
||||
{ IRP_MJ_CREATE, 0, ProtectFile_PreCreate, NULL },
|
||||
{ IRP_MJ_WRITE, 0, ProtectFile_PreWrite, NULL },
|
||||
{ IRP_MJ_SET_INFORMATION, 0, ProtectFile_PreSetInfo, NULL },
|
||||
{ IRP_MJ_SET_SECURITY, 0, ProtectFile_SetSecurity, NULL },
|
||||
{ IRP_MJ_FILE_SYSTEM_CONTROL, 0, ProtectFile_FileSystemControl, NULL },
|
||||
{ IRP_MJ_DEVICE_CONTROL, 0, ProtectBoot_PreDeviceControl, NULL },
|
||||
{ IRP_MJ_OPERATION_END }
|
||||
};
|
||||
|
||||
CONST FLT_REGISTRATION FilterRegistration = {
|
||||
sizeof(FLT_REGISTRATION),
|
||||
FLT_REGISTRATION_VERSION,
|
||||
0,
|
||||
NULL,
|
||||
Callbacks,
|
||||
DriverUnload,
|
||||
InstanceSetup,
|
||||
InstanceQueryTeardown,
|
||||
InstanceTeardownStart,
|
||||
InstanceTeardownComplete,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
PSECURITY_DESCRIPTOR sd = NULL;
|
||||
OBJECT_ATTRIBUTES oa = { 0 };
|
||||
UNICODE_STRING name = { 0 };
|
||||
|
||||
RtlZeroMemory(&GlobalData, sizeof(GlobalData));
|
||||
GlobalData.DriverObject = DriverObject;
|
||||
KeInitializeSpinLock(&GlobalData.PortMutex);
|
||||
KeInitializeSpinLock(&GlobalData.TrackerMutex);
|
||||
KeInitializeSpinLock(&GlobalData.PidLock);
|
||||
GlobalData.Initialized = TRUE;
|
||||
|
||||
ExInitializeRundownProtection(&GlobalData.PortRundown);
|
||||
|
||||
InitializeRulesEngine();
|
||||
|
||||
status = LoadRulesFromDisk(RegistryPath);
|
||||
|
||||
status = FltRegisterFilter(DriverObject, &FilterRegistration, &GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
return status;
|
||||
}
|
||||
|
||||
status = FltBuildDefaultSecurityDescriptor(&sd, FLT_PORT_ALL_ACCESS);
|
||||
if (NT_SUCCESS(status)) {
|
||||
RtlInitUnicodeString(&name, PYAS_PORT_NAME);
|
||||
InitializeObjectAttributes(&oa, &name, OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, sd);
|
||||
status = FltCreateCommunicationPort(GlobalData.FilterHandle, &GlobalData.ServerPort, &oa, NULL, PortConnect, PortDisconnect, PortMessage, 1);
|
||||
FltFreeSecurityDescriptor(sd);
|
||||
}
|
||||
|
||||
if (!NT_SUCCESS(status)) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
return status;
|
||||
}
|
||||
|
||||
InitializeProcessProtection();
|
||||
|
||||
InitializeRegistryProtection(DriverObject);
|
||||
|
||||
NTSTATUS imageNotifyStatus = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
if (NT_SUCCESS(imageNotifyStatus)) {
|
||||
g_ImageNotifyRegistered = TRUE;
|
||||
}
|
||||
|
||||
status = FltStartFiltering(GlobalData.FilterHandle);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
if (g_ImageNotifyRegistered) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ImageNotifyRegistered = FALSE;
|
||||
}
|
||||
|
||||
UninitializeRegistryProtection();
|
||||
UninitializeProcessProtection();
|
||||
|
||||
if (GlobalData.ServerPort) {
|
||||
FltCloseCommunicationPort(GlobalData.ServerPort);
|
||||
GlobalData.ServerPort = NULL;
|
||||
}
|
||||
|
||||
if (GlobalData.FilterHandle) {
|
||||
FltUnregisterFilter(GlobalData.FilterHandle);
|
||||
GlobalData.FilterHandle = NULL;
|
||||
}
|
||||
|
||||
UnloadRules();
|
||||
UninitializeRulesEngine();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "DriverCommon.h"
|
||||
|
||||
static PVOID ObRegistrationHandle = NULL;
|
||||
static OB_CALLBACK_REGISTRATION ObRegistration;
|
||||
static OB_OPERATION_REGISTRATION ObCallbacks[2];
|
||||
static OB_CALLBACK_REGISTRATION ObRegistration = {0};
|
||||
static OB_OPERATION_REGISTRATION ObCallbacks[2] = {0};
|
||||
static KSPIN_LOCK ObDataLock = {0};
|
||||
static BOOLEAN ObDataInitialized = FALSE;
|
||||
|
||||
static BOOLEAN IsSystemImage(PUNICODE_STRING FullImageName) {
|
||||
if (!FullImageName || !FullImageName->Buffer) return FALSE;
|
||||
@@ -57,6 +59,10 @@ static BOOLEAN IsCriticalSystemProcess(HANDLE ProcessId) {
|
||||
WildcardMatch(L"*\\Windows\\System32\\smss.exe", imageFileName->Buffer, imageFileName->Length)) {
|
||||
isCritical = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (imageFileName) {
|
||||
if (imageFileName->Buffer) ExFreePool(imageFileName->Buffer);
|
||||
ExFreePool(imageFileName);
|
||||
}
|
||||
|
||||
@@ -77,15 +83,17 @@ static BOOLEAN IsBlacklistedAdminTool(HANDLE ProcessId) {
|
||||
|
||||
status = SeLocateProcessImageName(Process, &imageFileName);
|
||||
|
||||
if (NT_SUCCESS(status) && imageFileName) {
|
||||
if (imageFileName->Buffer) {
|
||||
if (WildcardMatch(L"*Taskmgr.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*taskkill.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*ProcessHacker.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*procmon.exe", imageFileName->Buffer, imageFileName->Length)) {
|
||||
isBlacklisted = TRUE;
|
||||
}
|
||||
if (NT_SUCCESS(status) && imageFileName && imageFileName->Buffer) {
|
||||
if (WildcardMatch(L"*Taskmgr.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*taskkill.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*ProcessHacker.exe", imageFileName->Buffer, imageFileName->Length) ||
|
||||
WildcardMatch(L"*procmon.exe", imageFileName->Buffer, imageFileName->Length)) {
|
||||
isBlacklisted = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (imageFileName) {
|
||||
if (imageFileName->Buffer) ExFreePool(imageFileName->Buffer);
|
||||
ExFreePool(imageFileName);
|
||||
}
|
||||
|
||||
@@ -105,20 +113,37 @@ 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 ((ULONG)(ULONG_PTR)SourcePid == SafeGetPyasPid()) return OB_PREOP_SUCCESS;
|
||||
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
|
||||
|
||||
if (IsTargetProtected(TargetPid)) {
|
||||
if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS;
|
||||
|
||||
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
|
||||
|
||||
if (bIsTrusted) {
|
||||
if (IsBlacklistedAdminTool(SourcePid)) {
|
||||
bIsTrusted = FALSE;
|
||||
}
|
||||
BOOLEAN isBlacklisted = FALSE;
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
isBlacklisted = IsBlacklistedAdminTool(SourcePid);
|
||||
}
|
||||
|
||||
if (isBlacklisted) {
|
||||
ACCESS_MASK DenyMask = PROCESS_TERMINATE |
|
||||
PROCESS_VM_OPERATION |
|
||||
PROCESS_VM_WRITE |
|
||||
PROCESS_CREATE_THREAD |
|
||||
PROCESS_VM_READ |
|
||||
PROCESS_DUP_HANDLE |
|
||||
PROCESS_SUSPEND_RESUME |
|
||||
PROCESS_SET_INFORMATION |
|
||||
PROCESS_SET_QUOTA;
|
||||
|
||||
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
|
||||
|
||||
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
|
||||
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
|
||||
}
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
|
||||
if (bIsTrusted) return OB_PREOP_SUCCESS;
|
||||
|
||||
ACCESS_MASK DenyMask = PROCESS_TERMINATE |
|
||||
@@ -156,20 +181,33 @@ static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE
|
||||
HANDLE SourcePid = PsGetCurrentProcessId();
|
||||
|
||||
if (SourcePid == TargetPid) return OB_PREOP_SUCCESS;
|
||||
if ((ULONG)(ULONG_PTR)SourcePid == GlobalData.PyasPid) return OB_PREOP_SUCCESS;
|
||||
if ((ULONG)(ULONG_PTR)SourcePid == SafeGetPyasPid()) return OB_PREOP_SUCCESS;
|
||||
if (SourcePid == (HANDLE)4) return OB_PREOP_SUCCESS;
|
||||
|
||||
if (IsTargetProtected(TargetPid)) {
|
||||
if (IsCriticalSystemProcess(SourcePid)) return OB_PREOP_SUCCESS;
|
||||
|
||||
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
|
||||
|
||||
if (bIsTrusted) {
|
||||
if (IsBlacklistedAdminTool(SourcePid)) {
|
||||
bIsTrusted = FALSE;
|
||||
}
|
||||
BOOLEAN isBlacklisted = FALSE;
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
isBlacklisted = IsBlacklistedAdminTool(SourcePid);
|
||||
}
|
||||
|
||||
if (isBlacklisted) {
|
||||
ACCESS_MASK DenyMask = THREAD_TERMINATE |
|
||||
THREAD_SUSPEND_RESUME |
|
||||
THREAD_SET_CONTEXT |
|
||||
THREAD_SET_INFORMATION |
|
||||
THREAD_SET_THREAD_TOKEN;
|
||||
|
||||
OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
|
||||
|
||||
if (OperationInformation->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
|
||||
OperationInformation->Parameters->DuplicateHandleInformation.DesiredAccess &= (ACCESS_MASK)(~DenyMask);
|
||||
}
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
BOOLEAN bIsTrusted = IsProcessTrusted(SourcePid);
|
||||
if (bIsTrusted) return OB_PREOP_SUCCESS;
|
||||
|
||||
ACCESS_MASK DenyMask = THREAD_TERMINATE |
|
||||
@@ -191,6 +229,9 @@ static OB_PREOP_CALLBACK_STATUS PreOpenThread(PVOID RegistrationContext, POB_PRE
|
||||
NTSTATUS InitializeProcessProtection() {
|
||||
static UNICODE_STRING Altitude = RTL_CONSTANT_STRING(L"320000.PYAS.Ob");
|
||||
|
||||
KeInitializeSpinLock(&ObDataLock);
|
||||
ObDataInitialized = TRUE;
|
||||
|
||||
ObCallbacks[0].ObjectType = PsProcessType;
|
||||
ObCallbacks[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
ObCallbacks[0].PreOperation = PreOpenProcess;
|
||||
@@ -219,6 +260,8 @@ NTSTATUS InitializeProcessProtection() {
|
||||
}
|
||||
|
||||
VOID UninitializeProcessProtection() {
|
||||
ObDataInitialized = FALSE;
|
||||
|
||||
if (ObRegistrationHandle) {
|
||||
ObUnRegisterCallbacks(ObRegistrationHandle);
|
||||
ObRegistrationHandle = NULL;
|
||||
|
||||
+984
-932
File diff suppressed because it is too large.
Load diff
@@ -128,9 +128,9 @@
|
||||
"*\\360Safe.exe",
|
||||
"*\\bilibili\\bilibili.exe",
|
||||
|
||||
"*\\Kingsoft\\WPS Office\\ksolaunch.exe",
|
||||
"*\\Kingsoft\\WPS Office\\*\\wps.exe",
|
||||
"*\\Kingsoft\\WPS Office\\*\\*.exe",
|
||||
"*\\WPS Office\\ksolaunch.exe",
|
||||
"*\\WPS Office\\*\\wps.exe",
|
||||
"*\\WPS Office\\*\\*.exe",
|
||||
|
||||
"*\\PC4399_WPFLauncher.exe",
|
||||
"*\\bin\\APlayer.exe",
|
||||
|
||||
Reference in new issue
Block a user