windows 驱动实例分析系列: HidHide 驱动分析 - HidHide 篇(四)

发布时间:2026/9/15 9:56:25
windows 驱动实例分析系列: HidHide 驱动分析 - HidHide 篇(四) HidHide 驱动分析 - HidHide 篇四日志系统与数据结构自检一、ETW 日志与追踪系统Logging.c/hHidHide 驱动利用Windows 事件跟踪ETW提供全面的诊断能力。日志系统分为两个独立的提供者日志提供者Logging ProviderNefarius_Hid_Hide记录关键事件如驱动启用/禁用、白名单命中、异常。追踪提供者Tracing ProviderNefarius_Drivers_HidHide记录详细执行路径支持性能分析和深度调试。1.1 提供者注册与注销NTSTATUSLogRegisterProviders(){ntstatusEventRegisterNefarius_Hid_Hide();// 日志提供者ntstatusEventRegisterNefarius_Drivers_HidHide();// 追踪提供者LogEvent(ETW(Started),L%s,_L(BldProductVersion));// 记录启动事件returnSTATUS_SUCCESS;}NTSTATUSLogUnregisterProviders(){LogEvent(ETW(Stopped),L);EventUnregisterNefarius_Drivers_HidHide();EventUnregisterNefarius_Hid_Hide();returnSTATUS_SUCCESS;}_L(BldProductVersion)是编译期注入的版本号每次驱动加载时记录便于版本追溯。1.2 日志消息的写入LogEvent是核心日志函数支持格式化输出NTSTATUSLogEvent(...,PCEVENT_DESCRIPTOR event,PCWSTR format,...){WCHAR buffer[LOGGING_MESSAGE_MAXIMUM_SIZE];va_start(args,format);RtlStringCchVPrintfW(buffer[0],_countof(buffer),format,args);// 写入日志提供者LogWriteTransfer(EtwProviderLogging_Context,event,fileName,lineNumber,functionName,buffer[0],);// 同时写入追踪提供者使用相同消息LogWriteTransfer(EtwProviderTracing_Context,eventDescriptor,fileName,lineNumber,functionName,buffer[0],);returnSTATUS_SUCCESS;}这种一次日志双重输出的策略确保了日志事件同时出现在系统事件日志和跟踪日志中方便管理员从不同视角查看。1.3 追踪宏#defineTRACE_PERFORMANCE(message){TraceEvent(...,EtwEventTracePerformance,message,);}#defineTRACE_ALWAYS(message){TraceEvent(...,EtwEventTraceAlways,message,);}#defineTRACE_DETAILED(message){TraceEvent(...,EtwEventTraceDetailed,message,);}三种追踪级别对应不同的 ETW 事件关键字管理员可通过wevtutil工具动态调整启用级别无需重启驱动。1.4 调试输出与日志的联动TRACE_XXX宏在写入 ETW 的同时若EtwEventTraceDebugging关键字被启用还会调用DbgPrintEx输出到调试器WinDbg / DebugViewif(MCGEN_EVENT_ENABLED(EtwEventTraceDetailed)MCGEN_EVENT_ENABLED(EtwEventTraceDebugging))DbgPrintEx(DPFLTR_IHVDRIVER_ID,DPFLTR_ERROR_LEVEL,%s(%d) %s\n,fileName,lineNumber,functionName);这在驱动开发阶段极其有用开发者无需借助 ETW 查看器即可实时看到执行路径。二、数据结构自检机制2.1 二叉搜索树自检HidHideVerifyInternalConsistency在驱动加载时执行测试 BST 的完整功能ULONG s_testPattern[]{5,11,15,10,8,9,3,4,1,2};NTSTATUSHidHideVerifyInternalConsistency(){// 1. 插入测试序列for(index0;index_countof(s_testPattern);index){BstNewNode(s_testPattern[index],dummy,node);BstInsert(tree,node);// 验证查找成功ntstatus((NULL!BstLookup(tree,s_testPattern[index]))?STATUS_SUCCESS:STATUS_UNSUCCESSFUL);}// 2. 验证左右子树大小BstAddCount(tree-left,count);// 应为 4BstAddCount(tree-right,count);// 应为 5// 3. 删除根节点5BstDelete(tree,5);// 4. 验证删除后左右子树大小BstAddCount(tree-left,count);// 应为 7合并了原右子树的一部分BstAddCount(tree-right,count);// 应为 1// 5. 清理树BstCleanup(tree);returnSTATUS_SUCCESS;}若任何检查失败驱动加载将中止防止运行时出现不可预期的数据损坏。2.2 内存池标签驱动使用统一的内存池标签CONFIG_TAG1gaT和LOGIC_TAG2gaT便于在 Driver Verifier 和 PoolMon 工具中追踪内存泄漏。tempExAllocatePoolZero(NonPagedPool,sizeof(*temp),CONFIG_TAG);使用NonPagedPool确保 BST 节点始终驻留在非分页内存中可在任意 IRQL 安全访问。三、同步机制WDFWAITLOCK驱动使用WdfWaitLock基于FAST_MUTEX的内核锁保护共享数据结构WDFWAITLOCK s_criticalSectionLockNULL;// 获取锁WdfWaitLockAcquire(s_criticalSectionLock,NULL);// 临界区操作BstLookup(...);// 释放锁WdfWaitLockRelease(s_criticalSectionLock);WdfWaitLockAcquire的第二个参数为NULL表示无限等待这简化了错误处理但要求锁持有时间极短避免死锁。所有对 BST 的访问插入、查找、删除、缓存刷新都在锁保护下进行。HidHideProcessIdCheckFullImageNameAgainstWhitelist中虽然持有锁但在遍历集合WdfCollectionGetCount时不持有锁因为集合本身在遍历时不会被修改修改操作会先创建新集合再交换指针。四、设备实例路径获取HidHideDeviceInstancePath通过 WDF 属性 API 获取设备的唯一标识符NTSTATUSHidHideDeviceInstancePath(WDFDEVICE wdfDevice,WDFSTRING*deviceInstancePath){WDF_DEVICE_PROPERTY_DATA_INIT(wdfDevicePropertyData,DEVPKEY_Device_InstanceId);WdfDeviceAllocAndQueryPropertyEx(wdfDevice,wdfDevicePropertyData,NonPagedPool,wdfObjectAttributes,wdfMemory,devPropType);// 复制字符串ntstatusWdfStringCreate(path,wdfObjectAttributes,deviceInstancePath);returnSTATUS_SUCCESS;}设备实例路径的格式为HID\VID_xxxxPID_yyyy\...是 PnP 管理器分配的唯一标识驱动用其在黑名单中精确匹配设备。五、异常处理与错误传播驱动代码广泛使用LOG_AND_RETURN_NTSTATUS宏将错误信息记录到 ETW 后返回 NTSTATUS 码#defineLOG_AND_RETURN_NTSTATUS(message,result){\LogEvent(ETW(Exception),L%s reports NT status 0x%08X,message,result);\return(result);\}这种模式确保了任何 API 失败都会被记录便于事后分析故障根因。六、驱动生命周期完整性总结从DriverEntry到EvtDriverUnload驱动的生命周期管理非常严谨加载阶段注册 ETW → 创建控制设备 → 注册进程/镜像回调 → 自检通过。运行阶段OnDeviceFileCreate拦截请求OnControlDeviceIoSetXxx处理配置变更进程回调维护 BST 缓存。关机阶段OnSystemShutdown注销系统回调刷新缓存减少引用计数。卸载阶段EvtDriverUnload注销 ETW 提供者。这种设计确保了驱动在任何情况下包括异常关机都能尽可能干净地释放资源配合用户态 Watchdog 的注册表清理机制构成了完整的故障自愈闭环。