64位下使用回调函数实现监控
字数 782 2025-08-06 18:07:33

64位系统下使用回调函数实现监控技术详解

0x00 前言

在64位Windows系统中,由于Patch Guard(内核补丁保护)机制的存在,传统的API挂钩技术变得不稳定且容易引发系统崩溃。微软为此提供了系统回调API函数,作为更安全可靠的监控实现方式。

0x01 进程监控与保护

核心API: PsSetCreateProcessNotifyRoutineEx

NTSTATUS PsSetCreateProcessNotifyRoutineEx(
    [in] PCREATE_PROCESS_NOTIFY_ROUTINE_EX NotifyRoutine,
    [in] BOOLEAN Remove
);

绕过强制完整性检查

微软要求驱动必须有数字签名才能使用此API,通过检查IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY标志实现。绕过方法:

BOOLEAN bypass_signcheck(PDRIVER_OBJECT pDriverObject) {
#ifdef _WIN64
    typedef struct _KLDR_DATA_TABLE_ENTRY {
        // 64位结构体定义
        ULONG Flags;
        // 其他成员...
    } KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
#else
    // 32位结构体定义
#endif
    
    PKLDR_DATA_TABLE_ENTRY pLdrData = (PKLDR_DATA_TABLE_ENTRY)pDriverObject->DriverSection;
    pLdrData->Flags = pLdrData->Flags | 0x20;
    return TRUE;
}

回调函数实现

VOID CreateProcessNotifyEx(
    PEPROCESS Process,
    HANDLE ProcessId,
    PPS_CREATE_NOTIFY_INFO CreateInfo
) {
    if (CreateInfo == NULL) {
        DbgPrint("进程退出\n");
        return;
    }
    
    PCHAR pszImageFileName = PsGetProcessImageFileName(Process);
    if (pszImageFileName) {
        DbgPrint("新创建的进程是:%s\r\n", pszImageFileName);
        if (strcmp(pszImageFileName, "test.exe") == 0) {
            CreateInfo->CreationStatus = STATUS_UNSUCCESSFUL;
            DbgPrint("拦截进程:%s成功\r\n", pszImageFileName);
        }
    }
}

驱动卸载处理

VOID DriverUnload(IN PDEVICE_OBJECT driverObject) {
    NTSTATUS status = PsSetCreateProcessNotifyRoutineEx(
        (PCREATE_PROCESS_NOTIFY_ROUTINE_EX)CreateProcessNotifyEx, 
        TRUE
    );
    // 错误处理...
}

0x02 线程监控与保护

核心API: PsSetCreateThreadNotifyRoutine

NTSTATUS PsSetCreateThreadNotifyRoutine(
    [in] PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
);

回调函数结构

VOID CreateThreadNotifyRoutine(
    [in] HANDLE ProcessId,
    [in] HANDLE ThreadId,
    [in] BOOLEAN Create
) {
    if (Create) {
        DbgPrint("新创建的线程ID为:%d,所属进程ID为:%d\r\n", ThreadId, ProcessId);
    } else {
        DbgPrint("新销毁的线程ID为:%d,所属进程ID为:%d\r\n", ThreadId, ProcessId);
    }
}

线程保护实现

  1. 通过PID找到EPROCESS
  2. 通过TID找到ETHREAD
  3. 通过EPROCESS得到进程路径
  4. 判断进程名是否匹配
  5. 修改线程回调函数地址(ETHREAD + 0x410)为ret指令(0xC3)
if (strstr(pszImageName, "notepad") != NULL) {
    pWin32Address = *(UCHAR**)((UCHAR*)Thread + 0x410);
    if (MmIsAddressValid(pWin32Address)) {
        ClosePageProtect();
        *pWin32Address = 0xC3;
        OpenPageProtect();
    }
}

0x03 模块监控与保护

核心API: PsSetLoadImageNotifyRoutine

NTSTATUS PsSetLoadImageNotifyRoutine(
    [in] PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine
);

回调函数结构

