通过原生API实现正确的ntdll.dll .text节脱钩。支持x86/x64/wow64。
程序遍历PEB定位ntdll.dll基地址,手动解析PE导出表以解析NT API函数,无需接触导入地址表(IAT)。然后使用NtOpenFile从磁盘打开干净的ntdll.dll,通过NtCreateSection创建节对象,再通过NtMapViewOfSection将其映射到进程中,从而获得脱钩源,而无需通过LoadLibrary实际加载第二个dll。被挂钩的.text节通过NtProtectVirtualMemory更改为PAGE_EXECUTE_READWRITE,使用自定义memcpy将干净的.text复制覆盖被挂钩的节,然后将保护恢复为原始标志。经过逐字节验证脱钩成功之后,通过NtUnmapViewOfSection正确取消映射干净的副本,确保内存中不再保留第二个ntdll。
大多数公开的脱钩代码实际上都是从同一个垃圾源(比如ired.team的示例以及GitHub上几乎所有开源脱钩器)复制粘贴过来的,存在大量问题,使其对任何真正的EDR都毫无用处。它们使用VirtualProtect而不是原生API,这完全违背了目的——因为你是在调用被挂钩的函数来解除挂钩;它们在.text节上设置RWX权限,这是一个巨大的侵入指标(IOC),EDR会立即标记;而且它们实际上没有取消映射干净的副本,因为对节映射调用CloseHandle并不会释放内存。你需要使用UnmapViewOfFile或NtUnmapViewOfSection,但每个人都忘记了这一步,结果进程中加载了两个ntdll副本,这基本上就是一个巨大的霓虹灯写着“我是恶意软件”。它们还试图在主要的ntdll上使用FreeLibrary,这根本不起作用,反而导致句柄泄漏;此外,它们不必要地将保护更改为RWX两次,而如果正确恢复只需一次。
本实现通过全程使用原生API(NtOpenFile、NtCreateSection、NtMapViewOfSection、NtProtectVirtualMemory)、使用NtUnmapViewOfSection正确取消映射干净的副本以避免加载两个ntdll副本、不在主要ntdll上使用FreeLibrary、以及通过内存比较验证成功,修复了公共脱钩代码中的常见错误。然而,它并未避免现代EDR检测到的基本IOC。在C:\Windows\System32\ntdll.dll上使用NtOpenFile是一个会被记录的IOC,使用SEC_IMAGE指向ntdll.dll的NtCreateSection会通过ETW被跟踪,即使使用原生API修改ntdll的.text节内存保护也是一个巨大的红旗,而写入.text可通过内存写入回调检测。这项技术广为人知,现代EDR如CrowdStrike和SentinelOne已对整个模式有签名检测。高级EDR如Microsoft Defender for Endpoint和Elastic甚至不再使用用户态钩子,因为它们依赖内核回调和ETW遥测,所以脱钩对它们根本无效。这仅对仅使用内联钩子的基本EDR和较旧的安全产品有效,但对任何具有内核模式组件或行为分析的产品都会失败。更好的替代方案包括直接系统调用(你从一开始就不调用被挂钩的函数)、用于wow64边界穿越的Heaven's Gate、运行时从ntdll.text手动提取系统调用,或者完全避免使用可疑API,因为在2024/2025年,脱钩对于真正的企业级EDR基本已是一种过时技术。
支持x64原生进程、x86原生进程以及wow64进程(x64 Windows上的x86)。它自动检测wow64并选择正确的系统目录(System32 vs SysWOW64),因此无需手动考虑。
仅处理ntdll.dll的.text节,因为所有实际的函数代码都位于那里,EDR的内联函数钩子(函数序言处的jmp指令)也放置在那里。其他节如.data和.rdata保持不变,因为没有理由去碰它们,而且只会无益地增加更多的IOC。
cl /EHsc /std:c++17 main.cpp /Fe:unhook.exe
或者任何其他方式,任何现代C++编译器都可以。需要windows.h和winternl.h。
未经授权访问计算机系统是违法的。请仅在你拥有或已获得授权测试的系统上使用。联邦监狱是真实存在的。
代码使用CONTAINING_RECORD宏正确遍历LDR链表,通过段寄存器(x64上为gs,x86上为fs)访问PEB,通过名称比较进行硬编码的.text节搜索(这可以更优雅,但能用就行),通过NTSTATUS代码和NT_SUCCESS宏处理错误,并将内存操作包装在SEH try/except中以确保安全。如果你读不懂C++并且不理解PE格式内部细节,那你可能不应该使用这个工具。