ObRegisterCallbacks可用于在进程、线程等内核对象操作(如OpenProcess、DuplicateHandle)前拦截并过滤权限,例如清除PROCESS_TERMINATE位以阻止终止指定进程,需注册回调结构体并设置PreOperation函数,在其中检查PID、访问权限及调用方上下文后返回STATUS_ACCESS_DENIED实现防护。

ObRegisterCallbacks 在 Windows 驱动中能做什么
ObRegisterCallbacks 是 Windows 内核提供的对象回调注册机制,允许驱动在进程、线程、句柄等内核对象被创建、关闭或复制时插入自定义逻辑。它不是“防打开”的银弹,而是提供一个拦截点:当用户态调用 OpenProcess、DuplicateHandle 或内核中类似 ZwOpenProcess 时,若目标是进程对象(*ProcessObject),且操作类型为 OB_OPEN_HANDLE_OPERATION,你的回调函数会被触发,此时可检查调用方权限、PID、签名或上下文,并通过返回 STATUS_ACCESS_DENIED 拒绝操作。
注意:该接口仅在 Windows Vista 及以后启用,且要求驱动以 SE_DEBUG_PRIVILEGE 权限运行(通常由系统授予),同时必须通过 SeAssignPrimaryTokenPrivilege 或启动时加载策略绕过部分限制——但实际中,普通驱动无法绕过 PatchGuard 对关键结构的保护,更不能直接 patch PsProcessType 或挂钩 ZwOpenProcess。
注册 ObRegisterCallbacks 的最小可行步骤
要让回调真正生效,不能只调用注册函数。常见失败原因是没填全结构体字段、没正确设置回调掩码、或在错误的 IRQL 级别调用:
- 必须初始化
OBCALLBACK_REGISTRATION结构体,其中:-
Version设为OB_FLT_REGISTRATION_VERSION(Windows 10 20H1+ 推荐用OB_FLT_REGISTRATION_VERSION_V2) -
OperationRegistrationCount至少为 1,且对应数组首项的ObjectType必须设为*PsProcessType(不是字符串名,是全局导出变量地址) -
PreOperation和/或PostOperation回调函数需声明为POB_PRE_OPERATION_CALLBACK类型,且不能访问分页内存(即所有代码和数据必须位于非分页池)
-
示例关键片段(不带错误检查):
立即学习“C++免费学习笔记(深入)”;
OB_CALLBACK_REGISTRATION reg = {0};
reg.Version = OB_FLT_REGISTRATION_VERSION_V2;
reg.OperationRegistrationCount = 1;
reg.RegistrationContext = NULL;
<p>OB_OPERATION_REGISTRATION ops[1] = {0};
ops[0].ObjectType = *PsProcessType;
ops[0].Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
ops[0].PreOperation = MyPreHandleOperation;
reg.OperationRegistration = ops;</p><p>NTSTATUS status = ObRegisterCallbacks(®, &g_CallbackHandle);</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill9022" title="Windows-Android_SSH_Remote_Control"><img
src="https://img.php.cn/upload/skill/000/000/081/179238424271266.jpg" alt="Windows-Android_SSH_Remote_Control" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill9022" title="Windows-Android_SSH_Remote_Control">Windows-Android_SSH_Remote_Control</a>
<p>通过 SSH 隧道从 Android 设备在互联网上对 Windows PC 实现完整远程控制的设置与使用指南,适用于远程桌面访问、安全 RDP 等。</p>
</div>
<a href="/xiazai/skill9022" title="Windows-Android_SSH_Remote_Control" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div>MyPreHandleOperation 中如何判断“非法打开”
MyPreHandleOperation 接收 POB_PRE_OPERATION_INFORMATION 参数,核心字段是:
-
Object:指向EPROCESS结构,可用PsGetProcessId获取 PID -
OperationInformation->KernelHandle:是否为内核模式请求(TRUE 表示来自驱动,通常放行) -
OperationInformation->SubjectSecurityContext:包含调用方的ACCESS_MASK(如PROCESS_QUERY_INFORMATION)、源进程 PID、会话 ID,但不直接暴露调用方进程名或路径
常见误判点:
- 仅靠 PID 黑名单容易被绕过(攻击者 fork 后立即 exec)
- 直接比对
CurrentThread->ApcState.Process获取调用方 EPROCESS 不可靠(可能为系统线程或 APC 上下文) - 使用
PsLookupProcessByProcessId可能引发死锁或 STATUS_INVALID_CID(目标进程已退出)
较稳妥做法是组合判断:
- 若
KernelHandle == FALSE且OperationInformation->ObjectInformation->HandleAttributes & OBJ_KERNEL_HANDLE为假,则为用户态请求 - 检查
OperationInformation->SecurityDescriptor是否为空(空 SD 表示默认 DACL,风险高) - 用
SeSinglePrivilegeCheck验证调用方是否有SE_DEBUG_PRIVILEGE(有则大概率是恶意工具) - 对特定 PID(如你保护的进程)且操作
DesiredAccess包含PROCESS_VM_READ或PROCESS_DUP_HANDLE时,返回STATUS_ACCESS_DENIED
为什么驱动加载后回调没触发或蓝屏
最常踩的坑不是逻辑错,而是生命周期和内存模型问题:
-
ObRegisterCallbacks注册的回调函数及其引用的所有数据(包括字符串常量、全局变量)必须驻留在非分页池。用ExAllocatePoolWithTag(NonPagedPoolNx, ...)分配,不能用栈变量或分页池 - 卸载驱动前必须调用
ObUnregisterCallbacks(g_CallbackHandle),否则系统重启前无法再次注册同类型回调,且可能导致后续蓝屏(CRITICAL_STRUCTURE_CORRUPTION) -
PsProcessType地址在不同 Windows 版本/更新中变化,不能硬编码。应通过MmGetSystemRoutineAddress查找PsGetProcessId后反推,或使用符号解析(DbgHelp + PDB)——但驱动中不可用 DbgHelp - Windows 10 1903+ 引入 Kernel Data Protection(KDP),对
PsProcessType等关键指针启用写保护,直接解引用可能触发ATTEMPTED_WRITE_TO_READONLY_MEMORY
真正稳定的方案往往要配合 ETW 事件过滤、Minifilter 配合用户态守护进程做二次决策,而不是单靠 ObRegisterCallbacks 做全部防护。它只是第一道门禁,不是整面墙。


