VOID SetLoadImageNotifyRoutine(
    _In_opt_ PUNICODE_STRING FullImageName,
    _In_ HANDLE ProcessId,
    _In_ PIMAGE_INFO ImageInfo
) {
    // 实现模块监控或保护
}

驱动模块卸载

通过修改入口点代码实现:

PIMAGE_DOS_HEADER pDosHeader = pLoadImageBase;
PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((PCHAR)pDosHeader + pDosHeader->e_lfanew);
PVOID pAddressOfEntryPoint = (PVOID)((PCHAR)pDosHeader + pNtHeaders->OptionalHeader.AddressOfEntryPoint);

ULONG CodeSize = 6;
UCHAR pShellCode[6] = {0xB8, 0x22, 0x00, 0x00, 0xC0, 0xC3}; // mov eax, 0xC0000022; ret

PMDL pMdl = MmCreateMdl(NULL, pAddressOfEntryPoint, CodeSize);
MmBuildMdlForNonPagedPool(pMdl);
PVOID pVoid = MmMapLockedPages(pMdl, KernelMode);
RtlCopyMemory(pVoid, pShellCode, ulShellCodeSize);
MmUnmapLockedPages(pVoid, pMdl);
IoFreeMdl(pMdl);

DLL模块卸载

使用未公开API MmUnmapViewOfSection:

NTSTATUS NoLoadDll(HANDLE ProcessId, PVOID pImageBase) {
    NTSTATUS status = STATUS_SUCCESS;
    PEPROCESS pEProcess = NULL;
    
    status = PsLookupProcessByProcessId(ProcessId, &pEProcess);
    if (!NT_SUCCESS(status)) {
        return status;
    }
    
    status = MmUnmapViewOfSection(pEProcess, pImageBase);
    return status;
}

实现效果

  1. 进程保护:阻止特定进程(如notepad.exe)启动
  2. 驱动模块保护:阻止特定驱动(如DriverTest.sys)加载
  3. DLL模块保护:阻止特定DLL(如Test.dll)注入

注意事项

  1. 在64位系统下不能直接使用内联汇编,需要单独编译.obj文件
  2. 修改内存属性时需要先关闭页保护(ClosePageProtect)
  3. 卸载驱动时必须移除回调函数,否则会导致蓝屏
  4. Windows 10及更新版本可能有额外的保护机制需要考虑
64位系统下使用回调函数实现监控技术详解 0x00 前言 在64位Windows系统中,由于Patch Guard(内核补丁保护)机制的存在,传统的API挂钩技术变得不稳定且容易引发系统崩溃。微软为此提供了系统回调API函数,作为更安全可靠的监控实现方式。 0x01 进程监控与保护 核心API: PsSetCreateProcessNotifyRoutineEx 绕过强制完整性检查 微软要求驱动必须有数字签名才能使用此API,通过检查 IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 标志实现。绕过方法: 回调函数实现 驱动卸载处理 0x02 线程监控与保护 核心API: PsSetCreateThreadNotifyRoutine 回调函数结构 线程保护实现 通过PID找到EPROCESS 通过TID找到ETHREAD 通过EPROCESS得到进程路径 判断进程名是否匹配 修改线程回调函数地址(ETHREAD + 0x410)为ret指令(0xC3) 0x03 模块监控与保护 核心API: PsSetLoadImageNotifyRoutine 回调函数结构 驱动模块卸载 通过修改入口点代码实现: DLL模块卸载 使用未公开API MmUnmapViewOfSection : 实现效果 进程保护:阻止特定进程(如notepad.exe)启动 驱动模块保护:阻止特定驱动(如DriverTest.sys)加载 DLL模块保护:阻止特定DLL(如Test.dll)注入 注意事项 在64位系统下不能直接使用内联汇编,需要单独编译.obj文件 修改内存属性时需要先关闭页保护(ClosePageProtect) 卸载驱动时必须移除回调函数,否则会导致蓝屏 Windows 10及更新版本可能有额外的保护机制需要考虑