Windows驱动开发实战:用ObRegisterCallbacks实现进程与文件操作的监控与拦截(含完整代码)

张开发
2026/4/13 22:17:59 15 分钟阅读

分享文章

Windows驱动开发实战:用ObRegisterCallbacks实现进程与文件操作的监控与拦截(含完整代码)
Windows内核安全开发实战基于ObRegisterCallbacks的进程与文件监控体系构建在Windows内核安全领域进程创建监控和文件操作拦截是构建主动防御系统的核心技术。本文将深入探讨如何利用ObRegisterCallbacks机制实现这两大核心功能并分享在实际反作弊系统和终端检测响应(EDR)产品中的实战经验。1. ObRegisterCallbacks机制深度解析ObRegisterCallbacks是Windows内核提供的一种对象管理器回调机制允许驱动程序在进程/线程句柄操作发生时进行拦截。与传统的PsSetCreateProcessNotifyRoutine相比它具有以下显著优势操作前拦截能在句柄创建前进行干预而不仅仅是事后通知细粒度控制可修改访问权限(DesiredAccess)实现精确管控多对象类型支持不仅限于进程还支持文件、注册表等内核对象关键数据结构解析typedef struct _OB_CALLBACK_REGISTRATION { USHORT Version; USHORT OperationRegistrationCount; UNICODE_STRING Altitude; PVOID RegistrationContext; OB_OPERATION_REGISTRATION *OperationRegistration; } OB_CALLBACK_REGISTRATION; typedef struct _OB_OPERATION_REGISTRATION { POBJECT_TYPE *ObjectType; OB_OPERATION Operations; POB_PRE_OPERATION_CALLBACK PreOperation; POB_POST_OPERATION_CALLBACK PostOperation; } OB_OPERATION_REGISTRATION;典型初始化流程NTSTATUS RegisterProcessCallback() { OB_CALLBACK_REGISTRATION obReg; OB_OPERATION_REGISTRATION opReg; RtlZeroMemory(obReg, sizeof(obReg)); obReg.Version ObGetFilterVersion(); obReg.OperationRegistrationCount 1; obReg.RegistrationContext NULL; RtlInitUnicodeString(obReg.Altitude, L100000); RtlZeroMemory(opReg, sizeof(opReg)); opReg.ObjectType PsProcessType; opReg.Operations OB_OPERATION_HANDLE_CREATE; opReg.PreOperation ProcessCallback; PVOID RegistrationHandle; return ObRegisterCallbacks(obReg, RegistrationHandle); }2. 进程监控实战从基础到高级防护2.1 基础进程创建监控最基本的进程监控回调函数实现OB_PREOP_CALLBACK_STATUS ProcessCallback( PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInfo) { if (OperationInfo-Operation OB_OPERATION_HANDLE_CREATE) { PEPROCESS Process (PEPROCESS)OperationInfo-Object; HANDLE ProcessId PsGetProcessId(Process); PCHAR ImageName PsGetProcessImageFileName(Process); DbgPrint(Process handle created: PID%d, Name%s\n, ProcessId, ImageName); } return OB_PREOP_SUCCESS; }2.2 高级访问权限控制在游戏反作弊系统中经常需要限制调试器对游戏进程的访问权限OB_PREOP_CALLBACK_STATUS ProcessCallback(...) { // 检查是否为受保护进程 if (IsProtectedProcess(OperationInfo-Object)) { // 移除危险权限 OperationInfo-Parameters-CreateHandleInformation.DesiredAccess ~(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION); // 特殊情况下完全拒绝访问 if (IsDebuggerProcess(PsGetCurrentProcess())) { return OB_PREOP_DISALLOW; } } return OB_PREOP_SUCCESS; }常见需要限制的进程权限标志访问权限标志风险描述PROCESS_VM_READ允许读取进程内存PROCESS_VM_WRITE允许写入进程内存PROCESS_VM_OPERATION允许操作虚拟内存(VirtualAlloc等)PROCESS_CREATE_THREAD允许创建远程线程PROCESS_SUSPEND_RESUME允许暂停/恢复进程2.3 进程隐藏技术实现通过断链ActiveProcessLinks实现进程隐藏VOID HideProcess(PEPROCESS Process) { KIRQL irql KeRaiseIrqlToDpcLevel(); // 从进程链表中移除目标进程 PLIST_ENTRY prev Process-ActiveProcessLinks.Blink; PLIST_ENTRY next Process-ActiveProcessLinks.Flink; prev-Flink next; next-Blink prev; // 初始化自己的链表指针避免BSOD InitializeListHead(Process-ActiveProcessLinks); KeLowerIrql(irql); }注意现代Windows版本会验证EPROCESS结构体的完整性单纯的断链可能触发PatchGuard。更稳妥的做法是在回调中过滤进程枚举请求。3. 文件操作监控与拦截3.1 文件回调注册文件监控需要先启用文件对象的回调支持BOOLEAN RegisterFileCallback() { // 启用文件对象回调支持 ((POBJECT_TYPE)*IoFileObjectType)-TypeInfo.SupportsObjectCallbacks TRUE; OB_CALLBACK_REGISTRATION obReg {0}; obReg.Version ObGetFilterVersion(); obReg.OperationRegistrationCount 1; RtlInitUnicodeString(obReg.Altitude, L321000); OB_OPERATION_REGISTRATION opReg {0}; opReg.ObjectType IoFileObjectType; opReg.Operations OB_OPERATION_HANDLE_CREATE; opReg.PreOperation FileCallback; return NT_SUCCESS(ObRegisterCallbacks(obReg, gFileCallbackHandle)); }3.2 敏感文件访问拦截防止调试工具访问游戏核心文件OB_PREOP_CALLBACK_STATUS FileCallback(...) { PFILE_OBJECT FileObject OperationInfo-Object; // 验证对象有效性 if (!FileObject-FileName.Buffer || !MmIsAddressValid(FileObject-DeviceObject)) return OB_PREOP_SUCCESS; // 获取DOS设备名 UNICODE_STRING DosName; RtlVolumeDeviceToDosName(FileObject-DeviceObject, DosName); // 检查是否为敏感文件 if (IsSensitiveFile(FileObject-FileName.Buffer)) { // 修改为无效参数使后续操作失败 FileObject-Size 1; OperationInfo-Parameters-CreateHandleInformation.DesiredAccess DELETE; return OB_PREOP_SUCCESS; } return OB_PREOP_SUCCESS; }常见需要保护的文件特征调试器相关*.exe(OllyDbg, x64dbg, CheatEngine等)游戏核心文件*.pak,*.data, 配置文件和DLL反作弊模块反作弊驱动和DLL文件4. 注册表操作监控虽然ObRegisterCallbacks不直接支持注册表但可以结合CmRegisterCallback实现全面防护NTSTATUS RegistryCallback( PVOID CallbackContext, PVOID Argument1, PVOID Argument2) { REG_NOTIFY_CLASS notifyClass (REG_NOTIFY_CLASS)Argument1; switch (notifyClass) { case RegNtPreCreateKeyEx: case RegNtPreOpenKeyEx: { PREG_CREATE_KEY_INFORMATION info (PREG_CREATE_KEY_INFORMATION)Argument2; if (IsProtectedKey(info-RootObject)) { return STATUS_ACCESS_DENIED; } break; } // 处理其他注册表操作类型... } return STATUS_SUCCESS; }关键注册表操作类型操作类型描述RegNtPreCreateKeyEx创建注册表项前RegNtPreOpenKeyEx打开注册表项前RegNtPreQueryValueKey查询键值前RegNtPreSetValueKey设置键值前5. 实战中的问题与解决方案5.1 回调卸载时机不正确的回调卸载会导致系统不稳定。推荐做法VOID DriverUnload(PDRIVER_OBJECT DriverObject) { // 先禁用所有回调功能 g_ShuttingDown TRUE; // 等待所有回调执行完成 LARGE_INTEGER interval; interval.QuadPart -10000 * 1000; // 1秒 KeDelayExecutionThread(KernelMode, FALSE, interval); // 正式卸载回调 if (g_ProcessCallbackHandle) { ObUnRegisterCallbacks(g_ProcessCallbackHandle); } // 其他资源清理... }5.2 性能优化技巧快速路径判断在回调开头进行快速过滤if (!IsGameProcess(PsGetCurrentProcess())) { return OB_PREOP_SUCCESS; }避免阻塞操作回调中严禁使用可能导致阻塞的API缓存常用数据如进程黑白名单可缓存在非分页内存中5.3 对抗逆向分析混淆回调函数通过动态计算函数指针增加分析难度定期变更Altitude防止被特定Altitude的驱动绕过完整性检查监控自身回调是否被恶意移除6. 完整示例代码一个整合进程保护和文件监控的驱动示例#include ntddk.h #include ntstrsafe.h PVOID g_ProcessCallbackHandle NULL; PVOID g_FileCallbackHandle NULL; OB_PREOP_CALLBACK_STATUS ProcessCallback(...) { // 实现见上文 } OB_PREOP_CALLBACK_STATUS FileCallback(...) { // 实现见上文 } NTSTATUS RegisterCallbacks() { // 注册进程回调 OB_CALLBACK_REGISTRATION obReg {0}; obReg.Version ObGetFilterVersion(); obReg.OperationRegistrationCount 1; RtlInitUnicodeString(obReg.Altitude, L100000); OB_OPERATION_REGISTRATION opReg {0}; opReg.ObjectType PsProcessType; opReg.Operations OB_OPERATION_HANDLE_CREATE; opReg.PreOperation ProcessCallback; NTSTATUS status ObRegisterCallbacks(obReg, g_ProcessCallbackHandle); if (!NT_SUCCESS(status)) { return status; } // 注册文件回调 ((POBJECT_TYPE)*IoFileObjectType)-TypeInfo.SupportsObjectCallbacks TRUE; RtlInitUnicodeString(obReg.Altitude, L321000); opReg.ObjectType IoFileObjectType; opReg.PreOperation FileCallback; return ObRegisterCallbacks(obReg, g_FileCallbackHandle); } VOID UnregisterCallbacks() { if (g_ProcessCallbackHandle) { ObUnRegisterCallbacks(g_ProcessCallbackHandle); } if (g_FileCallbackHandle) { ObUnRegisterCallbacks(g_FileCallbackHandle); } } NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { DriverObject-DriverUnload DriverUnload; return RegisterCallbacks(); } VOID DriverUnload(PDRIVER_OBJECT DriverObject) { UnregisterCallbacks(); }在实际产品中我们还需要考虑与用户态组件的通信机制配置的热加载能力完善的日志记录系统对抗恶意卸载的保护措施通过合理运用ObRegisterCallbacks机制开发者可以构建强大的内核级监控系统为安全产品和游戏反作弊提供坚实的技术基础。

更多文章