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);
}
}
线程保护实现
- 通过PID找到EPROCESS
- 通过TID找到ETHREAD
- 通过EPROCESS得到进程路径
- 判断进程名是否匹配
- 修改线程回调函数地址(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;
}
实现效果
- 进程保护:阻止特定进程(如notepad.exe)启动
- 驱动模块保护:阻止特定驱动(如DriverTest.sys)加载
- DLL模块保护:阻止特定DLL(如Test.dll)注入
注意事项
- 在64位系统下不能直接使用内联汇编,需要单独编译.obj文件
- 修改内存属性时需要先关闭页保护(ClosePageProtect)
- 卸载驱动时必须移除回调函数,否则会导致蓝屏
- Windows 10及更新版本可能有额外的保护机制需要考虑