windows 驱动实例分析系列: HidHide 驱动分析 - drivers 篇(一) 📅 发布时间:2026/9/16 10:20:27 👁 浏览次数: 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 对象类型数量说明WDFDRIVER1驱动对象代表整个驱动程序WDFDEVICEN 1每个 HID 设备对应一个过滤设备对象 一个控制设备对象WDFQUEUE2控制设备的 I/O 队列 默认 I/O 队列WDFREQUEST动态代表每个传入的 I/O 请求WDFWAITLOCK1全局同步锁保护共享数据结构WDFCOLLECTION2白名单集合 黑名单集合每个控制设备上下文WDFSTRINGN每个过滤设备存储其设备实例路径2.1 WDFDRIVER 对象的创建与配置DriverEntry中的WdfDriverCreate调用创建了驱动对象WDF_DRIVER_CONFIG_INIT(wdfDriverConfig,NULL);wdfDriverConfig.EvtDriverDeviceAddHidHideDriverEvtDeviceAdd;wdfDriverConfig.EvtDriverUnloadHidHideDriverEvtUnload;ntstatusWdfDriverCreate(pDriverObject,pRegistryPath,WDF_NO_OBJECT_ATTRIBUTES,wdfDriverConfig,wdfDriver);关键配置解析EvtDriverDeviceAddPnP 管理器为每个新发现的设备调用此回调。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相关联形成设备栈中的一层。两者的创建流程差异显著// 控制设备创建使用 WdfControlDeviceInitAllocatewdfDeviceInitWdfControlDeviceInitAllocate(wdfDriver,SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RWX_RES_RWX);WdfDeviceInitAssignName(wdfDeviceInit,controlDeviceNtDeviceName);WdfDeviceCreate(wdfDeviceInit,wdfObjectAttributes,wdfControlDevice);WdfControlFinishInitializing(*wdfControlDevice);// 必须显式完成初始化// 过滤设备创建使用传入的 wdfDeviceInitWdfFdoInitSetFilter(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.EvtCleanupCallbackOnDeviceContextCleanup;ntstatusWdfDeviceCreate(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.EvtIoDeviceControlHidHideControlDeviceEvtIoDeviceControl;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(FALSEWdfRequestSend(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 秒超时防止下层驱动挂死导致系统卡顿。这是生产级驱动的重要保障措施。