windows 驱动实例分析系列: HidHide 驱动分析 - drivers 篇(二)
2026/9/16 10:20:09 网站建设 项目流程

HidHide 驱动分析 - drivers 篇(二):同步与并发控制

一、并发场景分析

内核驱动在运行过程中面临多种并发挑战:

并发来源说明影响
多处理器系统不同 CPU 核心可同时执行驱动代码共享数据结构需要同步保护
IRQL 提升代码在高 IRQL 被中断,可能被重新调度锁的选择需匹配 IRQL 级别
异步回调进程/镜像回调在任意线程上下文触发回调函数必须可重入
多线程应用多个用户态线程同时访问控制设备IOCTL 处理需要序列化

HidHide 通过组合使用多种同步机制应对这些挑战。

二、WDFWAITLOCK:全局同步锁

2.1 锁的创建与类型

WdfWaitLockCreate(&wdfObjectAttributes,&s_criticalSectionLock);

WdfWaitLock在 WDF 内部实现为快速互斥体(Fast Mutex),具有以下特性:

  • 可以在 PASSIVE_LEVEL IRQL 获取和释放。
  • 支持递归获取(同一线程可多次获取而不会死锁)。
  • 获取锁时若被其他线程持有,当前线程进入等待状态(不会自旋)。

2.2 锁保护的关键数据

所有共享数据结构都在s_criticalSectionLock保护之下:

  • BST(进程-路径映射树):插入、查找、删除、缓存刷新操作。
  • 控制设备上下文:白名单集合、黑名单集合的原子替换。
  • 引用计数numberOfDevicesCreated的增减。

2.3 加锁模式与粒度

HidHide 采用粗粒度锁策略,整个驱动只有一个全局锁。理由如下:

  1. 并发度低:BST 操作发生在进程创建/终止时,频率远低于 I/O 请求。
  2. 实现简单:避免复杂的多锁嵌套和死锁分析。
  3. 锁持有时间短:所有临界区操作(查找、比较、指针交换)都在微秒级完成。

加锁示例(HidHideProcessIdRegister):

WdfWaitLockAcquire(wdfWaitLock,NULL);node=BstLookup(s_ProcessIdToFullLoadImageNameMappingTree,processId);if(NULL!=node){WdfWaitLockRelease(wdfWaitLock);returnSTATUS_PROCESS_IN_JOB;}ntstatus=BstNewNode(processId,fullImageName,&node);ntstatus=BstInsert(&s_ProcessIdToFullLoadImageNameMappingTree,node);WdfWaitLockRelease(wdfWaitLock);

锁在函数入口获取,在函数出口释放,最小化了锁持有时间。

三、IRQL 管理与锁兼容性

3.1 不同回调函数的 IRQL 级别

回调函数IRQL 级别说明
DriverEntryPASSIVE_LEVEL驱动加载阶段
EvtDriverDeviceAddPASSIVE_LEVELPnP 设备枚举
OnDeviceFileCreatePASSIVE_LEVEL用户态文件创建请求
OnSystemLoadImagePASSIVE_LEVEL镜像加载通知(APC 级别)
OnSystemProcessChangePASSIVE_LEVEL进程创建/终止通知
OnControlDeviceIoDeviceControlDISPATCH_LEVELIOCTL 处理(可达 DISPATCH_LEVEL)

所有回调都运行在 PASSIVE_LEVEL 或更低,因此WdfWaitLock(需要 PASSIVE_LEVEL)在所有上下文中都是安全的。

3.2 避免在提升 IRQL 时持有锁

OnControlDeviceIoDeviceControl虽然声明为_IRQL_requires_max_(DISPATCH_LEVEL),但在实际执行中,WDF 默认队列将请求从 DISPATCH_LEVEL 降级到 PASSIVE_LEVEL 处理。因此,持有WdfWaitLock是安全的。

驱动文档明确标注了 IRQL 要求:

_IRQL_requires_same__IRQL_requires_max_(PASSIVE_LEVEL)NTSTATUSHidHideProcessIdRegister(...);

