Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
264dd50969 | ||
|
|
26d0ad1a6e | ||
|
|
90284018e5 | ||
|
|
46eeec16d4 | ||
|
|
083fb0dccc | ||
|
|
d46f145806 | ||
|
|
868eddfd2b | ||
|
|
8295e6bb5b | ||
|
|
424914b842 | ||
|
|
8c5027df0f | ||
|
|
0b7817d992 | ||
|
|
ca6c9bfd05 | ||
|
|
0a1abd8040 | ||
|
|
ed69a4fdf6 | ||
|
|
2ae19a6d4e | ||
|
|
404bc50dd4 | ||
|
|
cada5eeeaf | ||
|
|
5e9bd40e8b | ||
|
|
0788bab0dd | ||
|
|
4e8e0dbea7 | ||
|
|
b8c5279137 | ||
|
|
a3255b1681 | ||
|
|
f151379f8f |
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import os, gc, re, sys, time, json, base64
|
||||
import os, gc, re, sys, time, json, shutil
|
||||
import ctypes, ctypes.wintypes, threading
|
||||
import requests, webbrowser, winreg, shutil
|
||||
import traceback, hashlib, msvcrt, pyperclip
|
||||
import requests, webbrowser, winreg, msvcrt
|
||||
import traceback, hashlib
|
||||
|
||||
from PYAS_Config import *
|
||||
from PYAS_Engine import *
|
||||
@@ -89,6 +89,8 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.python = sys.executable
|
||||
self.process_timer = QTimer(self)
|
||||
|
||||
self.sign = sign_scanner()
|
||||
self.sign.init_windll(["wintrust"])
|
||||
self.model = model_scanner()
|
||||
self.model.load_path(self.path_models)
|
||||
self.rule = rule_scanner()
|
||||
@@ -112,7 +114,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
"traditional_switch", "simplified_switch", "english_switch",
|
||||
]
|
||||
self.pyas_default = {
|
||||
"version": "3.3.4",
|
||||
"version": "3.3.5",
|
||||
"product": "00000-00000-00000-00000-00000",
|
||||
"language": "english_switch",
|
||||
"theme": "white_switch",
|
||||
@@ -131,15 +133,17 @@ class MainWindow_Controller(QMainWindow):
|
||||
{"exe": "explorer.exe", "class": "", "title": ""},
|
||||
]
|
||||
self.block_replace = {
|
||||
"BOOT_BLOCK": "引導行為攔截",
|
||||
"REG_BLOCK": "註冊表行為攔截",
|
||||
"FILE_BLOCK": "檔案行為攔截",
|
||||
"RANSOM_BLOCK": "勒索行為攔截",
|
||||
"CLR_BLOCK": "托管行為攔截",
|
||||
"SHELLCODE_BLOCK": "殼碼行為攔截",
|
||||
"BOOT_BLOCK": "引導行為攔截",
|
||||
"REG_BLOCK": "註冊表行為攔截",
|
||||
"CLR_BLOCK": "執行環境攔截",
|
||||
"SHELLCODE_BLOCK": "殼碼加載攔截",
|
||||
"THREAD_BLOCK": "線程行為攔截",
|
||||
"INJECT_BLOCK": "注入行為攔截",
|
||||
"PROC_BLOCK": "進程行為攔截",
|
||||
"REMOTE_BLOCK": "遠控行為攔截",
|
||||
"SCREEN_BLOCK": "監控行為攔截",
|
||||
}
|
||||
|
||||
####################################################################################################
|
||||
@@ -266,6 +270,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.last_widget = self.widgets.get("home_window")
|
||||
self.setup_theme_mapping()
|
||||
self.setup_process_menu()
|
||||
self.setup_scan_menu()
|
||||
|
||||
def create_shadow(self):
|
||||
shadow = QGraphicsDropShadowEffect(self)
|
||||
@@ -348,7 +353,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
elif self.install_system_driver():
|
||||
self.start_daemon_thread(self.pipe_server_thread)
|
||||
else:
|
||||
self.send_message("驅動防護啟用失敗", "warn", True)
|
||||
self.send_message("驅動防護啟用失敗,請重啟裝置來修復錯誤", "warn", True)
|
||||
elif name == "network_switch":
|
||||
if checked:
|
||||
self.start_daemon_thread(self.protect_net_thread)
|
||||
@@ -572,6 +577,8 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.pyas_config["system_switch"] = False
|
||||
self.pyas_config["driver_switch"] = False
|
||||
self.pyas_config["network_switch"] = False
|
||||
|
||||
self.stop_button()
|
||||
self.stop_system_driver()
|
||||
|
||||
self.minimize_button()
|
||||
@@ -618,7 +625,8 @@ class MainWindow_Controller(QMainWindow):
|
||||
if not isinstance(message, str):
|
||||
message = str(message)
|
||||
if message and translate:
|
||||
message = self.trans(self.pyas_config["language"], message)
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
message = self.trans(lang, message)
|
||||
|
||||
m = mode.lower()
|
||||
if m == "quest":
|
||||
@@ -639,7 +647,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
elif m == "save":
|
||||
select = QFileDialog.getSaveFileName(self, message)[0]
|
||||
|
||||
select = select or False
|
||||
select = select or None
|
||||
now = time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
output = f"[{now}] | {mode} | {message} | {select}"
|
||||
log_widget = self.widgets.get("log_text")
|
||||
@@ -650,6 +658,8 @@ class MainWindow_Controller(QMainWindow):
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def invoke_method(self, obj, method, *args):
|
||||
q_args = []
|
||||
for arg in args:
|
||||
@@ -661,14 +671,25 @@ class MainWindow_Controller(QMainWindow):
|
||||
q_args.append(Q_ARG("QString", str(arg)))
|
||||
QMetaObject.invokeMethod(obj, method, Qt.QueuedConnection, *q_args)
|
||||
|
||||
def norm_path(self, path):
|
||||
if isinstance(path, (list, tuple, set)):
|
||||
t = type(path)
|
||||
return t(self.norm_path(p) for p in path)
|
||||
def norm_path(self, path, must_exist=True):
|
||||
if isinstance(path, list):
|
||||
return [p for p in (self.norm_path(x, must_exist) for x in path) if p]
|
||||
if isinstance(path, tuple):
|
||||
return tuple(p for p in (self.norm_path(x, must_exist) for x in path) if p)
|
||||
if isinstance(path, set):
|
||||
return {p for p in (self.norm_path(x, must_exist) for x in path) if p}
|
||||
if isinstance(path, str):
|
||||
return os.path.normpath(os.path.abspath(path))
|
||||
ap = os.path.normpath(os.path.abspath(path))
|
||||
return ap if (not must_exist or os.path.exists(ap)) else None
|
||||
return path
|
||||
|
||||
def path_equal(self, a, b):
|
||||
pa = self.norm_path(a, must_exist=False)
|
||||
pb = self.norm_path(b, must_exist=False)
|
||||
if not pa or not pb:
|
||||
return False
|
||||
return os.path.normcase(pa) == os.path.normcase(pb)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
@Slot()
|
||||
@@ -696,9 +717,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
@Slot(str)
|
||||
def slot_scan_result(self, msg):
|
||||
self.widgets["progress_text"].setText(msg)
|
||||
count = len(self.virus_results)
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
self.widgets["progress_text"].setText(msg)
|
||||
self.widgets["stop_button"].hide()
|
||||
self.widgets["method_button"].show()
|
||||
self.send_message(msg, "notify", True)
|
||||
@@ -713,8 +734,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.scan_start = time.time()
|
||||
self.last_widget = self.widgets.get("scan_window")
|
||||
self.scan_reset_signal.emit()
|
||||
self.progress_title_signal.emit(self.trans(self.pyas_config["language"], "正在掃描"))
|
||||
self.scan_progress_signal.emit(self.trans(self.pyas_config["language"], "正在初始化中"))
|
||||
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.scan_progress_signal.emit(self.trans(lang, "正在初始化中"))
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
@@ -748,21 +770,27 @@ class MainWindow_Controller(QMainWindow):
|
||||
if self.is_in_whitelist(norm_path):
|
||||
continue
|
||||
self.scan_count += 1
|
||||
|
||||
state = self.scan_engine(norm_path)
|
||||
if state:
|
||||
self.virus_results.append(norm_path)
|
||||
self.scan_add_virus_signal.emit(f"[{state}] > {norm_path}")
|
||||
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.progress_title_signal.emit(self.trans(lang, "正在掃描"))
|
||||
self.scan_progress_signal.emit(norm_path)
|
||||
gc.collect()
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
finally:
|
||||
elapsed = int(time.time() - self.scan_start)
|
||||
count = len(self.virus_results)
|
||||
elapsed = int(time.time() - self.scan_start)
|
||||
result = (
|
||||
f"發現 {count} 個病毒,共掃描 {self.scan_count} 檔案,耗時 {elapsed} 秒"
|
||||
if count else f"未發現病毒,共掃描 {self.scan_count} 檔案,耗時 {elapsed} 秒")
|
||||
self.progress_title_signal.emit(self.trans(self.pyas_config["language"], "病毒掃描"))
|
||||
self.scan_result_signal.emit(self.trans(self.pyas_config["language"], result))
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.progress_title_signal.emit(self.trans(lang, "病毒掃描"))
|
||||
self.scan_result_signal.emit(self.trans(lang, result))
|
||||
|
||||
def yield_files(self, targets):
|
||||
if isinstance(targets, str):
|
||||
@@ -778,6 +806,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
|
||||
def scan_engine(self, file_path):
|
||||
try:
|
||||
if self.sign.sign_verify(file_path):
|
||||
return False
|
||||
|
||||
label, level = self.model.model_scan(file_path)
|
||||
if label and level >= self.pyas_config.get("sensitive", 95):
|
||||
return f"{label}.{level}"
|
||||
@@ -786,15 +817,15 @@ class MainWindow_Controller(QMainWindow):
|
||||
label, level = self.rule.yara_scan(file_path)
|
||||
if label and level:
|
||||
return f"{label}.{level}"
|
||||
return False
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
return None
|
||||
return None
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def solve_button(self):
|
||||
deleted = 0
|
||||
started = False
|
||||
virus_list = self.widgets["virus_list"]
|
||||
start_ts = time.time()
|
||||
|
||||
@@ -802,40 +833,90 @@ class MainWindow_Controller(QMainWindow):
|
||||
item = virus_list.item(i)
|
||||
if item.checkState() == Qt.Checked:
|
||||
file_path = self.norm_path(item.text().split(">")[1].strip())
|
||||
if not started:
|
||||
self.progress_title_signal.emit(self.trans(self.pyas_config["language"], "正在刪除"))
|
||||
started = True
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.progress_title_signal.emit(self.trans(lang, "正在刪除"))
|
||||
self.scan_progress_signal.emit(file_path)
|
||||
|
||||
try:
|
||||
os.remove(file_path)
|
||||
deleted += 1
|
||||
virus_list.takeItem(i)
|
||||
try:
|
||||
self.virus_results.remove(file_path)
|
||||
except ValueError:
|
||||
pass
|
||||
self.virus_results.remove(file_path)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
QApplication.processEvents()
|
||||
remain = virus_list.count()
|
||||
|
||||
count = virus_list.count()
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
elapsed = int(time.time() - start_ts)
|
||||
self.widgets["solve_button"].setVisible(remain > 0)
|
||||
self.progress_title_signal.emit(self.trans(self.pyas_config["language"], "病毒掃描"))
|
||||
result = (
|
||||
f"剩餘 {remain} 個病毒,共刪除 {deleted} 檔案,耗時 {elapsed} 秒"
|
||||
if remain else f"未剩餘病毒,共刪除 {deleted} 檔案,耗時 {elapsed} 秒")
|
||||
self.scan_result_signal.emit(self.trans(self.pyas_config["language"], result))
|
||||
f"剩餘 {count} 個病毒,共刪除 {deleted} 檔案,耗時 {elapsed} 秒"
|
||||
if count else f"未剩餘病毒,共刪除 {deleted} 檔案,耗時 {elapsed} 秒")
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
self.progress_title_signal.emit(self.trans(lang, "病毒掃描"))
|
||||
self.scan_result_signal.emit(self.trans(lang, result))
|
||||
|
||||
def stop_button(self):
|
||||
self.scan_running = False
|
||||
self.widgets["stop_button"].hide()
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def setup_scan_menu(self):
|
||||
widget = self.widgets.get("virus_list")
|
||||
if widget:
|
||||
widget.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
widget.customContextMenuRequested.connect(self.show_scan_menu)
|
||||
|
||||
def show_scan_menu(self, pos):
|
||||
widget = self.widgets.get("virus_list")
|
||||
if not widget:
|
||||
return
|
||||
item = widget.itemAt(pos)
|
||||
if not item:
|
||||
return
|
||||
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
menu = QMenu()
|
||||
copy_action = menu.addAction(self.trans(lang, "複製路徑"))
|
||||
del_action = menu.addAction(self.trans(lang, "刪除檔案"))
|
||||
white_action = menu.addAction(self.trans(lang, "增加白名單"))
|
||||
act = menu.exec(widget.viewport().mapToGlobal(pos))
|
||||
|
||||
text = item.text()
|
||||
if ">" in text:
|
||||
path = text.split(">", 1)[1].strip()
|
||||
else:
|
||||
path = text.strip()
|
||||
|
||||
if act == copy_action:
|
||||
QGuiApplication.clipboard().setText(str(path or "").replace("/", "\\"))
|
||||
|
||||
elif act == del_action:
|
||||
file_path = self.norm_path(path)
|
||||
if file_path and self.send_message("您確定要刪除檔案嗎?", "quest"):
|
||||
try:
|
||||
os.remove(file_path)
|
||||
widget.takeItem(widget.row(item))
|
||||
self.virus_results.remove(file_path)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
count = len(self.virus_results)
|
||||
self.widgets["solve_button"].setVisible(count > 0)
|
||||
|
||||
elif act == white_action:
|
||||
file_path = self.norm_path(path)
|
||||
if file_path and self.send_message("您確定要增加到白名單嗎?", "quest"):
|
||||
n = self.manage_named_list("white_list", [file_path], action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info", True)
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def list_process(self):
|
||||
pe = self.get_process_entry()
|
||||
snapshot = self.kernel32.CreateToolhelp32Snapshot(0x00000002, 0)
|
||||
result, success = [], self.kernel32.Process32First(snapshot, ctypes.byref(pe))
|
||||
|
||||
while success:
|
||||
pid = pe.th32ProcessID
|
||||
name, file_path = (None, None)
|
||||
@@ -848,6 +929,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
name = raw.decode("mbcs", errors="replace") or None
|
||||
except Exception:
|
||||
name = None
|
||||
|
||||
result.append((pid, name, file_path))
|
||||
success = self.kernel32.Process32Next(snapshot, ctypes.byref(pe))
|
||||
self.kernel32.CloseHandle(snapshot)
|
||||
@@ -858,6 +940,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.proc_pid_map = [pid for pid, _, _ in procs]
|
||||
widget = self.widgets.get("manage_list")
|
||||
info_text = self.widgets.get("info_text")
|
||||
|
||||
if widget:
|
||||
model = QStringListModel([f"{name or 'None'} | {pid} | {file_path or 'None'}" for pid, name, file_path in procs])
|
||||
widget.setModel(model)
|
||||
@@ -879,6 +962,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
def start_process_timer(self):
|
||||
if hasattr(self, "process_timer") and self.process_timer.isActive():
|
||||
return
|
||||
|
||||
self.process_timer = QTimer(self)
|
||||
self.process_timer.timeout.connect(self.refresh_process)
|
||||
self.process_timer.start(0)
|
||||
@@ -900,19 +984,36 @@ class MainWindow_Controller(QMainWindow):
|
||||
idx = widget.indexAt(pos)
|
||||
if not idx.isValid() or idx.row() >= len(self.proc_pid_map):
|
||||
return
|
||||
pid = self.proc_pid_map[idx.row()]
|
||||
|
||||
lang = self.pyas_config.get("language", "english_switch")
|
||||
menu = QMenu()
|
||||
kill_action = menu.addAction("結束進程")
|
||||
if menu.exec(widget.viewport().mapToGlobal(pos)) == kill_action:
|
||||
copy_action = menu.addAction(self.trans(lang, "複製路徑"))
|
||||
kill_action = menu.addAction(self.trans(lang, "結束進程"))
|
||||
white_action = menu.addAction(self.trans(lang, "增加白名單"))
|
||||
act = menu.exec(widget.viewport().mapToGlobal(pos))
|
||||
|
||||
pid = self.proc_pid_map[idx.row()]
|
||||
name, file_path = self.get_exe_info(pid)
|
||||
|
||||
if act == copy_action:
|
||||
QGuiApplication.clipboard().setText(str(file_path or "").replace("/", "\\"))
|
||||
|
||||
elif act == kill_action:
|
||||
self.kill_process(pid)
|
||||
|
||||
elif act == white_action:
|
||||
if file_path and self.send_message("您確定要增加到白名單嗎?", "quest"):
|
||||
file_path = self.norm_path(file_path or "")
|
||||
n = self.manage_named_list("white_list", [file_path], action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info", True)
|
||||
|
||||
def kill_process(self, pid):
|
||||
try:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if h:
|
||||
file_path = self.norm_path(self.get_process_file(h))
|
||||
if hasattr(self, "file_pyas") and file_path == getattr(self, "file_pyas", None):
|
||||
self.close()
|
||||
if self.path_equal(file_path, self.file_pyas):
|
||||
self.close_button()
|
||||
else:
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.kernel32.CloseHandle(h)
|
||||
@@ -997,6 +1098,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
def repair_system_mbr(self):
|
||||
if not hasattr(self, "mbr_backup") or not self.mbr_backup:
|
||||
return
|
||||
|
||||
for drive, mbr_value in self.mbr_backup.items():
|
||||
drive_path = rf"\\.\PhysicalDrive{drive}"
|
||||
try:
|
||||
@@ -1015,9 +1117,11 @@ class MainWindow_Controller(QMainWindow):
|
||||
wallpaper = os.path.join(self.path_system, "web", "wallpaper", "Windows", "img0.jpg")
|
||||
if not os.path.exists(wallpaper):
|
||||
return
|
||||
|
||||
key = r"Control Panel\\Desktop"
|
||||
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, key, 0, winreg.KEY_SET_VALUE) as reg:
|
||||
winreg.SetValueEx(reg, "Wallpaper", 0, winreg.REG_SZ, wallpaper)
|
||||
|
||||
theme_dir = os.path.join(self.path_appdata, "Microsoft", "Windows", "Themes")
|
||||
for fname in ["TranscodedWallpaper", "TranscodedWallpaper.tmp"]:
|
||||
fpath = os.path.join(theme_dir, fname)
|
||||
@@ -1026,6 +1130,7 @@ class MainWindow_Controller(QMainWindow):
|
||||
os.remove(fpath)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
cache_dir = os.path.join(theme_dir, "CachedFiles")
|
||||
if os.path.exists(cache_dir):
|
||||
try:
|
||||
@@ -1135,11 +1240,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
if self.send_message("請選擇要攔截的軟體彈窗", "quest", True):
|
||||
while True:
|
||||
QApplication.processEvents()
|
||||
title, class_name, process_name = self.get_window_info(self.user32.GetForegroundWindow())
|
||||
if process_name and not any(self.window_rule_match(item, process_name, class_name, title) for item in self.pass_windows):
|
||||
if self.send_message(f"您確定要攔截 {process_name} ({title}) 嗎?", "quest", True):
|
||||
if self.add_window_rule(self.pyas_config["block_list"], process_name, class_name, title):
|
||||
self.send_message(f"成功增加到彈窗攔截: {process_name} ({title})", "info", True)
|
||||
|
||||
title, class_name, proc_name = self.get_window_info(self.user32.GetForegroundWindow())
|
||||
if proc_name and not any(self.window_rule_match(item, proc_name, class_name, title) for item in self.pass_windows):
|
||||
if self.send_message(f"您確定要攔截 {proc_name} ({title}) 嗎?", "quest", True):
|
||||
if self.add_window_rule(self.pyas_config["block_list"], proc_name, class_name, title):
|
||||
self.send_message(f"成功增加到彈窗攔截: {proc_name} ({title})", "info", True)
|
||||
else:
|
||||
self.send_message("已存在彈窗攔截", "info", True)
|
||||
break
|
||||
@@ -1151,32 +1257,33 @@ class MainWindow_Controller(QMainWindow):
|
||||
if self.send_message("請選擇要取消攔截的軟體彈窗", "quest", True):
|
||||
while True:
|
||||
QApplication.processEvents()
|
||||
title, class_name, process_name = self.get_window_info(self.user32.GetForegroundWindow())
|
||||
if process_name and not any(self.window_rule_match(item, process_name, class_name, title) for item in self.pass_windows):
|
||||
if self.send_message(f"您確定要取消攔截 {process_name} ({title}) 嗎?", "quest", True):
|
||||
if self.remove_window_rule(self.pyas_config["block_list"], process_name, class_name, title):
|
||||
self.send_message(f"成功取消彈窗攔截: {process_name} ({title})", "info", True)
|
||||
|
||||
title, class_name, proc_name = self.get_window_info(self.user32.GetForegroundWindow())
|
||||
if proc_name and not any(self.window_rule_match(item, proc_name, class_name, title) for item in self.pass_windows):
|
||||
if self.send_message(f"您確定要取消攔截 {proc_name} ({title}) 嗎?", "quest", True):
|
||||
if self.remove_window_rule(self.pyas_config["block_list"], proc_name, class_name, title):
|
||||
self.send_message(f"成功取消彈窗攔截: {proc_name} ({title})", "info", True)
|
||||
else:
|
||||
self.send_message("未找到彈窗攔截", "info", True)
|
||||
break
|
||||
self.start_daemon_thread(self.popup_intercept_thread)
|
||||
|
||||
def window_rule_match(self, item, process_name, class_name, title):
|
||||
for k, v in zip(["exe", "class", "title"], [process_name, class_name, title]):
|
||||
def window_rule_match(self, item, proc_name, class_name, title):
|
||||
for k, v in zip(["exe", "class", "title"], [proc_name, class_name, title]):
|
||||
if item.get(k, "") and item.get(k, "") != v:
|
||||
return False
|
||||
return True
|
||||
|
||||
def add_window_rule(self, rule_list, process_name, class_name, title):
|
||||
if not any(self.window_rule_match(item, process_name, class_name, title) for item in rule_list):
|
||||
rule_list.append({"exe": process_name, "class": class_name, "title": title})
|
||||
def add_window_rule(self, rule_list, proc_name, class_name, title):
|
||||
if not any(self.window_rule_match(item, proc_name, class_name, title) for item in rule_list):
|
||||
rule_list.append({"exe": proc_name, "class": class_name, "title": title})
|
||||
self.save_config(self.file_config, self.pyas_config)
|
||||
return True
|
||||
return False
|
||||
|
||||
def remove_window_rule(self, rule_list, process_name, class_name, title):
|
||||
def remove_window_rule(self, rule_list, proc_name, class_name, title):
|
||||
before = len(rule_list)
|
||||
rule_list[:] = [item for item in rule_list if not self.window_rule_match(item, process_name, class_name, title)]
|
||||
rule_list[:] = [item for item in rule_list if not self.window_rule_match(item, proc_name, class_name, title)]
|
||||
if len(rule_list) < before:
|
||||
self.save_config(self.file_config, self.pyas_config)
|
||||
return True
|
||||
@@ -1190,12 +1297,26 @@ class MainWindow_Controller(QMainWindow):
|
||||
rules = self.pyas_config.get("block_list", [])
|
||||
if not rules:
|
||||
continue
|
||||
|
||||
for hWnd in self.get_all_windows():
|
||||
title, class_name, process_name = self.get_window_info(hWnd)
|
||||
if (not any(self.window_rule_match(item, process_name, class_name, title) for item in self.pass_windows) and
|
||||
any(self.window_rule_match(item, process_name, class_name, title) for item in rules)):
|
||||
title, class_name, proc_name = self.get_window_info(hWnd)
|
||||
if (not any(self.window_rule_match(item, proc_name, class_name, title) for item in self.pass_windows) and
|
||||
any(self.window_rule_match(item, proc_name, class_name, title) for item in rules)):
|
||||
closed = False
|
||||
|
||||
for msg in [0x0010, 0x0002, 0x0012, 0x0112]:
|
||||
self.user32.SendMessageW(hWnd, msg, 0xF060, 0)
|
||||
if self.user32.SendMessageW(hWnd, msg, 0xF060, 0) != 0:
|
||||
closed = True
|
||||
break
|
||||
|
||||
if not closed and proc_name:
|
||||
pid = ctypes.c_ulong()
|
||||
self.user32.GetWindowThreadProcessId(hWnd, ctypes.byref(pid))
|
||||
if pid.value:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid.value)
|
||||
if h:
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
@@ -1219,73 +1340,79 @@ class MainWindow_Controller(QMainWindow):
|
||||
self.user32.GetClassNameW(hWnd, class_name, 256)
|
||||
pid = ctypes.c_ulong()
|
||||
self.user32.GetWindowThreadProcessId(hWnd, ctypes.byref(pid))
|
||||
process_name = ""
|
||||
|
||||
proc_name = ""
|
||||
try:
|
||||
process_name = self.get_process_name_by_pid(pid.value)
|
||||
proc_name = self.get_process_name(pid.value)
|
||||
except Exception:
|
||||
pass
|
||||
return str(title.value), str(class_name.value), process_name
|
||||
return str(title.value), str(class_name.value), proc_name
|
||||
|
||||
def get_process_name_by_pid(self, pid):
|
||||
def get_process_name(self, pid):
|
||||
name, file_path = self.get_exe_info(pid)
|
||||
if name:
|
||||
return name
|
||||
snapshot = self.kernel32.CreateToolhelp32Snapshot(0x00000002, 0)
|
||||
entry = PROCESSENTRY32()
|
||||
entry.dwSize = ctypes.sizeof(PROCESSENTRY32)
|
||||
name = ""
|
||||
|
||||
result = ""
|
||||
if self.kernel32.Process32First(snapshot, ctypes.byref(entry)):
|
||||
while True:
|
||||
if entry.th32ProcessID == pid:
|
||||
name = entry.szExeFile.decode("mbcs").rstrip("\x00")
|
||||
try:
|
||||
result = entry.szExeFile.decode("mbcs").rstrip("\x00")
|
||||
except Exception:
|
||||
result = ""
|
||||
break
|
||||
if not self.kernel32.Process32Next(snapshot, ctypes.byref(entry)):
|
||||
break
|
||||
self.kernel32.CloseHandle(snapshot)
|
||||
return name
|
||||
return result
|
||||
|
||||
####################################################################################################
|
||||
|
||||
def whitelist_button(self):
|
||||
self.config_list("white_list")
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
if files:
|
||||
if self.send_message("您確定要增加到白名單嗎?", "quest"):
|
||||
n = self.manage_named_list("white_list", files, action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info")
|
||||
if files and self.send_message("您確定要增加到白名單嗎?", "quest"):
|
||||
n = self.manage_named_list("white_list", files, action="add", with_hash=True)
|
||||
self.send_message(f"成功增加到白名單,共 {n} 個檔案", "info")
|
||||
|
||||
def whitelist_button_2(self):
|
||||
self.config_list("white_list")
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
if files:
|
||||
if self.send_message("您確定要移除白名單嗎?", "quest"):
|
||||
n = self.manage_named_list("white_list", files, action="remove", with_hash=True)
|
||||
self.send_message(f"成功移除白名單,共 {n} 個檔案", "info")
|
||||
if files and self.send_message("您確定要移除白名單嗎?", "quest"):
|
||||
n = self.manage_named_list("white_list", files, action="remove", with_hash=True)
|
||||
self.send_message(f"成功移除白名單,共 {n} 個檔案", "info")
|
||||
|
||||
def quarantine_button(self):
|
||||
self.config_list("quarantine")
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
if files:
|
||||
if self.send_message("您確定要增加到隔離區嗎?", "quest"):
|
||||
n = self.manage_named_list("quarantine", files, action="add", with_hash=True, lock_func=self.lock_file)
|
||||
if n > 0:
|
||||
self.send_message(f"成功增加到隔離區,共 {n} 個檔案", "info")
|
||||
if files and self.send_message("您確定要增加到隔離區嗎?", "quest"):
|
||||
n = self.manage_named_list("quarantine", files, action="add", with_hash=True, lock_func=self.lock_file)
|
||||
if n > 0:
|
||||
self.send_message(f"成功增加到隔離區,共 {n} 個檔案", "info")
|
||||
|
||||
def quarantine_button_2(self):
|
||||
self.config_list("quarantine")
|
||||
quarantine_dir = os.path.join(self.path_config, "quarantine")
|
||||
files = self.send_message("選擇檔案", "files", True)
|
||||
if files:
|
||||
if self.send_message("您確定要移除隔離區嗎?", "quest"):
|
||||
n = self.manage_named_list("quarantine", files, action="remove", with_hash=True, lock_func=self.lock_file)
|
||||
self.send_message(f"成功移除隔離區,共 {n} 個檔案", "info")
|
||||
if files and self.send_message("您確定要移除隔離區嗎?", "quest"):
|
||||
n = self.manage_named_list("quarantine", files, action="remove", with_hash=True, lock_func=self.lock_file)
|
||||
self.send_message(f"成功移除隔離區,共 {n} 個檔案", "info")
|
||||
|
||||
def add_to_quarantine(self, files):
|
||||
self.config_list("quarantine")
|
||||
return self.manage_named_list("quarantine", files, action="add", with_hash=True, lock_func=self.lock_file)
|
||||
|
||||
def is_in_whitelist(self, file_path):
|
||||
norm_path = self.norm_path(file_path)
|
||||
file_hash = self.calc_file_hash(norm_path)
|
||||
return any(item["file"] == norm_path and item["hash"] == file_hash
|
||||
for item in self.pyas_config.get("white_list", []))
|
||||
p = self.norm_path(file_path)
|
||||
if not p:
|
||||
return False
|
||||
file_hash = self.calc_file_hash(p)
|
||||
return any(os.path.normcase(item.get("file","")) == os.path.normcase(p) and
|
||||
item.get("hash","") == file_hash for item in self.pyas_config.get("white_list", []))
|
||||
|
||||
def init_whitelist(self):
|
||||
self.manage_named_list("white_list", [self.file_pyas], action="add", with_hash=True)
|
||||
@@ -1295,9 +1422,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
def manage_named_list(self, list_key, files, action="add", with_hash=True, lock_func=None):
|
||||
target_list = self.config_list(list_key)
|
||||
files = files or []
|
||||
norm_paths = self.norm_path(files)
|
||||
norm_paths = self.norm_path(files, must_exist=True)
|
||||
if isinstance(norm_paths, str):
|
||||
norm_paths = [norm_paths]
|
||||
norm_paths = [norm_paths] if norm_paths else []
|
||||
|
||||
n = 0
|
||||
if action == "add":
|
||||
@@ -1438,8 +1565,9 @@ class MainWindow_Controller(QMainWindow):
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if not h:
|
||||
return
|
||||
|
||||
file_path = self.norm_path(self.get_process_file(h))
|
||||
if file_path and os.path.exists(file_path) and not self.is_in_whitelist(file_path):
|
||||
if file_path and not self.is_in_whitelist(file_path):
|
||||
self.suspend_process(h, True)
|
||||
state = self.scan_engine(file_path)
|
||||
if state:
|
||||
@@ -1562,17 +1690,17 @@ class MainWindow_Controller(QMainWindow):
|
||||
def handle_new_connection(self, key):
|
||||
pid, remote_addr, remote_port = key
|
||||
try:
|
||||
h_process = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if not h_process:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, pid)
|
||||
if not h:
|
||||
return
|
||||
file_path = self.norm_path(self.get_process_file(h_process))
|
||||
remote_ip = f"{remote_addr & 0xFF}.{(remote_addr >> 8) & 0xFF}.{(remote_addr >> 16) & 0xFF}.{(remote_addr >> 24) & 0xFF}"
|
||||
|
||||
if file_path and os.path.exists(file_path) and not self.is_in_whitelist(file_path):
|
||||
file_path = self.norm_path(self.get_process_file(h))
|
||||
if file_path and not self.is_in_whitelist(file_path):
|
||||
if hasattr(self.rule, "network") and remote_ip in self.rule.network:
|
||||
self.kernel32.TerminateProcess(h_process, 0)
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.send_message(f"網路防護 | 規則列表攔截 | {pid} | {file_path} | {remote_ip}", "notify", True)
|
||||
self.kernel32.CloseHandle(h_process)
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
@@ -1662,11 +1790,12 @@ class MainWindow_Controller(QMainWindow):
|
||||
rules = rules.replace(old, new)
|
||||
self.send_message(f"驅動防護 | {rules} | {pid} | {raw_path} | {target}", "notify", True)
|
||||
|
||||
if not self.is_in_whitelist(self.device_path_to_drive(raw_path)):
|
||||
file_path = self.norm_path(self.device_path_to_drive(raw_path))
|
||||
if not self.is_in_whitelist(file_path) and not self.sign.sign_verify(file_path):
|
||||
try:
|
||||
h = self.kernel32.OpenProcess(0x1F0FFF, False, int(pid.strip()))
|
||||
if h:
|
||||
self.kernel32.TerminateProcess(h, 0xC0000022)
|
||||
self.kernel32.TerminateProcess(h, 0)
|
||||
self.kernel32.CloseHandle(h)
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
+3
-2
@@ -71,9 +71,10 @@ translate_dict = {
|
||||
"沙箱":"Sandbox ", "虛擬":"Virtual ", "隔離":"Quarantine ", "時":"",
|
||||
"建立":"Create ","環境":"Environment ","沙箱":"Sandbox ","化":"", "表":"",
|
||||
"達":"Reached ", "上限":"Limit ", "區":"", "金鑰":"Key ", "產品":"Product ",
|
||||
"托管":"CLR ","殼碼":"Shellcode ","線程":"Thread ","注入":"Inject ",
|
||||
"運行庫":".NET CLR ","殼碼":"Shellcode ","線程":"Thread ","注入":"Inject ",
|
||||
"前往":"Go ","檢查":"Check ","可用":"Available ","靜態":"Static ",
|
||||
"版本":"Version ","是":"","剩餘":"Remain "," ":" "},
|
||||
"版本":"Version ","是":"","剩餘":"Remain ","遠控":"Remote ","監控":"Monitor ",
|
||||
" ":" "},
|
||||
|
||||
"white_switch": {
|
||||
"_white": "_black",},
|
||||
|
||||
+75
-9
@@ -1,6 +1,72 @@
|
||||
import os, yara, numpy, locale, pefile, onnxruntime
|
||||
import os, yara, numpy, onnxruntime
|
||||
import ctypes, ctypes.wintypes, pefile
|
||||
from PIL import Image
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class GUID(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("Data1", ctypes.wintypes.DWORD),
|
||||
("Data2", ctypes.wintypes.WORD),
|
||||
("Data3", ctypes.wintypes.WORD),
|
||||
("Data4", ctypes.c_ubyte * 8)]
|
||||
|
||||
class WINTRUST_FILE_INFO(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cbStruct", ctypes.wintypes.DWORD),
|
||||
("pcwszFilePath", ctypes.wintypes.LPCWSTR),
|
||||
("hFile", ctypes.wintypes.HANDLE),
|
||||
("pgKnownSubject", ctypes.wintypes.LPVOID)]
|
||||
|
||||
class WINTRUST_DATA(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cbStruct", ctypes.wintypes.DWORD),
|
||||
("pPolicyCallbackData", ctypes.wintypes.LPVOID),
|
||||
("pSIPClientData", ctypes.wintypes.LPVOID),
|
||||
("dwUIChoice", ctypes.wintypes.DWORD),
|
||||
("fdwRevocationChecks", ctypes.wintypes.DWORD),
|
||||
("dwUnionChoice", ctypes.wintypes.DWORD),
|
||||
("pFile", ctypes.POINTER(WINTRUST_FILE_INFO)),
|
||||
("dwStateAction", ctypes.wintypes.DWORD),
|
||||
("hWVTStateData", ctypes.wintypes.HANDLE),
|
||||
("pwszURLReference", ctypes.wintypes.LPCWSTR),
|
||||
("dwProvFlags", ctypes.wintypes.DWORD),
|
||||
("dwUIContext", ctypes.wintypes.DWORD),
|
||||
("pSignatureSettings", ctypes.wintypes.LPVOID)]
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class sign_scanner:
|
||||
def __init__(self):
|
||||
self.verify = GUID(0x00AAC56B, 0xCD44, 0x11D0, (0x8C, 0xC2, 0x00, 0xC0, 0x4F, 0xC2, 0x95, 0xEE))
|
||||
|
||||
def init_windll(self, path):
|
||||
for name in path:
|
||||
try:
|
||||
setattr(self, name.lower(), ctypes.WinDLL(name, use_last_error=True))
|
||||
except Exception as e:
|
||||
self.send_message(e, "warn", False)
|
||||
|
||||
self.WinVerifyTrust = self.wintrust.WinVerifyTrust
|
||||
self.WinVerifyTrust.restype = ctypes.wintypes.LONG
|
||||
self.WinVerifyTrust.argtypes = [ctypes.wintypes.HWND, ctypes.POINTER(GUID), ctypes.c_void_p]
|
||||
|
||||
def is_sign(self, file_path):
|
||||
with pefile.PE(file, fast_load=True) as pe:
|
||||
sec_dir = pe.OPTIONAL_HEADER.DATA_DIRECTORY[pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_SECURITY']]
|
||||
return sec_dir.VirtualAddress != 0 and sec_dir.Size > 0
|
||||
|
||||
def sign_verify(self, file_path):
|
||||
fi = WINTRUST_FILE_INFO(ctypes.sizeof(WINTRUST_FILE_INFO), file_path, None, None)
|
||||
data = WINTRUST_DATA(ctypes.sizeof(WINTRUST_DATA), None, None, 2, 0, 1,
|
||||
ctypes.pointer(fi), 1, None, None, 0, 0, None)
|
||||
s = self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(data))
|
||||
data.dwStateAction = 2
|
||||
self.WinVerifyTrust(None, ctypes.byref(self.verify), ctypes.byref(data))
|
||||
return s == 0
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class rule_scanner:
|
||||
def __init__(self):
|
||||
self.rules = {}
|
||||
@@ -26,9 +92,8 @@ class rule_scanner:
|
||||
|
||||
def yara_scan(self, file_path):
|
||||
try:
|
||||
data = open(file_path, "rb").read() if isinstance(file_path, str) else file_path
|
||||
for rules in self.rules.values():
|
||||
matches = rules.match(data=data)
|
||||
matches = rules.match(filepath=file_path)
|
||||
if matches:
|
||||
rule_name = str(matches[0])
|
||||
label = rule_name.split("_")[0]
|
||||
@@ -38,19 +103,18 @@ class rule_scanner:
|
||||
except Exception:
|
||||
return False, False
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class model_scanner:
|
||||
def __init__(self):
|
||||
self.models = {}
|
||||
self.suffix = {
|
||||
".com", ".dll", ".drv", ".exe", ".ocx", ".scr", ".sys",
|
||||
".bat", ".cmd", ".html", ".js", ".php", ".ps1", ".vbs", ".wsf", ".xml"
|
||||
}
|
||||
".bat", ".cmd", ".js", ".php", ".ps1", ".vbs", ".wsf"}
|
||||
self.labels = [
|
||||
"Pefile/White", "Script/White", "Pefile/General", "Script/General"
|
||||
]
|
||||
"Pefile/White", "Script/White", "Pefile/General", "Script/General"]
|
||||
self.detect = {
|
||||
"Pefile/General", "Script/General"
|
||||
}
|
||||
"Pefile/General", "Script/General"}
|
||||
self.resize = (224, 224)
|
||||
|
||||
def load_path(self, path):
|
||||
@@ -109,6 +173,7 @@ class model_scanner:
|
||||
arr = numpy.frombuffer(file_data, dtype=numpy.uint8)
|
||||
img = numpy.zeros(wah*wah*channels, dtype=numpy.uint8)
|
||||
img[:len(file_data)] = arr
|
||||
|
||||
if channels == 1:
|
||||
image = Image.fromarray(img.reshape((wah, wah)), 'L')
|
||||
else:
|
||||
@@ -136,6 +201,7 @@ class model_scanner:
|
||||
name = section.Name.rstrip(b'\x00').decode('latin1').lower()
|
||||
if section.Characteristics & 0x00000020:
|
||||
match_data[name] = section.get_data()
|
||||
|
||||
except pefile.PEFormatError:
|
||||
with open(file_path, 'rb') as f:
|
||||
file_content = f.read()
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import pyinstaller_versionfile
|
||||
|
||||
pyinstaller_versionfile.create_versionfile(
|
||||
output_file="versionfile.txt",
|
||||
version='3.3.4',
|
||||
version='3.3.5',
|
||||
company_name="PYAS Security",
|
||||
file_description="Python Antivirus Software",
|
||||
internal_name="PYAS",
|
||||
|
||||
@@ -13,6 +13,9 @@ BOOLEAN g_CmRegActive = FALSE;
|
||||
EX_RUNDOWN_REF g_Rundown;
|
||||
PDEVICE_OBJECT g_GuardDevice = NULL;
|
||||
|
||||
PDEVICE_OBJECT g_MbrFilterTargets[MAX_MBR_TARGETS] = { 0 };
|
||||
ULONG g_MbrFilterCount = 0;
|
||||
|
||||
volatile LONG g_LogWorkCount = 0;
|
||||
static UNICODE_STRING g_DeviceName;
|
||||
static WCHAR g_DeviceNameBuf[64];
|
||||
@@ -58,6 +61,7 @@ static NTSTATUS AttachDiskFilters(PDRIVER_OBJECT DriverObject)
|
||||
UNICODE_STRING name = RTL_CONSTANT_STRING(L"\\Driver\\Disk");
|
||||
UNICODE_STRING sym = RTL_CONSTANT_STRING(L"IoDriverObjectType");
|
||||
POBJECT_TYPE* pType = (POBJECT_TYPE*)MmGetSystemRoutineAddress(&sym);
|
||||
|
||||
if (!pType || !*pType)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
@@ -109,22 +113,79 @@ static NTSTATUS AttachDiskFilters(PDRIVER_OBJECT DriverObject)
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS AttachFsFilters(PDRIVER_OBJECT DriverObject)
|
||||
{
|
||||
UNICODE_STRING symDev = RTL_CONSTANT_STRING(L"IoDeviceObjectType");
|
||||
POBJECT_TYPE* pDevType = (POBJECT_TYPE*)MmGetSystemRoutineAddress(&symDev);
|
||||
if (!pDevType || !*pDevType)
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
for (ULONG i = 0; g_AttachDisk[i] && g_MbrFilterCount < MAX_MBR_TARGETS; ++i) {
|
||||
UNICODE_STRING name;
|
||||
RtlInitUnicodeString(&name, g_AttachDisk[i]);
|
||||
PDEVICE_OBJECT fsCtl = NULL;
|
||||
|
||||
status = ObReferenceObjectByName(&name, OBJ_CASE_INSENSITIVE, NULL, 0, *pDevType, KernelMode, NULL, (PVOID*)&fsCtl);
|
||||
if (!NT_SUCCESS(status) || !fsCtl)
|
||||
continue;
|
||||
|
||||
PDRIVER_OBJECT fsDrv = fsCtl->DriverObject;
|
||||
if (fsDrv) {
|
||||
ULONG need = 0;
|
||||
|
||||
status = IoEnumerateDeviceObjectList(fsDrv, NULL, 0, &need);
|
||||
if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_SUCCESS) {
|
||||
PDEVICE_OBJECT* list = (PDEVICE_OBJECT*)ExAllocatePool2(POOL_FLAG_NON_PAGED, need * sizeof(PDEVICE_OBJECT), 'fsTA');
|
||||
if (list) {
|
||||
|
||||
status = IoEnumerateDeviceObjectList(fsDrv, list, need * sizeof(PDEVICE_OBJECT), &need);
|
||||
if (NT_SUCCESS(status)) {
|
||||
for (ULONG j = 0; j < need && g_MbrFilterCount < MAX_MBR_TARGETS; ++j) {
|
||||
|
||||
PDEVICE_OBJECT lower = list[j], filter, attached = NULL;
|
||||
NTSTATUS s2 = IoCreateDevice(DriverObject, 0, NULL, lower->DeviceType, lower->Characteristics, FALSE, &filter);
|
||||
if (!NT_SUCCESS(s2))
|
||||
continue;
|
||||
|
||||
filter->Flags |= (lower->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO | DO_POWER_PAGABLE));
|
||||
s2 = IoAttachDeviceToDeviceStackSafe(filter, lower, &attached);
|
||||
if (!NT_SUCCESS(s2) || !attached) {
|
||||
IoDeleteDevice(filter);
|
||||
continue;
|
||||
}
|
||||
filter->StackSize = lower->StackSize + 1;
|
||||
filter->AlignmentRequirement = lower->AlignmentRequirement;
|
||||
filter->DeviceExtension = attached;
|
||||
filter->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
g_MbrFilterTargets[g_MbrFilterCount++] = filter;
|
||||
}
|
||||
}
|
||||
ExFreePool2(list, 'fsTA', NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
ObDereferenceObject(fsCtl);
|
||||
}
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistryPath);
|
||||
|
||||
|
||||
KeInitializeSpinLock(&g_ProtectLock);
|
||||
ExInitializeFastMutex(&HookMutex);
|
||||
KeInitializeEvent(&g_LogDrainEvent, NotificationEvent, TRUE);
|
||||
g_Unloading = FALSE;
|
||||
g_CmRegActive = FALSE;
|
||||
|
||||
|
||||
InterlockedExchange(&g_LogWorkCount, 0);
|
||||
ExInitializeRundownProtection(&g_Rundown);
|
||||
{
|
||||
UNICODE_STRING guardName = RTL_CONSTANT_STRING(L"\\Device\\PYAS_Driver_Guard");
|
||||
NTSTATUS gs = IoCreateDevice(DriverObject, 0, &guardName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_GuardDevice);
|
||||
if (!NT_SUCCESS(gs))
|
||||
if (!NT_SUCCESS(gs))
|
||||
return STATUS_DEVICE_BUSY;
|
||||
}
|
||||
{
|
||||
@@ -135,12 +196,15 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
g_DeviceName.Length = (USHORT)(wcslen(g_DeviceName.Buffer) * sizeof(WCHAR));
|
||||
}
|
||||
NTSTATUS status = IoCreateDevice(DriverObject, 0, &g_DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &g_ControlDeviceObject);
|
||||
if (!NT_SUCCESS(status))
|
||||
if (!NT_SUCCESS(status))
|
||||
return status;
|
||||
|
||||
|
||||
DriverObject->DriverUnload = DriverUnload;
|
||||
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i)
|
||||
for (ULONG i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i)
|
||||
DriverObject->MajorFunction[i] = CombinedDispatch;
|
||||
|
||||
DriverObject->Flags |= 0x20;
|
||||
|
||||
g_ControlDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
||||
IoDeleteSymbolicLink(&g_SymLink);
|
||||
{
|
||||
@@ -149,6 +213,7 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
status = IoCreateSymbolicLink(&g_SymLink, &g_DeviceName);
|
||||
if (NT_SUCCESS(status))
|
||||
break;
|
||||
|
||||
LARGE_INTEGER d = { 0 };
|
||||
d.QuadPart = -10LL * 1000LL * 10LL;
|
||||
KeDelayExecutionThread(KernelMode, FALSE, &d);
|
||||
@@ -157,10 +222,7 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
if (!NT_SUCCESS(status)) {
|
||||
IoDeleteDevice(g_ControlDeviceObject);
|
||||
g_ControlDeviceObject = NULL;
|
||||
if (g_GuardDevice) {
|
||||
IoDeleteDevice(g_GuardDevice);
|
||||
g_GuardDevice = NULL;
|
||||
}
|
||||
if (g_GuardDevice) { IoDeleteDevice(g_GuardDevice); g_GuardDevice = NULL; }
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -171,12 +233,28 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
LARGE_INTEGER t = KeQueryPerformanceCounter(NULL);
|
||||
RtlStringCchPrintfW(regAltBuf, RTL_NUMBER_OF(regAltBuf), L"385100.%I64x", t.QuadPart);
|
||||
RtlInitUnicodeString(®Alt, regAltBuf);
|
||||
|
||||
status = CmRegisterCallbackEx(RegistryProtectCallback, ®Alt, DriverObject, NULL, &g_CmRegHandle, NULL);
|
||||
if (NT_SUCCESS(status))
|
||||
if (NT_SUCCESS(status))
|
||||
g_CmRegActive = TRUE;
|
||||
}
|
||||
InitImageProtect();
|
||||
InitInjectProtect();
|
||||
InitRemoteProtect();
|
||||
InitScreenProtect();
|
||||
{
|
||||
NTSTATUS ps = InitProcessProtect();
|
||||
if (!NT_SUCCESS(ps)) {
|
||||
CHAR b[64] = { 0 };
|
||||
RtlStringCchPrintfA(b, RTL_NUMBER_OF(b), "PROC_INIT | 0x%08X", ps);
|
||||
SendPipeLog(b, strlen(b));
|
||||
}
|
||||
else {
|
||||
static CHAR ok[] = "PROC_INIT | 0x00000000";
|
||||
SendPipeLog(ok, sizeof(ok) - 1);
|
||||
}
|
||||
}
|
||||
AttachFsFilters(DriverObject);
|
||||
AttachDiskFilters(DriverObject);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
@@ -184,6 +262,7 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
|
||||
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(DriverObject);
|
||||
|
||||
g_Unloading = TRUE;
|
||||
if (g_SymLinkCreated) {
|
||||
IoDeleteSymbolicLink(&g_SymLink);
|
||||
@@ -195,7 +274,14 @@ VOID DriverUnload(PDRIVER_OBJECT DriverObject)
|
||||
}
|
||||
UninitInjectProtect();
|
||||
UninitImageProtect();
|
||||
UninitRemoteProtect();
|
||||
UninitScreenProtect();
|
||||
UninitProcessProtect();
|
||||
|
||||
ExWaitForRundownProtectionRelease(&g_Rundown);
|
||||
if (InterlockedCompareExchange((volatile LONG*)&g_LogWorkCount, 0, 0) != 0) {
|
||||
KeWaitForSingleObject(&g_LogDrainEvent, Executive, KernelMode, FALSE, NULL);
|
||||
}
|
||||
if (InterlockedCompareExchange(&g_LogWorkCount, 0, 0) != 0) {
|
||||
KeResetEvent(&g_LogDrainEvent);
|
||||
while (InterlockedCompareExchange(&g_LogWorkCount, 0, 0) != 0)
|
||||
@@ -221,4 +307,4 @@ VOID DriverUnload(PDRIVER_OBJECT DriverObject)
|
||||
IoDeleteDevice(g_GuardDevice);
|
||||
g_GuardDevice = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,17 @@
|
||||
#include <ntifs.h>
|
||||
#include <ntstrsafe.h>
|
||||
#include <ntdddisk.h>
|
||||
#include <ntddscsi.h>
|
||||
|
||||
#define PIPE_NAME L"\\??\\pipe\\PYAS_Output_Pipe"
|
||||
#define MAX_MBR_TARGETS 52
|
||||
#define MAX_MBR_TARGETS 256
|
||||
|
||||
#ifndef POOL_FLAG_NON_PAGED
|
||||
#define POOL_FLAG_NON_PAGED 0x00000001
|
||||
#endif
|
||||
#ifndef PROCESS_TERMINATE
|
||||
#define PROCESS_TERMINATE 0x0001
|
||||
#endif
|
||||
#ifndef PROCESS_CREATE_THREAD
|
||||
#define PROCESS_CREATE_THREAD 0x0002
|
||||
#endif
|
||||
@@ -42,16 +46,35 @@
|
||||
#ifndef PROCESS_SET_LIMITED_INFORMATION
|
||||
#define PROCESS_SET_LIMITED_INFORMATION 0x2000
|
||||
#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_IMPERSONATE
|
||||
#define THREAD_IMPERSONATE 0x0100
|
||||
#endif
|
||||
#ifndef THREAD_DIRECT_IMPERSONATION
|
||||
#define THREAD_DIRECT_IMPERSONATION 0x0200
|
||||
#endif
|
||||
#ifndef JOB_OBJECT_ASSIGN_PROCESS
|
||||
#define JOB_OBJECT_ASSIGN_PROCESS 0x0001
|
||||
#endif
|
||||
#ifndef JOB_OBJECT_TERMINATE
|
||||
#define JOB_OBJECT_TERMINATE 0x0008
|
||||
#endif
|
||||
#ifndef FILE_DISPOSITION_DELETE
|
||||
#define FILE_DISPOSITION_DELETE 0x00000001
|
||||
#endif
|
||||
#ifndef UINT
|
||||
typedef unsigned int UINT;
|
||||
#endif
|
||||
|
||||
extern POBJECT_TYPE* PsJobType;
|
||||
extern volatile LONG g_LogWorkCount;
|
||||
extern BOOLEAN g_Unloading;
|
||||
extern KEVENT g_LogDrainEvent;
|
||||
@@ -60,6 +83,7 @@ extern ULONG g_MbrFilterCount;
|
||||
extern KSPIN_LOCK g_ProtectLock;
|
||||
extern LARGE_INTEGER g_CmRegHandle;
|
||||
extern POBJECT_TYPE* IoDriverObjectType;
|
||||
extern POBJECT_TYPE* IoFileObjectType;
|
||||
extern FAST_MUTEX HookMutex;
|
||||
extern PDRIVER_OBJECT DiskDrvObj;
|
||||
extern PCSTR PsGetProcessImageFileName(PEPROCESS Process);
|
||||
@@ -74,14 +98,14 @@ typedef VOID(NTAPI* _pyas_ExFreePoolWithTag)(PVOID, ULONG);
|
||||
static __forceinline PVOID PYAS_ExAllocatePool2(ULONG f, SIZE_T s, ULONG t) {
|
||||
static _pyas_ExAllocatePool2 p2 = 0;
|
||||
if (!p2) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExAllocatePool2");
|
||||
p2 = (_pyas_ExAllocatePool2)MmGetSystemRoutineAddress(&n);
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExAllocatePool2");
|
||||
p2 = (_pyas_ExAllocatePool2)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p2)
|
||||
return p2(f, s, t);
|
||||
static _pyas_ExAllocatePoolWithTag p1 = 0;
|
||||
if (!p1) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExAllocatePoolWithTag");
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExAllocatePoolWithTag");
|
||||
p1 = (_pyas_ExAllocatePoolWithTag)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p1)
|
||||
@@ -92,9 +116,10 @@ static __forceinline PVOID PYAS_ExAllocatePool2(ULONG f, SIZE_T s, ULONG t) {
|
||||
static __forceinline VOID PYAS_ExFreePool2(PVOID a, ULONG t, PVOID h, ULONG f) {
|
||||
UNREFERENCED_PARAMETER(h);
|
||||
UNREFERENCED_PARAMETER(f);
|
||||
|
||||
static _pyas_ExFreePool2 p2 = 0;
|
||||
if (!p2) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExFreePool2");
|
||||
if (!p2) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExFreePool2");
|
||||
p2 = (_pyas_ExFreePool2)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p2) {
|
||||
@@ -102,13 +127,13 @@ static __forceinline VOID PYAS_ExFreePool2(PVOID a, ULONG t, PVOID h, ULONG f) {
|
||||
return;
|
||||
}
|
||||
static _pyas_ExFreePoolWithTag p1 = 0;
|
||||
if (!p1) {
|
||||
if (!p1) {
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"ExFreePoolWithTag");
|
||||
p1 = (_pyas_ExFreePoolWithTag)MmGetSystemRoutineAddress(&n);
|
||||
}
|
||||
if (p1) {
|
||||
if (p1) {
|
||||
p1(a, t);
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +144,9 @@ VOID DriverUnload(PDRIVER_OBJECT DriverObject);
|
||||
VOID SendPipeLog(PCSTR msg, SIZE_T len);
|
||||
VOID UninitImageProtect(VOID);
|
||||
VOID UninitInjectProtect(VOID);
|
||||
VOID UninitRemoteProtect(VOID);
|
||||
VOID UninitScreenProtect(VOID);
|
||||
VOID UninitProcessProtect(VOID);
|
||||
|
||||
NTSYSAPI NTSTATUS NTAPI ZwQueryInformationProcess(
|
||||
_In_ HANDLE ProcessHandle,
|
||||
@@ -143,5 +171,8 @@ NTSTATUS FileProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp);
|
||||
NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2);
|
||||
NTSTATUS InitImageProtect(VOID);
|
||||
NTSTATUS InitInjectProtect(VOID);
|
||||
NTSTATUS InitRemoteProtect(VOID);
|
||||
NTSTATUS InitScreenProtect(VOID);
|
||||
NTSTATUS InitProcessProtect(VOID);
|
||||
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath);
|
||||
@@ -17,19 +17,20 @@ static VOID PipeLogWork(PDEVICE_OBJECT DeviceObject, PVOID Context)
|
||||
UNICODE_STRING name;
|
||||
OBJECT_ATTRIBUTES oa = { 0 };
|
||||
IO_STATUS_BLOCK iosb;
|
||||
|
||||
|
||||
RtlInitUnicodeString(&name, PIPE_NAME);
|
||||
InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
|
||||
if (NT_SUCCESS(ZwCreateFile(&h, FILE_GENERIC_WRITE, &oa, &iosb, NULL, 0, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0)))
|
||||
{
|
||||
if (NT_SUCCESS(ZwCreateFile(&h, FILE_GENERIC_WRITE, &oa, &iosb, NULL, 0, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0))) {
|
||||
ZwWriteFile(h, NULL, NULL, NULL, &iosb, ctx->Buf, (ULONG)(ctx->Len > 0xFFFFFFFF ? 0xFFFFFFFF : ctx->Len), NULL, NULL);
|
||||
ZwClose(h);
|
||||
}
|
||||
IoFreeWorkItem(ctx->Item);
|
||||
ExFreePool2(ctx->Buf, 'golP', NULL, 0);
|
||||
ExFreePool2(ctx, 'golP', NULL, 0);
|
||||
if (InterlockedDecrement(&g_LogWorkCount) == 0 && g_Unloading)
|
||||
|
||||
if (InterlockedDecrement(&g_LogWorkCount) == 0) {
|
||||
KeSetEvent(&g_LogDrainEvent, IO_NO_INCREMENT, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
VOID SendPipeLog(PCSTR msg, SIZE_T len)
|
||||
@@ -40,11 +41,9 @@ VOID SendPipeLog(PCSTR msg, SIZE_T len)
|
||||
return;
|
||||
if (g_Unloading)
|
||||
return;
|
||||
|
||||
PPIPE_LOG_CTX ctx = (PPIPE_LOG_CTX)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(PIPE_LOG_CTX), 'golP');
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
ctx->Len = len;
|
||||
ctx->Buf = (PCHAR)ExAllocatePool2(POOL_FLAG_NON_PAGED, ctx->Len, 'golP');
|
||||
if (!ctx->Buf) {
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectRules.h"
|
||||
|
||||
PDEVICE_OBJECT g_MbrFilterTargets[MAX_MBR_TARGETS] = { 0 };
|
||||
ULONG g_MbrFilterCount = 0;
|
||||
|
||||
NTSTATUS FileProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
{
|
||||
PDEVICE_OBJECT lower = (PDEVICE_OBJECT)DeviceObject->DeviceExtension;
|
||||
if (!lower) {
|
||||
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
if (!ExAcquireRundownProtection(&g_Rundown)) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
return IoCallDriver(lower, Irp);
|
||||
}
|
||||
if (g_Unloading) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS st = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return st;
|
||||
}
|
||||
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (stack->MajorFunction == IRP_MJ_POWER) {
|
||||
PoStartNextPowerIrp(Irp);
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS st = PoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return st;
|
||||
}
|
||||
HANDLE pid = PsGetCurrentProcessId();
|
||||
ULONG currentPid = (ULONG)(ULONG_PTR)pid;
|
||||
if (currentPid == 0 || currentPid == 4) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS st = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return st;
|
||||
}
|
||||
BOOLEAN blocked = FALSE, is_white = FALSE;
|
||||
CHAR logbuf[1024] = { 0 };
|
||||
|
||||
WCHAR exeBuf[512] = { 0 };
|
||||
UNICODE_STRING exeName = { 0 };
|
||||
exeName.Buffer = exeBuf;
|
||||
exeName.MaximumLength = sizeof(exeBuf);
|
||||
exeName.Length = 0;
|
||||
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
if (GetProcessImagePathByPid(pid, &exeName))
|
||||
is_white = IsWhitelist(&exeName);
|
||||
else {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(exeBuf) - 1; i++)
|
||||
exeBuf[i] = (WCHAR)n[i];
|
||||
exeBuf[l] = 0;
|
||||
exeName.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
}
|
||||
if (!is_white) {
|
||||
UCHAR mj = stack->MajorFunction;
|
||||
BOOLEAN isDisk = (DeviceObject->DeviceType == FILE_DEVICE_DISK);
|
||||
|
||||
if (!isDisk) {
|
||||
PDEVICE_OBJECT tdo = IoGetAttachedDeviceReference(lower);
|
||||
if (tdo) {
|
||||
isDisk = (tdo->DeviceType == FILE_DEVICE_DISK || tdo->DeviceType == FILE_DEVICE_DISK_FILE_SYSTEM);
|
||||
ObDereferenceObject(tdo);
|
||||
}
|
||||
}
|
||||
if (isDisk) {
|
||||
if (mj == IRP_MJ_WRITE || mj == IRP_MJ_SET_INFORMATION || mj == IRP_MJ_DEVICE_CONTROL || mj == IRP_MJ_INTERNAL_DEVICE_CONTROL) blocked = TRUE;
|
||||
}
|
||||
}
|
||||
if (blocked) {
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
CHAR target[64] = "None";
|
||||
ULONG ret = 0;
|
||||
POBJECT_NAME_INFORMATION oni = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, 1024, 'nOmD');
|
||||
|
||||
if (oni && NT_SUCCESS(ObQueryNameString(lower, oni, 1024, &ret)) && oni->Name.Buffer && oni->Name.Length >= sizeof(WCHAR) * 2) {
|
||||
USHORT len = oni->Name.Length / sizeof(WCHAR);
|
||||
ULONG dr = 0;
|
||||
BOOLEAN hasDr = FALSE;
|
||||
|
||||
for (USHORT i = 0; i + 2 < len; i++) {
|
||||
if (oni->Name.Buffer[i] == L'D' && oni->Name.Buffer[i + 1] == L'R') {
|
||||
USHORT j = i + 2;
|
||||
ULONG x = 0;
|
||||
|
||||
while (j < len && oni->Name.Buffer[j] >= L'0' && oni->Name.Buffer[j] <= L'9') {
|
||||
x = x * 10 + (oni->Name.Buffer[j] - L'0');
|
||||
j++;
|
||||
}
|
||||
dr = x;
|
||||
hasDr = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasDr)
|
||||
RtlStringCchPrintfA(target, sizeof(target), "Disk\\Device\\DR%u", dr);
|
||||
}
|
||||
if (oni)
|
||||
ExFreePool2(oni, 'nOmD', NULL, 0);
|
||||
RtlStringCchPrintfA(logbuf, sizeof(logbuf), "BOOT_BLOCK | %u | %wZ | %s", currentPid, &exeName, target);
|
||||
SendPipeLog(logbuf, strlen(logbuf));
|
||||
}
|
||||
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS st = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return st;
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN GetFilePathFromFileObject(PFILE_OBJECT fo, PUNICODE_STRING out)
|
||||
{
|
||||
if (!fo || !out || !out->Buffer || out->MaximumLength == 0)
|
||||
return FALSE;
|
||||
|
||||
out->Length = 0;
|
||||
if (fo->DeviceObject && fo->FileName.Buffer && fo->FileName.Length) {
|
||||
ULONG need = 0;
|
||||
NTSTATUS s = ObQueryNameString(fo->DeviceObject, NULL, 0, &need);
|
||||
if (s == STATUS_INFO_LENGTH_MISMATCH && need > 0) {
|
||||
POBJECT_NAME_INFORMATION oni = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, need, 'nOmD');
|
||||
if (oni) {
|
||||
if (NT_SUCCESS(ObQueryNameString(fo->DeviceObject, oni, need, &need)) && oni->Name.Buffer && oni->Name.Length) {
|
||||
ULONG total = oni->Name.Length + fo->FileName.Length;
|
||||
if (total + sizeof(WCHAR) <= out->MaximumLength) {
|
||||
RtlCopyMemory(out->Buffer, oni->Name.Buffer, oni->Name.Length);
|
||||
RtlCopyMemory((PUCHAR)out->Buffer + oni->Name.Length, fo->FileName.Buffer, fo->FileName.Length);
|
||||
out->Length = (USHORT)total;
|
||||
out->Buffer[total / sizeof(WCHAR)] = 0;
|
||||
ExFreePool2(oni, 'nOmD', NULL, 0);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
ExFreePool2(oni, 'nOmD', NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
HANDLE hFile = NULL;
|
||||
OBJECT_ATTRIBUTES oa = { 0 };
|
||||
IO_STATUS_BLOCK iosb;
|
||||
|
||||
InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
|
||||
if (!NT_SUCCESS(ObOpenObjectByPointer(fo, OBJ_KERNEL_HANDLE, NULL, FILE_READ_ATTRIBUTES, *IoFileObjectType, KernelMode, &hFile)))
|
||||
return FALSE;
|
||||
|
||||
ULONG buflen = sizeof(FILE_NAME_INFORMATION) + 2048 * sizeof(WCHAR);
|
||||
PFILE_NAME_INFORMATION pfi = (PFILE_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, buflen, 'nIIF');
|
||||
if (!pfi) {
|
||||
ZwClose(hFile);
|
||||
return FALSE;
|
||||
}
|
||||
RtlZeroMemory(pfi, buflen);
|
||||
NTSTATUS st = ZwQueryInformationFile(hFile, &iosb, pfi, buflen, FileNormalizedNameInformation);
|
||||
|
||||
if (NT_SUCCESS(st) && pfi->FileNameLength > 0 && pfi->FileNameLength + sizeof(WCHAR) <= out->MaximumLength) {
|
||||
RtlCopyMemory(out->Buffer, pfi->FileName, pfi->FileNameLength);
|
||||
out->Length = (USHORT)pfi->FileNameLength;
|
||||
out->Buffer[out->Length / sizeof(WCHAR)] = 0;
|
||||
ExFreePool2(pfi, 'nIIF', NULL, 0);
|
||||
ZwClose(hFile);
|
||||
return TRUE;
|
||||
}
|
||||
ExFreePool2(pfi, 'nIIF', NULL, 0);
|
||||
ZwClose(hFile);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN WantDelete(PIRP Irp, PIO_STACK_LOCATION s)
|
||||
{
|
||||
if (s->MajorFunction != IRP_MJ_SET_INFORMATION)
|
||||
return FALSE;
|
||||
|
||||
FILE_INFORMATION_CLASS c = s->Parameters.SetFile.FileInformationClass;
|
||||
if (c == FileDispositionInformation) {
|
||||
PFILE_DISPOSITION_INFORMATION di = (PFILE_DISPOSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
||||
return di && di->DeleteFile;
|
||||
}
|
||||
if (c == FileDispositionInformationEx) {
|
||||
PFILE_DISPOSITION_INFORMATION_EX dx = (PFILE_DISPOSITION_INFORMATION_EX)Irp->AssociatedIrp.SystemBuffer;
|
||||
return dx && (dx->Flags & FILE_DISPOSITION_DELETE);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN WantRename(PIRP Irp, PIO_STACK_LOCATION s)
|
||||
{
|
||||
if (s->MajorFunction != IRP_MJ_SET_INFORMATION)
|
||||
return FALSE;
|
||||
|
||||
FILE_INFORMATION_CLASS c = s->Parameters.SetFile.FileInformationClass;
|
||||
if (c == FileRenameInformation || c == FileRenameInformationEx)
|
||||
return Irp->AssociatedIrp.SystemBuffer != NULL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static VOID GetExeNameForLog(HANDLE pid, PUNICODE_STRING exe)
|
||||
{
|
||||
if (!exe || !exe->Buffer || exe->MaximumLength == 0)
|
||||
return;
|
||||
|
||||
exe->Length = 0;
|
||||
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
|
||||
if (GetProcessImagePathByPid(pid, exe))
|
||||
return;
|
||||
}
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
|
||||
for (size_t i = 0; i < l && i < (exe->MaximumLength / sizeof(WCHAR)) - 1; i++)
|
||||
exe->Buffer[i] = (WCHAR)n[i];
|
||||
exe->Buffer[l] = 0;
|
||||
exe->Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
static BOOLEAN QueryDevName(PDEVICE_OBJECT dev, PUNICODE_STRING out)
|
||||
{
|
||||
if (!dev || !out || !out->Buffer || out->MaximumLength == 0)
|
||||
return FALSE;
|
||||
|
||||
ULONG need = 0;
|
||||
NTSTATUS s = ObQueryNameString(dev, NULL, 0, &need);
|
||||
if (s != STATUS_INFO_LENGTH_MISMATCH || need == 0)
|
||||
return FALSE;
|
||||
|
||||
POBJECT_NAME_INFORMATION oni = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, need, 'nOmD');
|
||||
if (!oni)
|
||||
return FALSE;
|
||||
|
||||
BOOLEAN ok = FALSE;
|
||||
s = ObQueryNameString(dev, oni, need, &need);
|
||||
if (NT_SUCCESS(s) && oni->Name.Buffer && oni->Name.Length && oni->Name.Length < out->MaximumLength) {
|
||||
RtlCopyMemory(out->Buffer, oni->Name.Buffer, oni->Name.Length);
|
||||
out->Length = (USHORT)oni->Name.Length;
|
||||
out->Buffer[out->Length / sizeof(WCHAR)] = 0;
|
||||
ok = TRUE;
|
||||
}
|
||||
ExFreePool2(oni, 'nOmD', NULL, 0);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static BOOLEAN IsDASDRoot(PFILE_OBJECT fo)
|
||||
{
|
||||
if (!fo)
|
||||
return TRUE;
|
||||
if (fo->FileName.Length == 0 && fo->RelatedFileObject == NULL)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsDangerousDiskIo(PIO_STACK_LOCATION s)
|
||||
{
|
||||
if (!s)
|
||||
return FALSE;
|
||||
if (s->MajorFunction == IRP_MJ_DEVICE_CONTROL || s->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) {
|
||||
ULONG code = s->Parameters.DeviceIoControl.IoControlCode;
|
||||
if (code == IOCTL_SCSI_PASS_THROUGH || code == IOCTL_SCSI_PASS_THROUGH_DIRECT)
|
||||
return TRUE;
|
||||
if (code == IOCTL_DISK_SET_DRIVE_LAYOUT || code == IOCTL_DISK_SET_DRIVE_LAYOUT_EX)
|
||||
return TRUE;
|
||||
if (code == IOCTL_DISK_DELETE_DRIVE_LAYOUT)
|
||||
return TRUE;
|
||||
if (code == IOCTL_DISK_FORMAT_TRACKS || code == IOCTL_DISK_FORMAT_TRACKS_EX)
|
||||
return TRUE;
|
||||
if (code == IOCTL_DISK_SET_PARTITION_INFO || code == IOCTL_DISK_SET_PARTITION_INFO_EX)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
if (s->MajorFunction == IRP_MJ_SCSI)
|
||||
return TRUE;
|
||||
if (s->MajorFunction == IRP_MJ_WRITE || s->MajorFunction == IRP_MJ_FLUSH_BUFFERS)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
NTSTATUS FileProtectDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
|
||||
{
|
||||
PDEVICE_OBJECT lower = (PDEVICE_OBJECT)DeviceObject->DeviceExtension;
|
||||
if (!lower) {
|
||||
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
return STATUS_INVALID_DEVICE_REQUEST;
|
||||
}
|
||||
if (!ExAcquireRundownProtection(&g_Rundown)) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
return IoCallDriver(lower, Irp);
|
||||
}
|
||||
if (g_Unloading) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS rs = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return rs;
|
||||
}
|
||||
PIO_STACK_LOCATION s = IoGetCurrentIrpStackLocation(Irp);
|
||||
if (s->MajorFunction == IRP_MJ_POWER) {
|
||||
PoStartNextPowerIrp(Irp);
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS ps = PoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return ps;
|
||||
}
|
||||
HANDLE pid = PsGetCurrentProcessId();
|
||||
ULONG upid = (ULONG)(ULONG_PTR)pid;
|
||||
if (upid == 0 || upid == 4) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS ns = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return ns;
|
||||
}
|
||||
BOOLEAN blocked = FALSE;
|
||||
BOOLEAN isWrite = (s->MajorFunction == IRP_MJ_WRITE);
|
||||
BOOLEAN isDeleteReq = WantDelete(Irp, s);
|
||||
BOOLEAN isRenameReq = WantRename(Irp, s);
|
||||
BOOLEAN canQuery = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
|
||||
WCHAR eb[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = eb;
|
||||
exe.MaximumLength = sizeof(eb);
|
||||
|
||||
if (canQuery) {
|
||||
GetExeNameForLog(pid, &exe);
|
||||
if (IsWhitelist(&exe)) {
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS ns2 = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return ns2;
|
||||
}
|
||||
}
|
||||
if (lower->DeviceType == FILE_DEVICE_DISK) {
|
||||
if (IsDangerousDiskIo(s)) {
|
||||
BOOLEAN hit = FALSE;
|
||||
WCHAR db[512] = { 0 };
|
||||
UNICODE_STRING dev = { 0 };
|
||||
dev.Buffer = db;
|
||||
dev.MaximumLength = sizeof(db);
|
||||
|
||||
if (canQuery && QueryDevName(lower, &dev)) {
|
||||
if (MatchBlockFile(&dev))
|
||||
hit = TRUE;
|
||||
}
|
||||
else {
|
||||
hit = TRUE;
|
||||
}
|
||||
if (!hit && s->FileObject && canQuery) {
|
||||
WCHAR nb[512] = { 0 };
|
||||
UNICODE_STRING n2 = { 0 };
|
||||
n2.Buffer = nb;
|
||||
n2.MaximumLength = sizeof(nb);
|
||||
|
||||
if (QueryDevName(s->FileObject->DeviceObject, &n2)) {
|
||||
if (MatchBlockFile(&n2))
|
||||
hit = TRUE;
|
||||
}
|
||||
}
|
||||
if (!hit && s->MajorFunction == IRP_MJ_WRITE && s->FileObject && IsDASDRoot(s->FileObject))
|
||||
hit = TRUE;
|
||||
if (hit) {
|
||||
blocked = TRUE;
|
||||
if (canQuery) {
|
||||
if (dev.Length == 0)
|
||||
QueryDevName(lower, &dev);
|
||||
LogAnsi3("BOOT_BLOCK", upid, &exe, dev.Length ? &dev : &exe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!blocked && (isWrite || isDeleteReq || isRenameReq) && s->FileObject && canQuery) {
|
||||
WCHAR pb[1024] = { 0 };
|
||||
UNICODE_STRING path = { 0 };
|
||||
path.Buffer = pb;
|
||||
path.MaximumLength = sizeof(pb);
|
||||
|
||||
if (GetFilePathFromFileObject(s->FileObject, &path)) {
|
||||
BOOLEAN hitFile = MatchBlockFile(&path);
|
||||
BOOLEAN hitRansom = MatchBlockRansom(&path) && HasBlockedSuffix(&path);
|
||||
if (hitFile || hitRansom) {
|
||||
blocked = TRUE;
|
||||
LogAnsi3(hitFile && !hitRansom ? "FILE_BLOCK" : "RANSOM_BLOCK", upid, &exe, &path);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ULONG need = 0;
|
||||
POBJECT_NAME_INFORMATION dev = NULL;
|
||||
NTSTATUS qs2 = ObQueryNameString(s->FileObject->DeviceObject, NULL, 0, &need);
|
||||
|
||||
if (qs2 == STATUS_INFO_LENGTH_MISMATCH && need > 0) {
|
||||
dev = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, need, 'dOnF');
|
||||
if (dev && NT_SUCCESS(ObQueryNameString(s->FileObject->DeviceObject, dev, need, &need)) && dev->Name.Buffer && dev->Name.Length) {
|
||||
if (MatchBlockFile(&dev->Name)) {
|
||||
blocked = TRUE;
|
||||
UNICODE_STRING rule = { 0 };
|
||||
rule.Buffer = GetMatchedBlockFileRule(&dev->Name);
|
||||
rule.Length = rule.Buffer ? (USHORT)(wcslen(rule.Buffer) * sizeof(WCHAR)) : 0;
|
||||
rule.MaximumLength = rule.Length;
|
||||
LogAnsi3("FILE_BLOCK", upid, &exe, rule.Buffer ? &rule : &dev->Name);
|
||||
}
|
||||
}
|
||||
if (dev)
|
||||
ExFreePool2(dev, 'dOnF', NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (blocked) {
|
||||
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
|
||||
Irp->IoStatus.Information = 0;
|
||||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
IoSkipCurrentIrpStackLocation(Irp);
|
||||
NTSTATUS rs2 = IoCallDriver(lower, Irp);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return rs2;
|
||||
}
|
||||
@@ -1,39 +1,15 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectRules.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN g_ImageNotifyEnabled = FALSE;
|
||||
|
||||
static BOOLEAN StrStrI(const wchar_t* s, const wchar_t* sub) {
|
||||
if (!s || !sub)
|
||||
return FALSE;
|
||||
|
||||
size_t n = wcslen(s), m = wcslen(sub);
|
||||
if (m == 0 || n < m)
|
||||
return FALSE;
|
||||
|
||||
for (size_t i = 0; i + m <= n; i++) {
|
||||
size_t j = 0;
|
||||
for (; j < m; j++) {
|
||||
WCHAR a = s[i + j], b = sub[j];
|
||||
if (a >= L'A' && a <= L'Z')
|
||||
a += 32;
|
||||
if (b >= L'A' && b <= L'Z')
|
||||
b += 32;
|
||||
if (a != b)
|
||||
break;
|
||||
}
|
||||
if (j == m)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN CheckStackVAD(PVOID a) {
|
||||
static BOOLEAN CheckVadExec(PVOID a)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi = { 0 };
|
||||
SIZE_T ret = 0;
|
||||
NTSTATUS s = ZwQueryVirtualMemory(NtCurrentProcess(), a, MemoryBasicInformation, &mbi, sizeof(mbi), &ret);
|
||||
|
||||
|
||||
if (!NT_SUCCESS(s))
|
||||
return FALSE;
|
||||
if (mbi.State != MEM_COMMIT)
|
||||
@@ -45,7 +21,8 @@ static BOOLEAN CheckStackVAD(PVOID a) {
|
||||
return (mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0;
|
||||
}
|
||||
|
||||
static BOOLEAN CheckShellcodeWalkStack(ULONG h) {
|
||||
static BOOLEAN WalkForShellcode(ULONG h)
|
||||
{
|
||||
#define MAXWALK 20
|
||||
PVOID frames[MAXWALK] = { 0 };
|
||||
ULONG c = RtlWalkFrameChain(frames, MAXWALK, 1);
|
||||
@@ -58,99 +35,100 @@ static BOOLEAN CheckShellcodeWalkStack(ULONG h) {
|
||||
|
||||
for (ULONG i = 0; i < d; i++) {
|
||||
PVOID p = frames[i];
|
||||
if (p && CheckStackVAD(p))
|
||||
if (p && CheckVadExec(p))
|
||||
return TRUE;
|
||||
}
|
||||
for (ULONG i = 0; i < d; i++) {
|
||||
ULONG idx = count - 1 - i;
|
||||
if (idx >= d) {
|
||||
PVOID p = frames[idx];
|
||||
if (p && CheckStackVAD(p))
|
||||
if (p && CheckVadExec(p))
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN IsFrameworkClrPath(const wchar_t* s) {
|
||||
if (!s)
|
||||
return FALSE;
|
||||
if (!StrStrI(s, L"\\Windows\\Microsoft.NET\\"))
|
||||
return FALSE;
|
||||
if (!StrStrI(s, L"\\Framework\\") && !StrStrI(s, L"\\Framework64\\"))
|
||||
return FALSE;
|
||||
if (StrStrI(s, L"\\clr.dll") || StrStrI(s, L"\\mscorwks.dll"))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
static VOID ImageLoadNotify(IN PUNICODE_STRING FullImageName, IN HANDLE ProcessId, IN PIMAGE_INFO ImageInfo)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ImageInfo);
|
||||
if (g_Unloading)
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (ProcessId == (HANDLE)0 || ProcessId == (HANDLE)4)
|
||||
if (ProcessId == (HANDLE)0 || ProcessId == (HANDLE)4)
|
||||
return;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
|
||||
WCHAR exeBuf[512] = { 0 };
|
||||
UNICODE_STRING exeName = { 0 };
|
||||
exeName.Buffer = exeBuf;
|
||||
exeName.MaximumLength = sizeof(exeBuf);
|
||||
exeName.Length = 0;
|
||||
|
||||
if (!GetProcessImagePathByPid(ProcessId, &exeName)) {
|
||||
|
||||
WCHAR eb[512] = { 0 };
|
||||
UNICODE_STRING exe = { 0 };
|
||||
exe.Buffer = eb;
|
||||
exe.MaximumLength = sizeof(eb);
|
||||
exe.Length = 0;
|
||||
|
||||
if (!GetProcessImagePathByPid(ProcessId, &exe)) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(exeBuf) - 1; i++)
|
||||
exeBuf[i] = (WCHAR)n[i];
|
||||
exeBuf[l] = 0;
|
||||
exeName.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(eb) - 1; i++)
|
||||
eb[i] = (WCHAR)n[i];
|
||||
eb[l] = 0;
|
||||
exe.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
if (exeName.Length & 1) exeName.Length--;
|
||||
if (exe.Length & 1) exe.Length--;
|
||||
|
||||
{
|
||||
size_t elen = exeName.Length / sizeof(WCHAR);
|
||||
if ((elen >= 14 && _wcsnicmp(exeName.Buffer + elen - 14, L"powershell.exe", 14) == 0) ||
|
||||
(elen >= 8 && _wcsnicmp(exeName.Buffer + elen - 8, L"pwsh.exe", 8) == 0)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (IsWhitelist(&exeName)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
CHAR logbuf[1024] = { 0 };
|
||||
if (FullImageName && FullImageName->Buffer) {
|
||||
if ((StrStrI(FullImageName->Buffer, L"clr.dll") || StrStrI(FullImageName->Buffer, L"mscorwks.dll")) && !IsFrameworkClrPath(FullImageName->Buffer)) {
|
||||
RtlStringCchPrintfA(logbuf, sizeof(logbuf), "CLR_BLOCK | %u | %wZ | %wZ", (ULONG)(ULONG_PTR)ProcessId, &exeName, FullImageName);
|
||||
SendPipeLog(logbuf, strlen(logbuf));
|
||||
size_t elen = exe.Length / sizeof(WCHAR);
|
||||
if ((elen >= 14 && _wcsnicmp(exe.Buffer + elen - 14, L"powershell.exe", 14) == 0) ||
|
||||
(elen >= 8 && _wcsnicmp(exe.Buffer + elen - 8, L"pwsh.exe", 8) == 0)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (CheckShellcodeWalkStack(8)) {
|
||||
RtlStringCchPrintfA(logbuf, sizeof(logbuf), "SHELLCODE_BLOCK | %u | %wZ | None", (ULONG)(ULONG_PTR)ProcessId, &exeName);
|
||||
SendPipeLog(logbuf, strlen(logbuf));
|
||||
if (IsWhitelist(&exe)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
if (FullImageName && FullImageName->Buffer) {
|
||||
if (MatchClrFromNonFramework(FullImageName)) {
|
||||
LogAnsi3("CLR_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &exe, FullImageName);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (WalkForShellcode(8)) {
|
||||
UNICODE_STRING none = RTL_CONSTANT_STRING(L"None");
|
||||
LogAnsi3("SHELLCODE_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &exe, &none);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitImageProtect(VOID) {
|
||||
NTSTATUS InitImageProtect(VOID)
|
||||
{
|
||||
NTSTATUS s = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
if (NT_SUCCESS(s))
|
||||
g_ImageNotifyEnabled = TRUE;
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitImageProtect(VOID) {
|
||||
VOID UninitImageProtect(VOID)
|
||||
{
|
||||
if (g_ImageNotifyEnabled) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ImageNotifyEnabled = FALSE;
|
||||
|
||||
@@ -1,32 +1,45 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectRules.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static PVOID g_ObRegHandle = NULL;
|
||||
static POB_OPERATION_REGISTRATION g_ObOps = NULL;
|
||||
static OB_CALLBACK_REGISTRATION g_ObReg = { 0 };
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS PreProcessCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
if (g_Unloading)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!Info || Info->KernelHandle)
|
||||
if (!Info || Info->KernelHandle)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!Info->Object)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (Info->Operation != OB_OPERATION_HANDLE_CREATE && Info->Operation != OB_OPERATION_HANDLE_DUPLICATE)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return OB_PREOP_SUCCESS;
|
||||
|
||||
|
||||
HANDLE cur = PsGetCurrentProcessId();
|
||||
if (cur == (HANDLE)0 || cur == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
HANDLE target = PsGetProcessId((PEPROCESS)Info->Object);
|
||||
if (target == cur) {
|
||||
if (target == cur) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
@@ -35,9 +48,9 @@ static OB_PREOP_CALLBACK_STATUS PreProcessCallback(PVOID RegistrationContext, PO
|
||||
PCSTR tn = PsGetProcessImageFileName((PEPROCESS)Info->Object);
|
||||
if (tn && (_stricmp(tn, "lsass.exe") == 0))
|
||||
mask |= PROCESS_VM_READ;
|
||||
|
||||
|
||||
if ((*desired & mask) == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
BOOLEAN logok = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
@@ -45,31 +58,27 @@ static OB_PREOP_CALLBACK_STATUS PreProcessCallback(PVOID RegistrationContext, PO
|
||||
UNICODE_STRING cexe = { 0 }, texe = { 0 };
|
||||
cexe.Buffer = cbuf; cexe.MaximumLength = sizeof(cbuf);
|
||||
texe.Buffer = tbuf; texe.MaximumLength = sizeof(tbuf);
|
||||
|
||||
if (logok)
|
||||
|
||||
if (logok)
|
||||
GetProcessImagePathByPid(cur, &cexe);
|
||||
if (logok && IsWhitelist(&cexe)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (logok && IsWhitelist(&cexe)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK old = *desired;
|
||||
*desired &= ~mask;
|
||||
if ((*desired) != old && logok) {
|
||||
GetProcessImagePathByPid(target, &texe);
|
||||
|
||||
if (cexe.Length == 0) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(cbuf) - 1; i++)
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(cbuf) - 1; i++)
|
||||
cbuf[i] = (WCHAR)n[i];
|
||||
cbuf[l] = 0;
|
||||
cexe.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
CHAR logbuf[1024] = { 0 };
|
||||
RtlStringCchPrintfA(logbuf, sizeof(logbuf), "INJECT_BLOCK | %u | %wZ | %wZ", (ULONG)(ULONG_PTR)cur, &cexe, &texe);
|
||||
SendPipeLog(logbuf, strlen(logbuf));
|
||||
LogAnsi3("INJECT_BLOCK", (ULONG)(ULONG_PTR)cur, &cexe, &texe);
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
@@ -78,29 +87,29 @@ static OB_PREOP_CALLBACK_STATUS PreProcessCallback(PVOID RegistrationContext, PO
|
||||
static OB_PREOP_CALLBACK_STATUS PreThreadCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
if (!Info || Info->KernelHandle)
|
||||
if (!Info || Info->KernelHandle)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (Info->Object == NULL)
|
||||
if (Info->Object == NULL)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (Info->Operation != OB_OPERATION_HANDLE_CREATE && Info->Operation != OB_OPERATION_HANDLE_DUPLICATE)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return OB_PREOP_SUCCESS;
|
||||
|
||||
|
||||
HANDLE cur = PsGetCurrentProcessId();
|
||||
if (cur == (HANDLE)0 || cur == (HANDLE)4) {
|
||||
if (cur == (HANDLE)0 || cur == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
HANDLE owner = PsGetThreadProcessId((PETHREAD)Info->Object);
|
||||
if (owner == cur) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (owner == cur) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK tmask = THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
|
||||
ACCESS_MASK* desired = (Info->Operation == OB_OPERATION_HANDLE_CREATE) ? &Info->Parameters->CreateHandleInformation.DesiredAccess : &Info->Parameters->DuplicateHandleInformation.DesiredAccess;
|
||||
if ((*desired & tmask) == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
if ((*desired & tmask) == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
BOOLEAN logok = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
@@ -108,47 +117,41 @@ static OB_PREOP_CALLBACK_STATUS PreThreadCallback(PVOID RegistrationContext, POB
|
||||
UNICODE_STRING cexe = { 0 };
|
||||
cexe.Buffer = cbuf;
|
||||
cexe.MaximumLength = sizeof(cbuf);
|
||||
|
||||
if (logok)
|
||||
|
||||
if (logok)
|
||||
GetProcessImagePathByPid(cur, &cexe);
|
||||
if (logok && IsWhitelist(&cexe)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
ACCESS_MASK old = *desired;
|
||||
*desired &= ~tmask;
|
||||
if ((*desired) != old && logok) {
|
||||
CHAR logbuf[1024] = { 0 };
|
||||
|
||||
if (cexe.Length == 0) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(cbuf) - 1; i++)
|
||||
cbuf[i] = (WCHAR)n[i];
|
||||
cbuf[l] = 0;
|
||||
cexe.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
RtlStringCchPrintfA(logbuf, sizeof(logbuf), "THREAD_BLOCK | %u | %wZ | %u", (ULONG)(ULONG_PTR)cur, &cexe, (ULONG)(ULONG_PTR)owner);
|
||||
SendPipeLog(logbuf, strlen(logbuf));
|
||||
UNICODE_STRING tnone = RTL_CONSTANT_STRING(L"None");
|
||||
LogAnsi3("THREAD_BLOCK", (ULONG)(ULONG_PTR)cur, &cexe, &tnone);
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
NTSTATUS InitInjectProtect(VOID)
|
||||
NTSTATUS InitInjectProtect(VOID)
|
||||
{
|
||||
static WCHAR obAltBuf[32];
|
||||
static UNICODE_STRING obAlt;
|
||||
LARGE_INTEGER t = KeQueryPerformanceCounter(NULL);
|
||||
RtlStringCchPrintfW(obAltBuf, RTL_NUMBER_OF(obAltBuf), L"385000.%I64x", t.QuadPart);
|
||||
RtlInitUnicodeString(&obAlt, obAltBuf);
|
||||
|
||||
RtlInitUnicodeString(&obAlt, L"385000");
|
||||
|
||||
g_ObOps = (POB_OPERATION_REGISTRATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, sizeof(OB_OPERATION_REGISTRATION) * 2, 'bOpr');
|
||||
if (!g_ObOps)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
|
||||
|
||||
RtlZeroMemory(g_ObOps, sizeof(OB_OPERATION_REGISTRATION) * 2);
|
||||
g_ObOps[0].ObjectType = PsProcessType;
|
||||
g_ObOps[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
@@ -156,14 +159,14 @@ NTSTATUS InitInjectProtect(VOID)
|
||||
g_ObOps[1].ObjectType = PsThreadType;
|
||||
g_ObOps[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
g_ObOps[1].PreOperation = PreThreadCallback;
|
||||
|
||||
|
||||
RtlZeroMemory(&g_ObReg, sizeof(g_ObReg));
|
||||
g_ObReg.Version = OB_FLT_REGISTRATION_VERSION;
|
||||
g_ObReg.OperationRegistrationCount = 2;
|
||||
g_ObReg.Altitude = obAlt;
|
||||
g_ObReg.RegistrationContext = NULL;
|
||||
g_ObReg.OperationRegistration = g_ObOps;
|
||||
|
||||
|
||||
NTSTATUS s = ObRegisterCallbacks(&g_ObReg, &g_ObRegHandle);
|
||||
if (!NT_SUCCESS(s)) {
|
||||
ExFreePool2(g_ObOps, 'bOpr', NULL, 0);
|
||||
@@ -175,7 +178,7 @@ NTSTATUS InitInjectProtect(VOID)
|
||||
|
||||
VOID UninitInjectProtect(VOID)
|
||||
{
|
||||
if (g_ObRegHandle) {
|
||||
if (g_ObRegHandle) {
|
||||
ObUnRegisterCallbacks(g_ObRegHandle);
|
||||
g_ObRegHandle = NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectRules.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN WildMatchN(const wchar_t* str, SIZE_T n, const wchar_t* pat)
|
||||
{
|
||||
if (*pat == 0)
|
||||
return n == 0;
|
||||
|
||||
if (*pat == L'*') {
|
||||
if (pat[1] == L'*') {
|
||||
for (SIZE_T i = 0; i <= n; ++i)
|
||||
if (WildMatchN(str + i, n - i, pat + 2))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
SIZE_T i = 0;
|
||||
for (; i < n && str[i] != L'\\'; ++i)
|
||||
if (WildMatchN(str + i, n - i, pat + 1))
|
||||
return TRUE;
|
||||
return WildMatchN(str + i, n - i, pat + 1);
|
||||
}
|
||||
if (*pat == L'?')
|
||||
return n > 0 && str[0] != L'\\' && WildMatchN(str + 1, n - 1, pat + 1);
|
||||
if (n == 0)
|
||||
return FALSE;
|
||||
|
||||
WCHAR sc = str[0], pc = *pat;
|
||||
if (sc >= L'A' && sc <= L'Z')
|
||||
sc += 32;
|
||||
if (pc >= L'A' && pc <= L'Z')
|
||||
pc += 32;
|
||||
return sc == pc && WildMatchN(str + 1, n - 1, pat + 1);
|
||||
}
|
||||
|
||||
static BOOLEAN MatchList(PUNICODE_STRING s, wchar_t** list)
|
||||
{
|
||||
if (!s || !s->Buffer || s->Length == 0)
|
||||
return FALSE;
|
||||
|
||||
size_t s_len = s->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; list[i]; ++i) {
|
||||
wchar_t* pat = list[i];
|
||||
size_t pat_len = wcslen(pat);
|
||||
if (wcschr(pat, L'*') || wcschr(pat, L'?')) {
|
||||
if (WildMatchN(s->Buffer, s_len, pat))
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
if (s_len == pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return TRUE;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer + s_len - pat_len, pat, pat_len) == 0)
|
||||
return TRUE;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static wchar_t* MatchListGetRule(PUNICODE_STRING s, wchar_t** list)
|
||||
{
|
||||
if (!s || !s->Buffer || s->Length == 0)
|
||||
return 0;
|
||||
|
||||
size_t s_len = s->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; list[i]; ++i) {
|
||||
wchar_t* pat = list[i];
|
||||
size_t pat_len = wcslen(pat);
|
||||
if (wcschr(pat, L'*') || wcschr(pat, L'?')) {
|
||||
if (WildMatchN(s->Buffer, s_len, pat))
|
||||
return pat;
|
||||
}
|
||||
else {
|
||||
if (s_len == pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return pat;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer + s_len - pat_len, pat, pat_len) == 0)
|
||||
return pat;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return pat;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __forceinline BOOLEAN USOK(PUNICODE_STRING s)
|
||||
{
|
||||
return s && s->Buffer && s->Length;
|
||||
}
|
||||
|
||||
static BOOLEAN StrStrIW(const wchar_t* s, const wchar_t* sub)
|
||||
{
|
||||
if (!s || !sub)
|
||||
return FALSE;
|
||||
|
||||
SIZE_T n = wcslen(s), m = wcslen(sub);
|
||||
if (m == 0 || n < m)
|
||||
return FALSE;
|
||||
|
||||
for (SIZE_T i = 0; i + m <= n; i++) {
|
||||
SIZE_T j = 0;
|
||||
for (; j < m; j++) {
|
||||
WCHAR a = s[i + j], b = sub[j];
|
||||
if (a >= L'A' && a <= L'Z') a += 32;
|
||||
if (b >= L'A' && b <= L'Z') b += 32;
|
||||
if (a != b) break;
|
||||
}
|
||||
if (j == m)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static BOOLEAN EndsWithNameUS(PUNICODE_STRING s, const wchar_t* name)
|
||||
{
|
||||
if (!USOK(s) || !name)
|
||||
return FALSE;
|
||||
SIZE_T n = s->Length / sizeof(WCHAR);
|
||||
SIZE_T m = wcslen(name);
|
||||
if (n < m)
|
||||
return FALSE;
|
||||
return _wcsnicmp(s->Buffer + (n - m), name, m) == 0;
|
||||
}
|
||||
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe)
|
||||
{
|
||||
if (USOK(key)) {
|
||||
size_t klen0 = key->Length / sizeof(WCHAR);
|
||||
if (WildMatchN(key->Buffer, klen0, L"\\REGISTRY\\MACHINE\\SYSTEM\\ControlSet001\\Services\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if (USOK(exe)) {
|
||||
if (IsWhitelist(exe))
|
||||
return FALSE;
|
||||
}
|
||||
if (USOK(exe) && USOK(key)) {
|
||||
size_t elen = exe->Length / sizeof(WCHAR);
|
||||
size_t klen = key->Length / sizeof(WCHAR);
|
||||
|
||||
if (((elen >= 12 && _wcsnicmp(exe->Buffer + elen - 12, L"services.exe", 12) == 0) ||
|
||||
(elen >= 11 && _wcsnicmp(exe->Buffer + elen - 11, L"svchost.exe", 11) == 0) ||
|
||||
(elen >= 6 && _wcsnicmp(exe->Buffer + elen - 6, L"sc.exe", 6) == 0))) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if ((elen >= 14 && _wcsnicmp(exe->Buffer + elen - 14, L"powershell.exe", 14) == 0) ||
|
||||
(elen >= 8 && _wcsnicmp(exe->Buffer + elen - 8, L"pwsh.exe", 8) == 0)) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap\\**") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Notifications\\Data\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if ((elen >= 11 && _wcsnicmp(exe->Buffer + elen - 11, L"msiexec.exe", 11) == 0) ||
|
||||
(elen >= 20 && _wcsnicmp(exe->Buffer + elen - 20, L"TrustedInstaller.exe", 20) == 0)) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Installer\\**") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Uninstall\\**") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if (elen >= 12 && _wcsnicmp(exe->Buffer + elen - 12, L"schtasks.exe", 12) == 0) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (USOK(key)) {
|
||||
if (MatchBlockReg(key))
|
||||
return TRUE;
|
||||
}
|
||||
if (USOK(valueName)) {
|
||||
SIZE_T n = valueName->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; i < (SIZE_T)-1 && g_BlockReg[i]; ++i) {
|
||||
wchar_t* pat = g_BlockReg[i];
|
||||
if (wcschr(pat, L'\\') || wcschr(pat, L'*') || wcschr(pat, L'?'))
|
||||
continue;
|
||||
size_t m = wcslen(pat);
|
||||
if (n == m && _wcsnicmp(valueName->Buffer, pat, m) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath)
|
||||
{
|
||||
PEPROCESS process = NULL;
|
||||
NTSTATUS status = PsLookupProcessByProcessId(pid, &process);
|
||||
if (!NT_SUCCESS(status))
|
||||
return FALSE;
|
||||
|
||||
HANDLE h = NULL;
|
||||
status = ObOpenObjectByPointer(process, OBJ_KERNEL_HANDLE, NULL, PROCESS_QUERY_LIMITED_INFORMATION, *PsProcessType, KernelMode, &h);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
ULONG len = 0;
|
||||
status = ZwQueryInformationProcess(h, ProcessImageFileName, NULL, 0, &len);
|
||||
if (status != STATUS_INFO_LENGTH_MISMATCH || len == 0) {
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
PVOID buf = ExAllocatePool2(POOL_FLAG_NON_PAGED, len, 'iPgN');
|
||||
if (!buf) {
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
status = ZwQueryInformationProcess(h, ProcessImageFileName, buf, len, &len);
|
||||
if (NT_SUCCESS(status)) {
|
||||
PUNICODE_STRING image = (PUNICODE_STRING)buf;
|
||||
if (image->Buffer && image->Length > 0 && ProcessImagePath->MaximumLength >= image->Length + sizeof(WCHAR)) {
|
||||
RtlCopyMemory(ProcessImagePath->Buffer, image->Buffer, image->Length);
|
||||
ProcessImagePath->Length = image->Length;
|
||||
ProcessImagePath->Buffer[image->Length / sizeof(WCHAR)] = 0;
|
||||
ExFreePool2(buf, 'iPgN', NULL, 0);
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
ExFreePool2(buf, 'iPgN', NULL, 0);
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN IsWhitelist(PUNICODE_STRING s)
|
||||
{
|
||||
if (MatchList(s, g_WhitelistExcept))
|
||||
return FALSE;
|
||||
return MatchList(s, g_Whitelist);
|
||||
}
|
||||
|
||||
BOOLEAN MatchBlockReg(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchList(s, g_BlockReg);
|
||||
}
|
||||
|
||||
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchListGetRule(s, g_BlockReg);
|
||||
}
|
||||
|
||||
BOOLEAN MatchBlockFile(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchList(s, g_BlockFile);
|
||||
}
|
||||
|
||||
BOOLEAN MatchBlockRansom(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchList(s, g_BlockRansom);
|
||||
}
|
||||
|
||||
wchar_t* GetMatchedBlockFileRule(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchListGetRule(s, g_BlockFile);
|
||||
}
|
||||
|
||||
BOOLEAN HasBlockedSuffix(PUNICODE_STRING s)
|
||||
{
|
||||
if (!USOK(s))
|
||||
return FALSE;
|
||||
SIZE_T n = s->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; g_Blocksuffix[i]; ++i) {
|
||||
wchar_t* ext = g_Blocksuffix[i];
|
||||
SIZE_T m = wcslen(ext);
|
||||
if (n >= m && _wcsnicmp(s->Buffer + (n - m), ext, m) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchRemoteSuspectBin(PUNICODE_STRING img)
|
||||
{
|
||||
for (SIZE_T i = 0; g_RemoteSuspectBins[i]; ++i)
|
||||
if (EndsWithNameUS(img, g_RemoteSuspectBins[i]))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img)
|
||||
{
|
||||
if (!USOK(cmd))
|
||||
return FALSE;
|
||||
|
||||
const wchar_t* s = cmd->Buffer;
|
||||
for (SIZE_T i = 0; g_RemoteCmdIndicatorsHttp[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdIndicatorsHttp[i]))
|
||||
return TRUE;
|
||||
|
||||
for (SIZE_T i = 0; g_RemoteCmdIndicatorsGeneric[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdIndicatorsGeneric[i]))
|
||||
return TRUE;
|
||||
|
||||
if (USOK(img) && EndsWithNameUS(img, L"cmd.exe")) {
|
||||
for (SIZE_T i = 0; g_RemoteCmdFromCmdExe[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdFromCmdExe[i]))
|
||||
return TRUE;
|
||||
}
|
||||
if (USOK(img) && EndsWithNameUS(img, L"rundll32.exe")) {
|
||||
for (SIZE_T i = 0; g_RemoteCmdFromRundll32Exe[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdFromRundll32Exe[i]))
|
||||
return TRUE;
|
||||
}
|
||||
if (USOK(img) && EndsWithNameUS(img, L"regsvr32.exe")) {
|
||||
BOOLEAN need = FALSE, http = FALSE;
|
||||
for (SIZE_T i = 0; g_RemoteCmdRegsvr32Need[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdRegsvr32Need[i])) {
|
||||
need = TRUE;
|
||||
break;
|
||||
}
|
||||
for (SIZE_T i = 0; g_RemoteCmdIndicatorsHttp[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdIndicatorsHttp[i])) {
|
||||
http = TRUE;
|
||||
break;
|
||||
}
|
||||
if (need && http)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (USOK(img) && EndsWithNameUS(img, L"mshta.exe")) {
|
||||
for (SIZE_T i = 0; g_RemoteCmdFromMshtaExe[i]; ++i)
|
||||
if (StrStrIW(s, g_RemoteCmdFromMshtaExe[i]))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchScreenCapModule(PUNICODE_STRING mod)
|
||||
{
|
||||
for (SIZE_T i = 0; g_ScreenCapModules[i]; ++i)
|
||||
if (EndsWithNameUS(mod, g_ScreenCapModules[i]))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchSuspiciousProcPath(PUNICODE_STRING img)
|
||||
{
|
||||
if (!USOK(img))
|
||||
return FALSE;
|
||||
|
||||
const wchar_t* s = img->Buffer;
|
||||
if (StrStrIW(s, L"\\Users\\")) {
|
||||
for (SIZE_T i = 0; g_ScreenUserSubdirs[i]; ++i)
|
||||
if (StrStrIW(s, g_ScreenUserSubdirs[i]))
|
||||
return TRUE;
|
||||
}
|
||||
for (SIZE_T i = 0; g_ScreenOtherProcNeedles[i]; ++i)
|
||||
if (StrStrIW(s, g_ScreenOtherProcNeedles[i]))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOLEAN MatchClrFromNonFramework(PUNICODE_STRING fullImageName)
|
||||
{
|
||||
if (!USOK(fullImageName))
|
||||
return FALSE;
|
||||
|
||||
const wchar_t* s = fullImageName->Buffer;
|
||||
BOOLEAN isClr = StrStrIW(s, L"\\clr.dll") || StrStrIW(s, L"\\mscorwks.dll");
|
||||
|
||||
if (!isClr)
|
||||
return FALSE;
|
||||
if (!StrStrIW(s, L"\\Windows\\Microsoft.NET\\"))
|
||||
return TRUE;
|
||||
if (!StrStrIW(s, L"\\Framework\\") && !StrStrIW(s, L"\\Framework64\\"))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <ntifs.h>
|
||||
|
||||
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s);
|
||||
wchar_t* GetMatchedBlockFileRule(PUNICODE_STRING s);
|
||||
|
||||
BOOLEAN IsWhitelist(PUNICODE_STRING s);
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath);
|
||||
|
||||
BOOLEAN MatchBlockReg(PUNICODE_STRING s);
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe);
|
||||
|
||||
BOOLEAN MatchBlockFile(PUNICODE_STRING s);
|
||||
BOOLEAN MatchBlockRansom(PUNICODE_STRING s);
|
||||
BOOLEAN HasBlockedSuffix(PUNICODE_STRING s);
|
||||
|
||||
BOOLEAN MatchRemoteSuspectBin(PUNICODE_STRING img);
|
||||
BOOLEAN MatchRemoteCommand(PUNICODE_STRING cmd, PUNICODE_STRING img);
|
||||
|
||||
BOOLEAN MatchScreenCapModule(PUNICODE_STRING mod);
|
||||
BOOLEAN MatchSuspiciousProcPath(PUNICODE_STRING img);
|
||||
BOOLEAN MatchClrFromNonFramework(PUNICODE_STRING fullImageName);
|
||||
@@ -0,0 +1,224 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
|
||||
typedef union _PS_PROTECTION {
|
||||
UCHAR Level;
|
||||
struct {
|
||||
UCHAR Type : 3;
|
||||
UCHAR Audit : 1;
|
||||
UCHAR Signer : 4;
|
||||
}Flags;
|
||||
}PS_PROTECTION, * PPS_PROTECTION;
|
||||
|
||||
typedef struct _OBCTX {
|
||||
POBJECT_TYPE ProcessType;
|
||||
POBJECT_TYPE ThreadType;
|
||||
}OBCTX, * POBCTX;
|
||||
|
||||
static PVOID g_PsSetProcessProtection = NULL;
|
||||
static BOOLEAN g_ProcNotifyEnabled = FALSE;
|
||||
static PVOID g_ObCookie = NULL;
|
||||
static OBCTX g_ObCtx = { 0 };
|
||||
static HANDLE g_ProtectedPid = NULL;
|
||||
static PWCH g_ObAltitudeBuf = NULL;
|
||||
|
||||
static BOOLEAN IsProcNameEq(PEPROCESS p, PCSTR name)
|
||||
{
|
||||
PCSTR img = PsGetProcessImageFileName(p);
|
||||
if (!img || !name)
|
||||
return FALSE;
|
||||
|
||||
ANSI_STRING a = { 0 }, b = { 0 };
|
||||
RtlInitAnsiString(&a, img);
|
||||
RtlInitAnsiString(&b, name);
|
||||
return RtlEqualString(&a, &b, TRUE) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
static NTSTATUS SetPPL31(PEPROCESS p)
|
||||
{
|
||||
if (!g_PsSetProcessProtection)
|
||||
return STATUS_PROCEDURE_NOT_FOUND;
|
||||
|
||||
PS_PROTECTION pr = { 0 };
|
||||
pr.Level = 0x31;
|
||||
((VOID(NTAPI*)(PEPROCESS, PS_PROTECTION))g_PsSetProcessProtection)(p, pr);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static VOID LogSet(HANDLE pid, NTSTATUS st)
|
||||
{
|
||||
CHAR b[128] = { 0 };
|
||||
RtlStringCchPrintfA(b, RTL_NUMBER_OF(b), "PROC_PPL_SET | %u | 0x31 | 0x%08X", (ULONG)(ULONG_PTR)pid, st);
|
||||
SendPipeLog(b, strlen(b));
|
||||
}
|
||||
|
||||
static VOID UpdateProtectedPid(HANDLE pid)
|
||||
{
|
||||
InterlockedExchangePointer((PVOID*)&g_ProtectedPid, pid);
|
||||
}
|
||||
|
||||
static VOID BootstrapExisting(VOID)
|
||||
{
|
||||
typedef PEPROCESS(NTAPI* PFN_PsGetNextProcess)(PEPROCESS);
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"PsGetNextProcess");
|
||||
PFN_PsGetNextProcess fp = (PFN_PsGetNextProcess)MmGetSystemRoutineAddress(&n);
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
PEPROCESS p = fp(NULL);
|
||||
while (p) {
|
||||
if (IsProcNameEq(p, "PYAS.exe")) {
|
||||
HANDLE pid = PsGetProcessId(p);
|
||||
NTSTATUS s = SetPPL31(p);
|
||||
UpdateProtectedPid(pid);
|
||||
LogSet(pid, s);
|
||||
}
|
||||
PEPROCESS nx = fp(p);
|
||||
ObDereferenceObject(p);
|
||||
p = nx;
|
||||
}
|
||||
}
|
||||
|
||||
static VOID StripProcessAccess(POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
ACCESS_MASK d = PROCESS_TERMINATE | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_SET_INFORMATION | PROCESS_SET_LIMITED_INFORMATION | PROCESS_SUSPEND_RESUME | PROCESS_DUP_HANDLE;
|
||||
if (Info->Operation == OB_OPERATION_HANDLE_CREATE) {
|
||||
Info->Parameters->CreateHandleInformation.DesiredAccess &= ~d;
|
||||
}
|
||||
else {
|
||||
Info->Parameters->DuplicateHandleInformation.DesiredAccess &= ~d;
|
||||
}
|
||||
}
|
||||
|
||||
static VOID StripThreadAccess(POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
ACCESS_MASK d = THREAD_TERMINATE | THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_IMPERSONATE | THREAD_DIRECT_IMPERSONATION;
|
||||
if (Info->Operation == OB_OPERATION_HANDLE_CREATE) {
|
||||
Info->Parameters->CreateHandleInformation.DesiredAccess &= ~d;
|
||||
}
|
||||
else {
|
||||
Info->Parameters->DuplicateHandleInformation.DesiredAccess &= ~d;
|
||||
}
|
||||
}
|
||||
|
||||
static OB_PREOP_CALLBACK_STATUS ObPreOperation(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(RegistrationContext);
|
||||
|
||||
if (g_Unloading)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!Info || Info->KernelHandle)
|
||||
return OB_PREOP_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return OB_PREOP_SUCCESS;
|
||||
|
||||
if (Info->ObjectType == g_ObCtx.ProcessType) {
|
||||
PEPROCESS pe = (PEPROCESS)Info->Object;
|
||||
HANDLE tpid = PsGetProcessId(pe);
|
||||
if (g_ProtectedPid && tpid == g_ProtectedPid) {
|
||||
if (PsGetCurrentProcessId() != g_ProtectedPid) StripProcessAccess(Info);
|
||||
}
|
||||
}
|
||||
else if (Info->ObjectType == g_ObCtx.ThreadType) {
|
||||
PETHREAD th = (PETHREAD)Info->Object;
|
||||
HANDLE owner = PsGetThreadProcessId(th);
|
||||
if (g_ProtectedPid && owner == g_ProtectedPid) {
|
||||
if (PsGetCurrentProcessId() != g_ProtectedPid) StripThreadAccess(Info);
|
||||
}
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return OB_PREOP_SUCCESS;
|
||||
}
|
||||
|
||||
static NTSTATUS InitObCallbacks(VOID)
|
||||
{
|
||||
g_ObCtx.ProcessType = *PsProcessType;
|
||||
g_ObCtx.ThreadType = *PsThreadType;
|
||||
|
||||
static OB_OPERATION_REGISTRATION ops[2];
|
||||
RtlZeroMemory(ops, sizeof(ops));
|
||||
ops[0].ObjectType = PsProcessType;
|
||||
ops[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
ops[0].PreOperation = ObPreOperation;
|
||||
ops[1].ObjectType = PsThreadType;
|
||||
ops[1].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
|
||||
ops[1].PreOperation = ObPreOperation;
|
||||
|
||||
const WCHAR* altStatic = L"321000";
|
||||
USHORT alen = (USHORT)(wcslen(altStatic) * sizeof(WCHAR));
|
||||
g_ObAltitudeBuf = (PWCH)ExAllocatePool2(POOL_FLAG_NON_PAGED, alen + sizeof(WCHAR), 'tlaO');
|
||||
if (!g_ObAltitudeBuf)
|
||||
return STATUS_INSUFFICIENT_RESOURCES;
|
||||
RtlCopyMemory(g_ObAltitudeBuf, altStatic, alen);
|
||||
g_ObAltitudeBuf[alen / sizeof(WCHAR)] = 0;
|
||||
|
||||
UNICODE_STRING alt = { 0 };
|
||||
alt.Buffer = g_ObAltitudeBuf;
|
||||
alt.Length = alen;
|
||||
alt.MaximumLength = alen;
|
||||
|
||||
OB_CALLBACK_REGISTRATION reg = { 0 };
|
||||
reg.Version = OB_FLT_REGISTRATION_VERSION;
|
||||
reg.OperationRegistrationCount = 2;
|
||||
reg.RegistrationContext = g_ObAltitudeBuf;
|
||||
reg.Altitude = alt;
|
||||
reg.OperationRegistration = ops;
|
||||
|
||||
NTSTATUS s = ObRegisterCallbacks(®, &g_ObCookie);
|
||||
if (!NT_SUCCESS(s)) {
|
||||
ExFreePool2(g_ObAltitudeBuf, 'tlaO', NULL, 0);
|
||||
g_ObAltitudeBuf = NULL;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
static VOID ProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
|
||||
{
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
if (CreateInfo) {
|
||||
if (IsProcNameEq(Process, "PYAS.exe")) {
|
||||
NTSTATUS s = SetPPL31(Process);
|
||||
UpdateProtectedPid(ProcessId);
|
||||
LogSet(ProcessId, s);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (g_ProtectedPid && ProcessId == g_ProtectedPid) UpdateProtectedPid(NULL);
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitProcessProtect(VOID)
|
||||
{
|
||||
UNICODE_STRING n = RTL_CONSTANT_STRING(L"PsSetProcessProtection");
|
||||
g_PsSetProcessProtection = MmGetSystemRoutineAddress(&n);
|
||||
|
||||
NTSTATUS s = InitObCallbacks();
|
||||
if (!NT_SUCCESS(s))
|
||||
return s;
|
||||
|
||||
s = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, FALSE);
|
||||
if (NT_SUCCESS(s))
|
||||
g_ProcNotifyEnabled = TRUE;
|
||||
BootstrapExisting();
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
VOID UninitProcessProtect(VOID)
|
||||
{
|
||||
if (g_ProcNotifyEnabled) {
|
||||
PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, TRUE);
|
||||
g_ProcNotifyEnabled = FALSE;
|
||||
}
|
||||
if (g_ObCookie) {
|
||||
ObUnRegisterCallbacks(g_ObCookie);
|
||||
g_ObCookie = NULL;
|
||||
}
|
||||
if (g_ObAltitudeBuf) {
|
||||
ExFreePool2(g_ObAltitudeBuf, 'tlaO', NULL, 0);
|
||||
g_ObAltitudeBuf = NULL;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <ntifs.h>
|
||||
#include "ProtectRules.h"
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN QueryRegPathFromObject(PVOID KeyObject, PUNICODE_STRING OutPath, POBJECT_NAME_INFORMATION* OutInfo)
|
||||
{
|
||||
@@ -8,11 +8,11 @@ static BOOLEAN QueryRegPathFromObject(PVOID KeyObject, PUNICODE_STRING OutPath,
|
||||
NTSTATUS s = ObQueryNameString(KeyObject, NULL, 0, &len);
|
||||
if (s != STATUS_INFO_LENGTH_MISMATCH)
|
||||
return FALSE;
|
||||
|
||||
|
||||
POBJECT_NAME_INFORMATION p = (POBJECT_NAME_INFORMATION)ExAllocatePool2(POOL_FLAG_NON_PAGED, len, 'rgeR');
|
||||
if (!p)
|
||||
return FALSE;
|
||||
|
||||
|
||||
s = ObQueryNameString(KeyObject, p, len, &len);
|
||||
if (!NT_SUCCESS(s) || !p->Name.Buffer || p->Name.Length == 0) {
|
||||
ExFreePool2(p, 'rgeR', NULL, 0);
|
||||
@@ -25,39 +25,51 @@ static BOOLEAN QueryRegPathFromObject(PVOID KeyObject, PUNICODE_STRING OutPath,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ctx);
|
||||
if (g_Unloading)
|
||||
if (g_Unloading)
|
||||
return STATUS_SUCCESS;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return STATUS_SUCCESS;
|
||||
|
||||
|
||||
REG_NOTIFY_CLASS type = (REG_NOTIFY_CLASS)(ULONG_PTR)arg1;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
UNICODE_STRING checkReg = { 0 };
|
||||
POBJECT_NAME_INFORMATION nameInfo = NULL;
|
||||
|
||||
|
||||
UNICODE_STRING exe = { 0 };
|
||||
WCHAR exeBuf[260] = { 0 };
|
||||
exe.Buffer = exeBuf;
|
||||
exe.MaximumLength = sizeof(exeBuf);
|
||||
exe.Length = 0;
|
||||
|
||||
|
||||
HANDLE pid = PsGetCurrentProcessId();
|
||||
if (pid == (HANDLE)0 || pid == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return STATUS_SUCCESS;
|
||||
if (pid == (HANDLE)0 || pid == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
PUNICODE_STRING valueName = NULL;
|
||||
BOOLEAN need_log = FALSE;
|
||||
CHAR logbuf[1024] = { 0 };
|
||||
BOOLEAN canQuery = (KeGetCurrentIrql() == PASSIVE_LEVEL);
|
||||
|
||||
|
||||
if (type == RegNtPreSetValueKey) {
|
||||
PREG_SET_VALUE_KEY_INFORMATION s = (PREG_SET_VALUE_KEY_INFORMATION)arg2;
|
||||
valueName = s->ValueName;
|
||||
if (canQuery)
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(s->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreDeleteValueKey) {
|
||||
@@ -68,7 +80,7 @@ NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2)
|
||||
}
|
||||
else if (type == RegNtPreDeleteKey) {
|
||||
PREG_DELETE_KEY_INFORMATION d = (PREG_DELETE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(d->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreRenameKey) {
|
||||
@@ -78,12 +90,12 @@ NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2)
|
||||
}
|
||||
else if (type == RegNtPreCreateKeyEx) {
|
||||
PREG_CREATE_KEY_INFORMATION c = (PREG_CREATE_KEY_INFORMATION)arg2;
|
||||
if (c->CompleteName)
|
||||
if (c->CompleteName)
|
||||
checkReg = *c->CompleteName;
|
||||
}
|
||||
else if (type == RegNtPreReplaceKey) {
|
||||
PREG_REPLACE_KEY_INFORMATION rk = (PREG_REPLACE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(rk->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreSaveKey) {
|
||||
@@ -93,7 +105,7 @@ NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2)
|
||||
}
|
||||
else if (type == RegNtPreRestoreKey) {
|
||||
PREG_RESTORE_KEY_INFORMATION res = (PREG_RESTORE_KEY_INFORMATION)arg2;
|
||||
if (canQuery)
|
||||
if (canQuery)
|
||||
QueryRegPathFromObject(res->Object, &checkReg, &nameInfo);
|
||||
}
|
||||
else if (type == RegNtPreLoadKey) {
|
||||
@@ -101,11 +113,11 @@ NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2)
|
||||
if (lk->KeyName)
|
||||
checkReg = *lk->KeyName;
|
||||
}
|
||||
|
||||
if (checkReg.Buffer || (valueName && valueName->Buffer)) {
|
||||
GetProcessImagePathByPid(pid, &exe);
|
||||
|
||||
if (canQuery)
|
||||
GetProcessImagePathByPid(pid, &exe);
|
||||
if (IsRegistryBlock(&checkReg, valueName, &exe)) {
|
||||
RtlStringCchPrintfA(logbuf, sizeof(logbuf), "REG_BLOCK | %u | %wZ | %wZ", (ULONG)(ULONG_PTR)pid, &exe, &checkReg);
|
||||
need_log = TRUE;
|
||||
status = STATUS_ACCESS_DENIED;
|
||||
}
|
||||
@@ -113,7 +125,7 @@ NTSTATUS RegistryProtectCallback(PVOID ctx, PVOID arg1, PVOID arg2)
|
||||
if (nameInfo)
|
||||
ExFreePool2(nameInfo, 'rgeR', NULL, 0);
|
||||
if (need_log)
|
||||
SendPipeLog(logbuf, strlen(logbuf));
|
||||
LogAnsi3("REG_BLOCK", (ULONG)(ULONG_PTR)pid, &exe, &checkReg);
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN g_RemoteNotifyEnabled = FALSE;
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
static VOID ProcessNotifyEx(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(Process);
|
||||
UNREFERENCED_PARAMETER(ProcessId);
|
||||
|
||||
if (!CreateInfo)
|
||||
return;
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
|
||||
HANDLE ppid = CreateInfo->CreatingThreadId.UniqueProcess;
|
||||
if (ppid == (HANDLE)0 || ppid == (HANDLE)4) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
UNICODE_STRING parent = { 0 };
|
||||
WCHAR pbuf[512] = { 0 };
|
||||
parent.Buffer = pbuf;
|
||||
parent.MaximumLength = sizeof(pbuf);
|
||||
|
||||
if (!GetProcessImagePathByPid(ppid, &parent)) {
|
||||
PEPROCESS eproc = PsGetCurrentProcess();
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
size_t l = n ? strlen(n) : 0;
|
||||
for (size_t i = 0; i < l && i < RTL_NUMBER_OF(pbuf) - 1; i++)
|
||||
pbuf[i] = (WCHAR)n[i];
|
||||
pbuf[l] = 0;
|
||||
parent.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
}
|
||||
if (IsWhitelist(&parent)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
UNICODE_STRING img = { 0 };
|
||||
if (CreateInfo->ImageFileName)
|
||||
img = *CreateInfo->ImageFileName;
|
||||
UNICODE_STRING cmd = { 0 };
|
||||
if (CreateInfo->CommandLine)
|
||||
cmd = *CreateInfo->CommandLine;
|
||||
|
||||
BOOLEAN suspect = FALSE;
|
||||
if (MatchRemoteSuspectBin(&img) && MatchRemoteCommand(&cmd, &img))
|
||||
suspect = TRUE;
|
||||
|
||||
if (suspect) {
|
||||
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
|
||||
if (cmd.Buffer && cmd.Length)
|
||||
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &cmd);
|
||||
else if (img.Buffer && img.Length)
|
||||
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &img);
|
||||
else {
|
||||
UNICODE_STRING none = RTL_CONSTANT_STRING(L"None");
|
||||
LogAnsi3("REMOTE_BLOCK", (ULONG)(ULONG_PTR)ppid, &parent, &none);
|
||||
}
|
||||
}
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitRemoteProtect(VOID)
|
||||
{
|
||||
NTSTATUS s = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, FALSE);
|
||||
if (NT_SUCCESS(s))
|
||||
g_RemoteNotifyEnabled = TRUE;
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitRemoteProtect(VOID)
|
||||
{
|
||||
if (g_RemoteNotifyEnabled) {
|
||||
PsSetCreateProcessNotifyRoutineEx(ProcessNotifyEx, TRUE);
|
||||
g_RemoteNotifyEnabled = FALSE;
|
||||
}
|
||||
}
|
||||
+96
-192
@@ -1,6 +1,5 @@
|
||||
#include <ntifs.h>
|
||||
#include "ProtectRules.h"
|
||||
#include "DriverEntry.h"
|
||||
|
||||
wchar_t* g_Whitelist[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\**",
|
||||
@@ -47,7 +46,7 @@ wchar_t* g_AttachDisk[] = {
|
||||
};
|
||||
|
||||
wchar_t* g_BlockFile[] = {
|
||||
L"\\DosDevices\\**",
|
||||
L"\\Device\\Harddisk*\\DR*",
|
||||
L"\\Device\\Harddisk*\\Partition\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Boot\\**",
|
||||
L"\\Device\\HarddiskVolume*\\EFI\\**",
|
||||
@@ -55,8 +54,9 @@ wchar_t* g_BlockFile[] = {
|
||||
L"\\Device\\HarddiskVolume*\\Recovery\\**",
|
||||
L"\\Device\\HarddiskVolume*\\System Volume Information\\**",
|
||||
L"\\Device\\HarddiskVolume*\\Windows\\**",
|
||||
L"\\Device\\HarddiskVolume*\\ProgramData\\PYAS\\**",
|
||||
L"\\Device\\HarddiskVolume*\\$*\\**",
|
||||
L"\\Device\\HarddiskVolume*\\ProgramData\\PYAS\\**",
|
||||
L"**\\PYAS.exe",
|
||||
|
||||
L"**\\CON**",
|
||||
L"**\\PRN**",
|
||||
@@ -79,14 +79,27 @@ wchar_t* g_BlockRansom[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_Blocksuffix[] = {
|
||||
L".exe", L".dll", L".sys", L".com", L".scr",
|
||||
L".zip", L".7z", L".rar", L".tar", L".gz",
|
||||
L".js", L".bat", L".cmd", L".ps1", L".vbs",
|
||||
L".ppt", L".pptx", L".wps", L".txt", L".rtf",
|
||||
L".pdf", L".xls", L".xlsx", L".doc", L".docx",
|
||||
L".jpg", L".jpeg", L".png", L".webp", L".gif",
|
||||
L".mp3", L".wav", L".aac", L".ogg", L".flac",
|
||||
L".mp4", L".avi", L".mov", L".wmv", L".mkv",
|
||||
L".aux", L".cur", L".mui", L".ttf", L".efi",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* g_BlockReg[] = {
|
||||
L"\\REGISTRY\\MACHINE\\BCD00000000\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SAM\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SECURITY\\**",
|
||||
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\*ControlSet*\\Control\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\*ControlSet*\\Enum\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\*ControlSet*\\Services\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\MountedDevices\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SYSTEM\\Setup\\**",
|
||||
|
||||
@@ -94,6 +107,7 @@ wchar_t* g_BlockReg[] = {
|
||||
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Policies\\Microsoft\\MMC\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Policies\\Microsoft\\Windows\\System\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Wow6432Node\\**",
|
||||
L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Cryptography\\MachineGuid",
|
||||
L"\\REGISTRY\\MACHINE\\**\\Microsoft\\Windows Defender\\**",
|
||||
|
||||
L"\\REGISTRY\\**\\SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\*\\StubPath",
|
||||
@@ -106,7 +120,6 @@ wchar_t* g_BlockReg[] = {
|
||||
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\Remcos*\\**",
|
||||
L"\\REGISTRY\\USER\\S-1-*\\SOFTWARE\\DC3_FEXEC\\**",
|
||||
|
||||
|
||||
L"DisableAntiSpyware",
|
||||
L"DisableWindowsUpdateAccess",
|
||||
L"EnableLUA",
|
||||
@@ -195,197 +208,88 @@ wchar_t* g_BlockReg[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static BOOLEAN WildMatchN(const wchar_t* str, SIZE_T n, const wchar_t* pat)
|
||||
{
|
||||
if (*pat == 0)
|
||||
return n == 0;
|
||||
if (*pat == L'*') {
|
||||
if (pat[1] == L'*') {
|
||||
for (SIZE_T i = 0; i <= n; ++i)
|
||||
if (WildMatchN(str + i, n - i, pat + 2))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
SIZE_T i = 0;
|
||||
for (; i < n && str[i] != L'\\'; ++i)
|
||||
if (WildMatchN(str + i, n - i, pat + 1))
|
||||
return TRUE;
|
||||
return WildMatchN(str + i, n - i, pat + 1);
|
||||
}
|
||||
if (*pat == L'?')
|
||||
return n > 0 && str[0] != L'\\' && WildMatchN(str + 1, n - 1, pat + 1);
|
||||
if (n == 0)
|
||||
return FALSE;
|
||||
WCHAR sc = str[0], pc = *pat;
|
||||
if (sc >= L'A' && sc <= L'Z')
|
||||
sc += 32;
|
||||
if (pc >= L'A' && pc <= L'Z')
|
||||
pc += 32;
|
||||
return sc == pc && WildMatchN(str + 1, n - 1, pat + 1);
|
||||
}
|
||||
wchar_t* g_RemoteSuspectBins[] = {
|
||||
L"powershell.exe",
|
||||
L"pwsh.exe",
|
||||
L"wscript.exe",
|
||||
L"cscript.exe",
|
||||
L"mshta.exe",
|
||||
L"rundll32.exe",
|
||||
L"regsvr32.exe",
|
||||
L"cmd.exe",
|
||||
L"bitsadmin.exe",
|
||||
L"msbuild.exe",
|
||||
L"installutil.exe",
|
||||
L"wmic.exe",
|
||||
NULL
|
||||
};
|
||||
|
||||
static BOOLEAN MatchList(PUNICODE_STRING s, wchar_t** list)
|
||||
{
|
||||
if (!s || !s->Buffer || s->Length == 0)
|
||||
return FALSE;
|
||||
size_t s_len = s->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; list[i]; ++i) {
|
||||
wchar_t* pat = list[i];
|
||||
size_t pat_len = wcslen(pat);
|
||||
if (wcschr(pat, L'*') || wcschr(pat, L'?')) {
|
||||
if (WildMatchN(s->Buffer, s_len, pat))
|
||||
return TRUE;
|
||||
}
|
||||
else {
|
||||
if (s_len == pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return TRUE;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer + s_len - pat_len, pat, pat_len) == 0)
|
||||
return TRUE;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
wchar_t* g_RemoteCmdIndicatorsHttp[] = {
|
||||
L"http://",
|
||||
L"https://",
|
||||
NULL
|
||||
};
|
||||
|
||||
static wchar_t* MatchListGetRule(PUNICODE_STRING s, wchar_t** list)
|
||||
{
|
||||
if (!s || !s->Buffer || s->Length == 0)
|
||||
return 0;
|
||||
size_t s_len = s->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; list[i]; ++i) {
|
||||
wchar_t* pat = list[i];
|
||||
size_t pat_len = wcslen(pat);
|
||||
if (wcschr(pat, L'*') || wcschr(pat, L'?')) {
|
||||
if (WildMatchN(s->Buffer, s_len, pat))
|
||||
return pat;
|
||||
}
|
||||
else {
|
||||
if (s_len == pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return pat;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer + s_len - pat_len, pat, pat_len) == 0)
|
||||
return pat;
|
||||
if (s_len > pat_len && _wcsnicmp(s->Buffer, pat, pat_len) == 0)
|
||||
return pat;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
wchar_t* g_RemoteCmdIndicatorsGeneric[] = {
|
||||
L"-enc",
|
||||
L" -e ",
|
||||
L"-encodedcommand",
|
||||
L"frombase64string(",
|
||||
L"iex(",
|
||||
L"downloadstring(",
|
||||
NULL
|
||||
};
|
||||
|
||||
static __forceinline BOOLEAN USOK(PUNICODE_STRING s)
|
||||
{
|
||||
return s && s->Buffer && s->Length;
|
||||
}
|
||||
wchar_t* g_RemoteCmdFromCmdExe[] = {
|
||||
L"powershell",
|
||||
L"pwsh",
|
||||
L"mshta",
|
||||
L"rundll32",
|
||||
L"wscript",
|
||||
L"cscript",
|
||||
NULL
|
||||
};
|
||||
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe)
|
||||
{
|
||||
if (USOK(exe)) {
|
||||
if (IsWhitelist(exe))
|
||||
return FALSE;
|
||||
}
|
||||
if (USOK(exe) && USOK(key)) {
|
||||
size_t elen = exe->Length / sizeof(WCHAR);
|
||||
size_t klen = key->Length / sizeof(WCHAR);
|
||||
if (((elen >= 12 && _wcsnicmp(exe->Buffer + elen - 12, L"services.exe", 12) == 0) ||
|
||||
(elen >= 11 && _wcsnicmp(exe->Buffer + elen - 11, L"svchost.exe", 11) == 0) ||
|
||||
(elen >= 6 && _wcsnicmp(exe->Buffer + elen - 6, L"sc.exe", 6) == 0))) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if ((elen >= 14 && _wcsnicmp(exe->Buffer + elen - 14, L"powershell.exe", 14) == 0) ||
|
||||
(elen >= 8 && _wcsnicmp(exe->Buffer + elen - 8, L"pwsh.exe", 8) == 0)) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Internet Settings\\ZoneMap\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if ((elen >= 11 && _wcsnicmp(exe->Buffer + elen - 11, L"msiexec.exe", 11) == 0) ||
|
||||
(elen >= 20 && _wcsnicmp(exe->Buffer + elen - 20, L"TrustedInstaller.exe", 20) == 0)) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Installer\\**") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\CurrentVersion\\Uninstall\\**") ||
|
||||
WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\SYSTEM\\CurrentControlSet\\Services\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
if (elen >= 12 && _wcsnicmp(exe->Buffer + elen - 12, L"schtasks.exe", 12) == 0) {
|
||||
if (WildMatchN(key->Buffer, klen, L"\\REGISTRY\\**\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\**"))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (USOK(key)) {
|
||||
if (MatchBlockReg(key))
|
||||
return TRUE;
|
||||
}
|
||||
if (USOK(valueName)) {
|
||||
SIZE_T n = valueName->Length / sizeof(WCHAR);
|
||||
for (SIZE_T i = 0; g_BlockReg[i]; ++i) {
|
||||
wchar_t* pat = g_BlockReg[i];
|
||||
if (wcschr(pat, L'\\') || wcschr(pat, L'*') || wcschr(pat, L'?'))
|
||||
continue;
|
||||
size_t m = wcslen(pat);
|
||||
if (n == m && _wcsnicmp(valueName->Buffer, pat, m) == 0)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
wchar_t* g_RemoteCmdFromRundll32Exe[] = {
|
||||
L".dll,",
|
||||
L"javascript:",
|
||||
L"http",
|
||||
NULL
|
||||
};
|
||||
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath)
|
||||
{
|
||||
PEPROCESS process = NULL;
|
||||
NTSTATUS status = PsLookupProcessByProcessId(pid, &process);
|
||||
if (!NT_SUCCESS(status))
|
||||
return FALSE;
|
||||
wchar_t* g_RemoteCmdRegsvr32Need[] = {
|
||||
L"/i:",
|
||||
NULL
|
||||
};
|
||||
|
||||
HANDLE h = NULL;
|
||||
status = ObOpenObjectByPointer(process, OBJ_KERNEL_HANDLE, NULL, PROCESS_QUERY_LIMITED_INFORMATION, *PsProcessType, KernelMode, &h);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
ULONG len = 0;
|
||||
status = ZwQueryInformationProcess(h, ProcessImageFileName, NULL, 0, &len);
|
||||
if (status != STATUS_INFO_LENGTH_MISMATCH || len == 0) {
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
PVOID buf = ExAllocatePool2(POOL_FLAG_NON_PAGED, len, 'iPgN');
|
||||
if (!buf) {
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
status = ZwQueryInformationProcess(h, ProcessImageFileName, buf, len, &len);
|
||||
if (NT_SUCCESS(status)) {
|
||||
PUNICODE_STRING image = (PUNICODE_STRING)buf;
|
||||
if (image->Buffer && image->Length > 0 && ProcessImagePath->MaximumLength >= image->Length + sizeof(WCHAR)) {
|
||||
RtlCopyMemory(ProcessImagePath->Buffer, image->Buffer, image->Length);
|
||||
ProcessImagePath->Length = image->Length;
|
||||
ProcessImagePath->Buffer[image->Length / sizeof(WCHAR)] = 0;
|
||||
ExFreePool2(buf, 'iPgN', NULL, 0);
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
ExFreePool2(buf, 'iPgN', NULL, 0);
|
||||
ZwClose(h);
|
||||
ObDereferenceObject(process);
|
||||
return FALSE;
|
||||
}
|
||||
wchar_t* g_RemoteCmdFromMshtaExe[] = {
|
||||
L"http://",
|
||||
L"https://",
|
||||
NULL
|
||||
};
|
||||
|
||||
BOOLEAN IsWhitelist(PUNICODE_STRING s)
|
||||
{
|
||||
if (MatchList(s, g_WhitelistExcept))
|
||||
return FALSE;
|
||||
return MatchList(s, g_Whitelist);
|
||||
}
|
||||
wchar_t* g_ScreenCapModules[] = {
|
||||
L"windows.graphics.capture.dll",
|
||||
L"dxgi.dll",
|
||||
L"d3d11.dll",
|
||||
L"dwmapi.dll",
|
||||
L"dcomp.dll",
|
||||
L"mfreadwrite.dll",
|
||||
L"mfplat.dll",
|
||||
L"opengl32.dll",
|
||||
L"gdi32full.dll",
|
||||
L"win32u.dll",
|
||||
NULL
|
||||
};
|
||||
|
||||
BOOLEAN MatchBlockReg(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchList(s, g_BlockReg);
|
||||
}
|
||||
wchar_t* g_ScreenUserSubdirs[] = {
|
||||
L"\\AppData\\",
|
||||
L"\\Temp\\",
|
||||
L"\\Downloads\\",
|
||||
NULL
|
||||
};
|
||||
|
||||
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s)
|
||||
{
|
||||
return MatchListGetRule(s, g_BlockReg);
|
||||
}
|
||||
wchar_t* g_ScreenOtherProcNeedles[] = {
|
||||
L"\\ProgramData\\",
|
||||
NULL
|
||||
};
|
||||
@@ -1,8 +1,25 @@
|
||||
#pragma once
|
||||
#include <ntifs.h>
|
||||
|
||||
wchar_t* GetMatchedBlockRegRule(PUNICODE_STRING s);
|
||||
BOOLEAN MatchBlockReg(PUNICODE_STRING s);
|
||||
BOOLEAN IsWhitelist(PUNICODE_STRING s);
|
||||
BOOLEAN GetProcessImagePathByPid(HANDLE pid, PUNICODE_STRING ProcessImagePath);
|
||||
BOOLEAN IsRegistryBlock(PUNICODE_STRING key, PUNICODE_STRING valueName, PUNICODE_STRING exe);
|
||||
extern wchar_t* g_Whitelist[];
|
||||
extern wchar_t* g_WhitelistExcept[];
|
||||
|
||||
extern wchar_t* g_AttachDisk[];
|
||||
extern wchar_t* g_BlockFile[];
|
||||
extern wchar_t* g_BlockRansom[];
|
||||
extern wchar_t* g_Blocksuffix[];
|
||||
|
||||
extern wchar_t* g_BlockReg[];
|
||||
|
||||
extern wchar_t* g_RemoteSuspectBins[];
|
||||
|
||||
extern wchar_t* g_RemoteCmdIndicatorsHttp[];
|
||||
extern wchar_t* g_RemoteCmdIndicatorsGeneric[];
|
||||
extern wchar_t* g_RemoteCmdFromCmdExe[];
|
||||
extern wchar_t* g_RemoteCmdFromRundll32Exe[];
|
||||
extern wchar_t* g_RemoteCmdRegsvr32Need[];
|
||||
extern wchar_t* g_RemoteCmdFromMshtaExe[];
|
||||
|
||||
extern wchar_t* g_ScreenCapModules[];
|
||||
extern wchar_t* g_ScreenUserSubdirs[];
|
||||
extern wchar_t* g_ScreenOtherProcNeedles[];
|
||||
@@ -0,0 +1,80 @@
|
||||
#include <ntifs.h>
|
||||
#include "DriverEntry.h"
|
||||
#include "ProtectMatch.h"
|
||||
|
||||
static BOOLEAN g_ScreenImgCb = FALSE;
|
||||
|
||||
static VOID LogAnsi3(PCSTR tag, ULONG upid, PUNICODE_STRING s1, PUNICODE_STRING s2)
|
||||
{
|
||||
ANSI_STRING a1 = { 0 }, a2 = { 0 };
|
||||
CHAR buf[1024] = { 0 };
|
||||
RtlUnicodeStringToAnsiString(&a1, s1, TRUE);
|
||||
if (s2)
|
||||
RtlUnicodeStringToAnsiString(&a2, s2, TRUE);
|
||||
RtlStringCchPrintfA(buf, RTL_NUMBER_OF(buf), "%s | %u | %s | %s", tag, upid, a1.Buffer ? a1.Buffer : "", a2.Buffer ? a2.Buffer : "");
|
||||
SendPipeLog(buf, strlen(buf));
|
||||
RtlFreeAnsiString(&a1);
|
||||
RtlFreeAnsiString(&a2);
|
||||
}
|
||||
|
||||
static VOID ImageLoadNotify(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(ImageInfo);
|
||||
if (g_Unloading)
|
||||
return;
|
||||
if (KeGetCurrentIrql() != PASSIVE_LEVEL)
|
||||
return;
|
||||
if (!ExAcquireRundownProtection(&g_Rundown))
|
||||
return;
|
||||
if (!FullImageName || !FullImageName->Buffer || FullImageName->Length == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
|
||||
UNICODE_STRING proc = { 0 };
|
||||
WCHAR pbuf[512] = { 0 };
|
||||
proc.Buffer = pbuf;
|
||||
proc.MaximumLength = sizeof(pbuf);
|
||||
|
||||
if (!GetProcessImagePathByPid(ProcessId, &proc)) {
|
||||
PEPROCESS eproc = NULL;
|
||||
if (NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &eproc))) {
|
||||
PCSTR n = PsGetProcessImageFileName(eproc);
|
||||
SIZE_T l = n ? strlen(n) : 0;
|
||||
for (SIZE_T i = 0; i < l && i < RTL_NUMBER_OF(pbuf) - 1; i++)
|
||||
pbuf[i] = (WCHAR)n[i];
|
||||
pbuf[l] = 0;
|
||||
proc.Length = (USHORT)(l * sizeof(WCHAR));
|
||||
ObDereferenceObject(eproc);
|
||||
}
|
||||
}
|
||||
if (proc.Length == 0) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
if (IsWhitelist(&proc)) {
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOLEAN suspicious = MatchSuspiciousProcPath(&proc);
|
||||
if (suspicious && MatchScreenCapModule(FullImageName))
|
||||
LogAnsi3("SCREEN_BLOCK", (ULONG)(ULONG_PTR)ProcessId, &proc, FullImageName);
|
||||
|
||||
ExReleaseRundownProtection(&g_Rundown);
|
||||
}
|
||||
|
||||
NTSTATUS InitScreenProtect(VOID)
|
||||
{
|
||||
NTSTATUS s = PsSetLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
if (NT_SUCCESS(s)) g_ScreenImgCb = TRUE;
|
||||
return s;
|
||||
}
|
||||
|
||||
VOID UninitScreenProtect(VOID)
|
||||
{
|
||||
if (g_ScreenImgCb) {
|
||||
PsRemoveLoadImageNotifyRoutine(ImageLoadNotify);
|
||||
g_ScreenImgCb = FALSE;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user