HidHide 驱动分析 - drivers 篇(一):驱动框架与对象模型
一、目录概述
在 HidHide 的源码结构中,drivers/目录实际上与HidHide/目录等同,是内核驱动模块HidHide.sys的完整实现所在。该驱动基于Windows Driver Framework (WDF) Kernel-Mode Driver Framework (KMDF) 1.13+开发,充分利用了 WDF 提供的对象模型、即插即用(PnP)支持和电源管理框架,将传统 WDM 驱动的复杂性大幅降低。
本文档从驱动框架和对象模型的角度,深入剖析 HidHide 如何利用 WDF 机制实现一个稳定、高效的过滤驱动。
二、WDF 对象层次结构
WDF 驱动模型的核心是对象层次结构,每个对象都有明确的生命周期和父子关系。HidHide 使用的关键 WDF 对象包括:
| WDF 对象类型 | 数量 | 说明 |
|---|---|---|
| WDFDRIVER | 1 | 驱动对象,代表整个驱动程序 |
| WDFDEVICE | N + 1 | 每个 HID 设备对应一个过滤设备对象 + 一个控制设备对象 |
| WDFQUEUE | 2 | 控制设备的 I/O 队列 + 默认 I/O 队列 |
| WDFREQUEST | 动态 | 代表每个传入的 I/O 请求 |
| WDFWAITLOCK | 1 | 全局同步锁,保护共享数据结构 |
| WDFCOLLECTION | 2 | 白名单集合 + 黑名单集合(每个控制设备上下文) |
| WDFSTRING | N | 每个过滤设备存储其设备实例路径 |
2.1 WDFDRIVER 对象的创建与配置
DriverEntry中的WdfDriverCreate调用创建了驱动对象:
WDF_DRIVER_CONFIG_INIT(&wdfDriverConfig,NULL);wdfDriverConfig.EvtDriverDeviceAdd=HidHideDriverEvtDeviceAdd;wdfDriverConfig.EvtDriverUnload=HidHideDriverEvtUnload;ntstatus=WdfDriverCreate(pDriverObject,pRegistryPath,WDF_NO_OBJECT_ATTRIBUTES,&wdfDriverConfig,&wdfDriver);关键配置解析:
EvtDriverDeviceAdd:PnP 管理器为每个新发现的设备调用此回调。HidHide 在此回调中创建过滤设备对象(Filter DO),附着在 HID 设备栈上。EvtDriverUnload:驱动卸载时调用,负责释放全局资源。
值得注意的是,WDF_DRIVER_CONFIG_INIT的第二个参数为NULL,这意味着驱动不创建自己的EvtIoDeviceControl默认队列,而是由后续的设备对象独立处理 I/O。
2.2 WDFDEVICE 对象的双重角色
HidHide 创建了两种不同类型的 WDFDEVICE 对象:
控制设备(Control Device):通过WdfControlDeviceInitAllocate创建,不绑定到任何物理设备。它提供用户态通信通道(\\.\HidHide),用于接收 IOCTL 配置请求。该设备在驱动加载时创建,在驱动卸载前销毁。
过滤设备(Filter DO):通过标准的WdfDeviceCreate创建,附着在每个 HID 物理设备之上。每个过滤设备都与一个物理设备对象(PDO)相关联,形成设备栈中的一层。
两者的创建流程差异显著:
// 控制设备创建(使用 WdfControlDeviceInitAllocate)wdfDeviceInit=WdfControlDeviceInitAllocate(wdfDriver,&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX);WdfDeviceInitAssignName(wdfDeviceInit,&controlDeviceNtDeviceName);WdfDeviceCreate(&wdfDeviceInit,&wdfObjectAttributes,&wdfControlDevice);WdfControlFinishInitializing(*wdfControlDevice);// 必须显式完成初始化// 过滤设备创建(使用传入的 wdfDeviceInit)WdfFdoInitSetFilter(wdfDeviceInit);// 声明为过滤驱动WdfDeviceCreate(&wdfDeviceInit,&wdfObjectAttributes,&wdfDevice);// 不需要调用 WdfControlFinishInitializing三、设备上下文的 WDF 对象扩展
WDF 提供了一种优雅的机制来为 WDF 对象附加自定义数据:对象上下文(Object Context)。
3.1 设备上下文的声明与访问
HidHide 定义了两个上下文结构:
// 过滤设备上下文typedefstruct_DEVICE_CONTEXT{WDFSTRING deviceInstancePath;// 设备的唯一实例路径}DEVICE_CONTEXT,*PDEVICE_CONTEXT;// 控制设备上下文typedefstruct_CONTROL_DEVICE_CONTEXT{WDFCOLLECTION whitelistedFullImageNames;// 白名单集合WDFCOLLECTION blacklistedDeviceInstancePaths;// 黑名单集合BOOLEAN active;BOOLEAN whitelistedInverse;BOOLEAN shutdownPending;INT32 numberOfDevicesCreated;}CONTROL_DEVICE_CONTEXT,*PCONTROL_DEVICE_CONTEXT;使用WDF_DECLARE_CONTEXT_TYPE_WITH_NAME宏生成类型安全的访问函数:
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT,DeviceGetContext)WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(CONTROL_DEVICE_CONTEXT,ControlDeviceGetContext)这允许通过DeviceGetContext(wdfDevice)直接获取上下文指针,而无需手动进行类型转换和内存偏移计算。
3.2 上下文的分配与初始化
上下文在WdfDeviceCreate时通过WDF_OBJECT_ATTRIBUTES指定:
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&wdfObjectAttributes,DEVICE_CONTEXT);wdfObjectAttributes.EvtCleanupCallback=OnDeviceContextCleanup;ntstatus=WdfDeviceCreate(&wdfDeviceInit,&wdfObjectAttributes,&wdfDevice);WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE宏自动计算所需上下文大小,并在对象分配时预留内存。EvtCleanupCallback在设备对象销毁时被调用,用于释放上下文中的资源(如WDFSTRING对象)。
这种设计让对象生命周期的内存管理完全由 WDF 框架托管,避免了手动ExAllocatePool和ExFreePool带来的复杂性和错误风险。
四、I/O 队列的配置与分发
WDF 通过I/O 队列(WDFQUEUE)管理传入的 I/O 请求。HidHide 为控制设备和过滤设备分别配置了独立的队列。
4.1 控制设备的 I/O 队列
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&wdfIoQueueConfig,WdfIoQueueDispatchParallel);wdfIoQueueConfig.EvtIoDeviceControl=HidHideControlDeviceEvtIoDeviceControl;WdfIoQueueCreate(*wdfControlDevice,&wdfIoQueueConfig,WDF_NO_OBJECT_ATTRIBUTES,&wdfQueue);WdfIoQueueDispatchParallel:请求并行处理,不排队等待。控制设备的请求量很小(仅配置操作),并行处理可最大化响应速度。EvtIoDeviceControl:仅处理IRP_MJ_DEVICE_CONTROL类型请求,其他类型请求(如读写)被自动拒绝。
4.2 过滤设备的默认队列
过滤设备未显式创建 I/O 队列,而是使用 WDF 框架为设备自动创建的默认队列。在OnDeviceFileCreate中,请求被处理的方式如下:
- 请求到达默认队列,框架调用
EvtIoDefault回调(如果注册)或EvtIoRead/EvtIoWrite等特定回调。 - HidHide 使用
WdfDeviceInitSetFileObjectConfig注册了EvtDeviceFileCreate回调,该回调在文件对象创建时触发,对应IRP_MJ_CREATE请求。 - 对于非 CREATE 请求,驱动不感兴趣,直接传递给下层驱动。
WDF_FILEOBJECT_CONFIG_INIT(&wdfFileObjectConfig,OnDeviceFileCreate,NULL,OnDeviceFileCleanup);WdfDeviceInitSetFileObjectConfig(wdfDeviceInit,&wdfFileObjectConfig,&wdfObjectAttributes);这种选择性拦截策略大大简化了驱动逻辑,只关心设备打开请求,对数据传输请求完全不干预。
五、请求转发与完成例程
作为过滤驱动,HidHide 对大多数请求不做实质处理,而是转发给下层驱动。
5.1 转发的标准模式
WdfRequestFormatRequestUsingCurrentType(wdfRequest);WdfRequestSetCompletionRoutine(wdfRequest,OnDeviceFileCreateRequestCompletion,WDF_NO_CONTEXT);WDF_REQUEST_SEND_OPTIONS_INIT(&wdfRequestSendOptions,WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&wdfRequestSendOptions,WDF_REL_TIMEOUT_IN_SEC(60));if(FALSE==WdfRequestSend(wdfRequest,WdfDeviceGetIoTarget(wdfDevice),&wdfRequestSendOptions)){WdfRequestComplete(wdfRequest,WdfRequestGetStatus(wdfRequest));}步骤说明:
WdfRequestFormatRequestUsingCurrentType:保持请求类型不变,准备转发。WdfRequestSetCompletionRoutine:注册完成例程,在下层驱动处理完毕后被调用。WDF_REQUEST_SEND_OPTIONS_INIT:指定同步发送(WDF_REQUEST_SEND_OPTION_SYNCHRONOUS),即等待下层驱动完成。WdfRequestSend:将请求发送到 I/O 目标(WdfDeviceGetIoTarget获取的下层驱动)。- 若发送失败,立即完成请求(
WdfRequestComplete)。
5.2 完成例程
VOIDOnDeviceFileCreateRequestCompletion(WDFREQUEST wdfRequest,WDFIOTARGET wdfIoTarget,PWDF_REQUEST_COMPLETION_PARAMS wdfRequestCompletionParams,WDFCONTEXT wdfContext){WdfRequestComplete(wdfRequest,wdfRequestCompletionParams->IoStatus.Status);}完成例程仅将下层驱动的返回状态向上传递,不进行额外处理。
5.3 超时机制
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&wdfRequestSendOptions,WDF_REL_TIMEOUT_IN_SEC(60));60 秒超时防止下层驱动挂死导致系统卡顿。这是生产级驱动的重要保障措施。