十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SSDT Hook原理与Windows内核进程拦截实战

SSDT Hook原理与Windows内核进程拦截实战 简介本资源是一份面向Windows内核安全开发者与系统防护研究者的SSDT Hook实战项目聚焦进程创建拦截、注册表写入监控及.sys驱动加载禁止三大核心防护场景适用于恶意软件分析、终端安全加固与驱动开发学习。压缩包共19个文件含关键驱动文件PRMonitor.sys、用户态控制程序PRMonitor.exe、核心Hook实现源码cpp/c/h、DDK构建脚本bat/makefile/sources及VC6工程配置dsw/dsp完整呈现从内核模块开发到编译部署的全流程结构。资源仅65KB精炼紧凑便于逆向分析与教学拆解。已有366人学习下载提供可直接调试的完整工程框架、SSDT函数地址解析逻辑、ZwCreateProcess等关键服务挂钩示例以及系统托盘界面ico/res/rc与日志输出log等实用细节是深入理解Windows底层监控机制的优质入门与进阶材料。1. SSDT Hook 不是“拦截器”而是内核服务调用的“门禁闸机”很多人第一次看到PRMonitor.sys和hookzwcreateprocess.c下意识觉得这是个“进程拦截工具”——点开就弹窗、运行就报错、双击就禁止。但实际拆开看它根本不是在用户层做 MessageBox 或 CreateProcess 失败返回而是在 Windows 内核中把ZwCreateProcess这个系统服务入口的函数指针从原始地址比如ntoskrnl.exe!NtCreateProcess悄悄替换成自己写的MyZwCreateProcess。这个替换动作发生在 SSDTSystem Service Dispatch Table这张表里而 SSDT 是 Windows 32 位系统XP/2003中所有用户态程序发起系统调用时必须经过的唯一跳转枢纽。它不像 API Hook 那样可以被绕过也不像 Inline Hook 那样容易被 PatchGuard 干掉在未启用 PatchGuard 的旧系统上。所以 PRMonitor 的核心能力不是“阻止程序启动”而是“让系统压根不承认这个进程创建请求合法”。它对注册表的管控同理不是等 RegEdit.exe 打开后再删键值而是在ZwCreateKey、ZwSetValueKey被内核执行前就截住直接返回STATUS_ACCESS_DENIED对.sys文件的加载控制则是挂钩ZwLoadDriver在驱动镜像还没映射进内核空间前就判定拒绝。这套机制适用于需要强管控的终端安全场景比如工业控制终端、金融柜面机、考试监考系统——它们不允许任何未经签名的进程、驱动或注册表写入且要求响应在毫秒级完成不能依赖用户层轮询或事件回调。2. SSDT Hook 的实现前提与结构解析为什么必须用 DDK 编译、为什么只兼容 32 位2.1 SSDT 表结构与 Windows 版本绑定关系SSDT 在 Windows 中并非公开导出符号其地址需通过内核模块导出表或特征码扫描获取。在PRMonitor.cpp中可见如下关键逻辑// 获取 SSDT 基址Windows XP SP3 典型偏移 PVOID GetSSDTBase() { ULONG_PTR pIdt 0; __asm sidt [pIdt] // 获取 IDTR 寄存器值 PULONG_PTR pIdtBase (PULONG_PTR)(*(PULONG_PTR)(pIdt 2)); ULONG_PTR pKiSystemService *(PULONG_PTR)(pIdtBase 0x8 * 0x2e); // 0x2e 是 int 0x2e 的中断号对应系统调用 // 向上回溯找到 KiServiceTable即 SSDT for (ULONG_PTR addr pKiSystemService; addr 0x80000000; addr - 4) { if (*(PULONG)addr 0x80567890 *(PULONG)(addr 4) 0x80567894) { // 特征码匹配此处为示意 return (PVOID)addr; } } return NULL; }注意这段代码仅在 Windows XP / Server 2003 的 32 位内核中稳定有效。从 Windows Vista 开始微软引入了 Shadow SSDT用于 GUI 子系统并在 64 位系统中彻底移除 SSDT 直接挂钩能力改用 Kernel Callbacks如PsSetCreateProcessNotifyRoutine、Minifilter文件系统过滤、WFP网络栈过滤等替代方案。因此PRMonitor.sys的makefile中明确指定TARGETOS WINXPTARGETPLATFORM x86且ddkbuild.bat调用的是build -ZcWindows Driver Kit 2003 SP1 工具链。试图在 Win10 x64 上加载该驱动会导致蓝屏IRQL_NOT_LESS_OR_EQUAL因为KeStackAttachProcess等函数行为已变更。2.2 SSDT Hook 的三步落地定位 → 备份 → 替换整个 hook 流程在hookzwcreateprocess.c中被封装为InstallHook()函数其核心步骤如下2.2.1 定位 SSDT 表基址与目标服务索引// 获取 ZwCreateProcess 在 SSDT 中的索引Windows XP 下固定为 0x68 ULONG uIndex 0x68; // 对应 nt!ZwCreateProcess PVOID* pSSDT (PVOID*)GetSSDTBase(); if (!pSSDT) return FALSE; // 备份原始函数地址用于后续调用或恢复 OriginalZwCreateProcess pSSDT[uIndex];这里0x68并非硬编码魔法数而是由ntoskrnl.exe导出的ZwCreateProcess符号在nt!KiServiceTable中的相对位置。可通过dumpbin /exports ntoskrnl.exe查得其 RVA再结合内核基址计算得出。PRMonitor.dsp工程中sources文件定义了NTTARGETFILE0 $(OBJ_PATH)\PRMonitor.sys说明该驱动依赖ntoskrnl.lib链接而非动态解析。2.2.2 关闭写保护CR0 寄存器操作// 关闭内存写保护CR0.PG1, CR0.WP0 ULONG cr0; __asm { mov eax, cr0 mov cr0, eax and eax, 0xFFFEFFFF // 清除 WP 位 mov cr0, eax } pSSDT[uIndex] (PVOID)MyZwCreateProcess; // 替换函数指针 // 恢复写保护 __asm { mov eax, cr0 or eax, 0x10000 // 设置 WP 位 mov cr0, eax }提示CR0.WPWrite Protect位控制是否允许对只读页写入。内核数据段默认设为只读直接写pSSDT[uIndex]会触发页故障。此段汇编必须在 IRQL DISPATCH_LEVEL 下执行否则KeRaiseIrqlToDpcLevel()未调用时修改 CR0 可能导致系统死锁。PRMonitor.cpp中DriverEntry显式调用KeRaiseIrqlToDpcLevel(OldIrql)正是为此。2.2.3 MyZwCreateProcess 的决策逻辑NTSTATUS NTAPI MyZwCreateProcess( OUT PHANDLE ProcessHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN HANDLE ParentProcess, IN BOOLEAN InheritObjectTable, IN HANDLE SectionHandle OPTIONAL, IN HANDLE DebugPort OPTIONAL, IN HANDLE ExceptionPort OPTIONAL ) { // 解析进程路径从 OBJECT_ATTRIBUTES-ObjectName-Buffer UNICODE_STRING usPath; if (ObjectAttributes ObjectAttributes-ObjectName) { RtlInitUnicodeString(usPath, ObjectAttributes-ObjectName-Buffer); if (RtlCompareUnicodeString(usPath, g_BlockProcessName, TRUE) 0) { return STATUS_ACCESS_DENIED; // 匹配黑名单进程直接拒绝 } } // 其他进程放行调用原始函数 return OriginalZwCreateProcess( ProcessHandle, DesiredAccess, ObjectAttributes, ParentProcess, InheritObjectTable, SectionHandle, DebugPort, ExceptionPort ); }该函数接收完整系统调用参数可精确提取待创建进程的完整路径ObjectAttributes-ObjectName指向\??\C:\xxx.exe比用户层CreateProcess的lpApplicationName更可靠后者可被伪造。g_BlockProcessName在PRMDlg.rc中定义为资源字符串由PRMonitor.exe通过DeviceIoControl传入驱动实现运行时策略更新。2.3 注册表与驱动加载的 Hook 实现差异目标服务SSDT 索引XP关键参数提取方式拒绝条件判断依据ZwCreateKey0x1AObjectAttributes-ObjectName注册表路径路径包含Software\Policies\MicrosoftZwSetValueKey0x1BValueName-Buffer键名 Data值内容ValueName为DisableTaskMgr且Data非零ZwLoadDriver0x8EDriverServiceName-Buffer驱动服务名名称匹配malware.sys或签名无效hookzwcreateprocess.h中定义了HOOK_SERVICE结构体统一管理各服务的原始地址、hook 函数及启用标志。PRMonitor.exe通过IOCTL_PRMONITOR_SET_HOOK控制开关避免全量 hook 带来的性能损耗。3. 驱动与用户态协同PRMonitor.exe 如何下发策略并维持通信3.1 驱动通信接口设计DeviceIoControl 的 IOCTL 码定义PRMonitor.sys在DriverEntry中创建设备对象\\Device\\PRMonitor和符号链接\\DosDevices\\PRMonitor暴露标准 I/O 控制接口。PRMonitor.exe通过CreateFile打开该设备再调用DeviceIoControl传递策略// PRMonitor.cpp 中的策略下发逻辑 HANDLE hDevice CreateFile(L\\\\.\\PRMonitor, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hDevice INVALID_HANDLE_VALUE) return; // 构造策略包 typedef struct _PRMONITOR_POLICY { WCHAR szProcessName[MAX_PATH]; WCHAR szRegPath[MAX_PATH]; WCHAR szDriverName[MAX_PATH]; BOOLEAN bBlockProcess; BOOLEAN bBlockReg; BOOLEAN bBlockDriver; } PRMONITOR_POLICY, *PPRMONITOR_POLICY; PRMONITOR_POLICY policy {0}; wcscpy_s(policy.szProcessName, Lnotepad.exe); policy.bBlockProcess TRUE; DWORD dwRet; DeviceIoControl(hDevice, IOCTL_PRMONITOR_SET_POLICY, policy, sizeof(policy), NULL, 0, dwRet, NULL);IOCTL_PRMONITOR_SET_POLICY定义在resource.h中值为CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)。驱动端在PRMonitorDispatchIoctl中解析该请求将szProcessName复制到全局变量g_BlockProcessName供MyZwCreateProcess使用。3.2 系统托盘与实时状态反馈PRMonitor.exe主界面由PRMDlg.rc定义采用传统 Win32 对话框。其核心是CPRMDlg类中的OnTrayNotify消息处理// CPRMDlg.cpp LRESULT CPRMDlg::OnTrayNotify(WPARAM wParam, LPARAM lParam) { if (lParam WM_LBUTTONDBLCLK) { ShowWindow(SW_SHOW); // 双击托盘图标显示主窗口 SetForegroundWindow(); } else if (lParam WM_RBUTTONUP) { // 弹出右键菜单启用/禁用监控、添加进程、查看日志 TrackPopupMenu(m_hMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, m_hWnd, NULL); } return 0; }提示托盘图标资源_systemtray.ico被编译进Dlg.res图标状态绿色启用灰色禁用由Shell_NotifyIcon的NIM_MODIFY消息动态更新。用户点击“启用监控”时PRMonitor.exe发送IOCTL_PRMONITOR_ENABLE_HOOK驱动调用InstallHook()点击“禁用”则调用UninstallHook()恢复原始 SSDT 地址。3.3 日志记录与调试信息输出驱动日志不写文件避免 I/O 延迟而是通过DbgPrint输出到内核调试缓冲区。PRMonitor.sys中#define LOG(fmt, ...) DbgPrint([PRMonitor] fmt \n, ##__VA_ARGS__) // 在 MyZwCreateProcess 中 LOG(Blocked process: %wZ, usPath);配合DebugView工具Sysinternals 套件可实时捕获这些日志。buildchk.log记录了 DDK 编译过程中的警告例如warning C4152: nonstandard extension used : function pointer to data pointer conversion提示pSSDT[uIndex]类型转换不安全需强制类型转换*(PVOID*)pSSDT[uIndex] MyZwCreateProcess。4. 实战部署与常见失效场景排查为什么你的 hook 总是失败4.1 必须关闭的系统保护机制清单SSDT Hook 在现代 Windows 上极易失效以下四项必须确认关闭保护机制检查命令关闭方法需管理员权限失效表现数据执行保护DEPbcdedit /enum {current}bcdedit /set {current} nx AlwaysOff加载驱动时STATUS_INVALID_IMAGE_FORMAT内核补丁保护PatchGuardwmic qfe list查看 KBxxxxxx 更新仅限 Windows XP/2003Win7 无法禁用DRIVER_VERIFIER_DETECTED_VIOLATION蓝屏驱动签名强制Test Signingbcdedit /enum {current}bcdedit /set {current} testsigning onSTATUS_INVALID_IMAGE_HASH加载失败Windows Defender 实时防护powershell Get-MpComputerStatusSet-MpPreference -DisableRealtimeMonitoring $truePRMonitor.sys被隔离删除ddkbuild.bat中set BUILDENVchk表明使用 Checked Build调试版其驱动包含额外断言和日志但体积更大、性能更低仅用于开发验证。4.2 SSDT 地址扫描失败的三大原因与修复当GetSSDTBase()返回NULL常见原因及对策4.2.1 IDT 基址获取错误sidt指令被虚拟化拦截某些安全软件如 360、火绒会 Hooksidt指令返回伪造 IDTR。此时需改用KeQuerySystemTime 特征码扫描// 替代方案扫描 ntoskrnl.exe 内存寻找 KiServiceTable PUCHAR pNtos (PUCHAR)MmGetSystemRoutineAddress(ntoskrnlName); for (ULONG i 0; i 0x100000; i) { if (pNtos[i] 0x8B pNtos[i1] 0x0D // mov ecx, [xxx] *(PULONG)(pNtos i 2) 0x80000000) { SSDTBase *(PVOID*)(pNtos i 2); break; } }4.2.2 SSDT 索引偏移变动SP 补丁导致Windows XP SP2 与 SP3 的ZwCreateProcess索引不同SP2 为0x67SP3 为0x68。PRMonitor.dsw工程配置中Configuration Properties → General → Platform Toolset应设为WindowsKernelModeDriver10.0并确保sources文件指定TARGETOS WINXP否则 DDK 会链接错误版本的ntoskrnl.lib。4.2.3 内存布局随机化ASLR干扰虽然 XP 不支持内核 ASLR但某些第三方驱动如显卡驱动会重映射ntoskrnl.exe。此时需在DriverEntry中先调用MmGetSystemRoutineAddress获取ZwCreateProcess地址再反向计算 SSDT 基址PVOID pZwCreate MmGetSystemRoutineAddress(usZwCreateProcess); // 根据 ntoskrnl.exe 中 KiServiceTable 的相对偏移-0x12345计算 SSDTBase (PVOID)((PUCHAR)pZwCreate - 0x12345);4.3 用户态策略同步失败的调试技巧若PRMonitor.exe点击“添加进程”后无效果按以下顺序排查确认设备句柄有效CreateFile返回INVALID_HANDLE_VALUE时检查GetLastError()是否为ERROR_FILE_NOT_FOUND驱动未加载或ERROR_ACCESS_DENIED权限不足验证 IOCTL 码匹配PRMonitor.sys中switch (dwIoControlCode)是否包含IOCTL_PRMONITOR_SET_POLICY且METHOD_BUFFERED模式下输入缓冲区长度sizeof(PRMONITOR_POLICY)必须精确检查驱动全局变量用WinDbg连接目标机执行dt PRMonitor!g_BlockProcessName查看字符串是否成功写入确认 hook 已安装!ssdt命令输出中索引0x68对应地址是否为PRMonitor!MyZwCreateProcess而非原始ntoskrnl!NtCreateProcess。5. 进阶技巧如何将 SSDT Hook 与现代 Windows 兼容方案桥接5.1 在 Win10 x64 上复用 PRMonitor 逻辑的迁移路径SSDT Hook 在 Win10 x64 已不可用但PRMonitor的策略引擎进程/注册表/驱动黑名单仍可复用。推荐采用分层架构功能WinXP/2003 方案Win10 x64 替代方案数据互通方式进程创建拦截SSDT HookZwCreateProcessPsSetCreateProcessNotifyRoutineExObRegisterCallbacks共享内存CreateFileMapping注册表修改拦截SSDT HookZwSetValueKeyMinifilter CmRegisterCallbackEx需内核模式DeviceIoControl传递键路径驱动加载拦截SSDT HookZwLoadDriverCiInitializeCiRegisterCrossCallCallback签名验证策略文件C:\PRMonitor\policy.xmlPRMonitor.exe可改造为跨平台策略中心在 WinXP 上加载PRMonitor.sys在 Win10 上启动PRMonitorService.exe以 LocalSystem 身份运行后者调用CmRegisterCallbackEx注册注册表回调并通过WMI查询Win32_SystemDriver判断驱动加载状态。5.2 使用 DbgView 实时分析 hook 触发频率PRMonitor.sys的DbgPrint日志是性能瓶颈但也是唯一可观测入口。在MyZwCreateProcess开头添加时间戳LARGE_INTEGER li; KeQuerySystemTime(li); LOG(Hook triggered at %llx, PID %d, li.QuadPart, PsGetCurrentProcessId());用 DbgView 设置 Filter 为PRMonitor开启Capture Global Win32即可统计每秒 hook 触发次数。若数值超过 500 次/秒说明策略过于宽泛如通配符*.exe需优化为精确路径匹配。5.3 一个实用的注册表保护技巧绕过 RegEdit 的隐藏键值ZwCreateKeyHook 只能拦截 API 调用但 RegEdit.exe 可通过RegOpenKeyRegEnumKey枚举子键再RegDeleteKey删除。PRMonitor未实现此逻辑需补充// 在 MyZwEnumerateKey 中添加 if (RtlCompareUnicodeString(usKeyPath, g_ProtectedKey, TRUE) 0) { // 返回 STATUS_NO_MORE_ENTRIES使 RegEdit 认为无子键可枚举 return STATUS_NO_MORE_ENTRIES; }g_ProtectedKey设为L\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run即可让恶意软件无法通过枚举方式发现自启动项比单纯禁止RegSetValueKey更隐蔽。PRMonitor.exe的“注册表保护”选项勾选后驱动自动安装此枚举拦截无需重启。本文还有配套的精品资源点击获取
返回列表