这提醒调用者必须在 PASSIVE_LEVEL 调用此函数。

四、WDF 自动同步机制

4.1 设备对象的同步范围

wdfObjectAttributes.SynchronizationScope=WdfSynchronizationScopeNone;

HidHide 的过滤设备明确设置WdfSynchronizationScopeNone,即 WDF 框架不为该设备提供自动同步保护。决策依据是:

  1. 性能优先:自动同步会序列化所有请求,影响 I/O 吞吐量。
  2. 手动保护充分OnDeviceFileCreate只读取共享数据(白名单/黑名单),且读取操作使用原子指针交换,无需额外锁。

4.2 控制设备的独占模式

WdfDeviceInitSetExclusive(wdfDeviceInit,TRUE);

控制设备设置为独占模式,确保同一时刻只有一个用户态进程可以打开\\.\HidHide。这避免了以下竞态:

  • 两个配置工具同时修改白名单,导致数据不一致。
  • 在配置更新过程中,另一个进程读取到半完成状态。

五、原子指针交换:无锁读取

5.1 双缓冲技术的核心

配置更新的核心操作是对集合指针的原子替换:

WdfWaitLockAcquire(s_criticalSectionLock,NULL);WdfObjectDelete(pControlDeviceContext->whitelistedFullImageNames);pControlDeviceContext->whitelistedFullImageNames=newCollection;WdfWaitLockRelease(s_criticalSectionLock);

关键设计:指针赋值在 x64 上是原子操作。因此,当读取线程(OnDeviceFileCreate)访问whitelistedFullImageNames时,它总是看到完整的旧集合完整的新集合,而不会看到中间状态。

5.2 读取端的无锁访问

// 在 OnDeviceFileCreate 中BOOLEANWhitelisted(HANDLE processId,BOOLEAN*cacheHit){pControlDeviceContext=ControlDeviceGetContext(s_wdfControlDevice);// 直接读取指针,无需加锁BOOLEAN result=HidHideProcessIdCheckFullImageNameAgainstWhitelist(s_criticalSectionLock,processId,pControlDeviceContext->whitelistedFullImageNames,cacheHit);returnGetInverse()?!result:result;}

读取时未持有s_criticalSectionLock,但仍安全,因为:

  1. 指针赋值是原子的。
  2. 旧的集合对象在指针交换后不会被立即释放(通过WdfObjectDelete延迟释放)。
  3. WDFCOLLECTION对象在删除时引用计数为零才会真正释放。

这种设计实现了读操作无锁,对高频 I/O 路径影响极小。

六、进程回调的上下文隔离

6.1 系统线程上下文

OnSystemLoadImageOnSystemProcessChange运行在系统进程上下文中,而非调用者进程上下文。这意味着:

  • 不能使用PsGetCurrentProcessId()获取调用者 PID,因为当前进程是 System。
  • 回调参数明确提供了HANDLE processId,必须使用此值。

6.2 安全的数据传递

BST 节点存储的fullImageName字符串在插入时被完整复制到非分页池中:

ntstatus=RtlStringCchCopyUnicodeStringEx(&temp->fullImageName[0],_countof(temp->fullImageName),fullImageName,...);

这确保了在回调函数返回后,字符串数据依然有效,不受栈空间释放的影响。

七、自检与防御性编程

7.1 启动时 BST 自检

HidHideVerifyInternalConsistency在驱动启动时执行完整 BST 验证。这种Fail-Fast策略确保:

  • 若 BST 算法实现存在缺陷,驱动在加载阶段即失败,而非在运行时触发蓝屏。
  • 为后续的进程追踪功能提供正确性保证。

7.2 断言与验证

虽然代码中未直接使用ASSERT宏,但通过NT_SUCCESS检查和LOG_AND_RETURN_NTSTATUS实现了相似效果:

if(!NT_SUCCESS(ntstatus)){LOG_AND_RETURN_NTSTATUS(L"WdfDriverOpenParametersRegistryKey",ntstatus);}

所有 API 调用的返回值都被检查,失败时记录详细错误信息并返回。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询