news 2026/9/12 18:18:39

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
windows 驱动实例分析系列: HidHide驱动分析-HidHideCLI 篇(四)

HidHide 驱动分析 - HidHideCLI 篇(四):日志系统与异常处理

一、日志系统的分层架构

HidHideCLI 的日志系统与内核驱动共享相同的 ETW 提供者结构,但在用户态实现了独立的日志后端。日志系统分为三个层次:

1.1 宏定义层(Logging.h)

// 三种追踪级别#defineTRACE_DETAILED(message){TraceEvent(...,&EtwEventTraceDetailed,message,"");...}#defineTRACE_PERFORMANCE(message){TraceEvent(...,&EtwEventTracePerformance,message,"");...}#defineTRACE_ALWAYS(message){TraceEvent(...,&EtwEventTraceAlways,message,"");...}// 日志事件宏(简化参数传递)#defineETW(eventDescriptor)&__FILE__[ProjectDirLength],__LINE__,__FUNCTION__,&EtwEventLog##eventDescriptor// 日志与返回宏#defineLOG_AND_RETURN_NTSTATUS(message,result){LogEvent(ETW(Exception),L"%s reports NT status 0x%08X",message,result);return(result);}

ProjectDirLength是一个编译期定义的常量,用于裁剪__FILE__中的绝对路径前缀,使日志输出更简洁。

1.2 核心日志函数(Logging.cpp)

LogEvent:格式化并写入事件日志 + 追踪日志

NTSTATUSLogEvent(LPCSTR fileName,UINT32 lineNumber,LPCSTR functionName,PCEVENT_DESCRIPTOR event,LPCWSTR format,...){va_list args;va_start(args,format);std::vector<WCHAR>messageW(LOGGING_MESSAGE_MAXIMUM_SIZE);std::vswprintf(messageW.data(),messageW.size(),format,args);UpdateEventLog(false,fileName,lineNumber,functionName,event,messageW.data(),"");returnSTATUS_SUCCESS;}

UpdateEventLog函数封装了EventWriteTransfer调用,将日志事件同时写入日志提供者和追踪提供者。

TraceEvent:仅写入追踪日志(不写入事件日志),用于高性能场景:

NTSTATUSTraceEvent(LPCSTR fileName,UINT32 lineNumber,LPCSTR functionName,PCEVENT_DESCRIPTOR event,LPCWSTR messageW,LPCSTR messageA){UpdateEventLog(true,fileName,lineNumber,functionName,event,messageW,messageA);returnSTATUS_SUCCESS;}

DbgPrintEx 模拟:在用户态模拟内核态DbgPrintEx行为,输出到调试器:

NTSTATUSDbgPrintEx(ULONG componentId,ULONG level,LPCSTR format,...){va_list args;va_start(args,format);std::vector<CHAR>buffer(LOGGING_MESSAGE_MAXIMUM_SIZE);std::vsnprintf(buffer.data(),buffer.size(),format,args);OutputDebugStringA(buffer.data());returnSTATUS_SUCCESS;}

1.3 ETW 提供者注册/注销

NTSTATUS WINAPILogRegisterProviders()noexcept{EventRegisterNefarius_Hid_Hide_CLI();EventRegisterNefarius_Drivers_HidHideCLI();LogEvent(ETW(Started),L"%s",_L(BldProductVersion));returnSTATUS_SUCCESS;}

两个提供者分别对应"日志"(Logging)和"追踪"(Tracing),方便管理员根据需要启用不同级别的诊断信息。

二、异常处理框架

2.1 异常编码与传递(LogException 类)

LogException是一个辅助类,用于将不同来源的错误码(CONFIGRET、HRESULT、NTSTATUS、WIN32)统一编码为字符串,便于日志记录和异常传递。

编码函数

staticstd::stringEncodeWIN32(LPCSTR fileName,UINT32 lineNumber,LPCSTR functionName,PCEVENT_DESCRIPTOR event,DWORD result)noexcept{std::ostringstream os;FormatMessageA(...);// 获取错误描述os<<PrefixWIN32<<" 0x"<<std::hex<<std::setw(8)<<result<<" at "<<fileName<<"("<<lineNumber<<") "<<functionName<<": "<<buffer.data();returnos.str();}

输出格式示例:Error code 0x00000002 at HID.cpp(123) HidModelInfo: The system cannot find the file specified.

解码与转换

CONFIGRETLogException::ToCONFIGRET()constnoexcept{autoconstdecoded=DecodeExceptionData();switch(decoded.encoding){caseEncoding::configret:returndecoded.configret;caseEncoding::hresult:returnSUCCEEDED(decoded.hresult)?CR_SUCCESS:CR_FAILURE;// ...}}

支持在异常处理中将错误码灵活转换为所需类型。

2.2 异常抛出宏

#defineTHROW_WIN32(result){throwstd::runtime_error(LogException::EncodeWIN32(ETW(Exception),result));}#defineTHROW_WIN32_LAST_ERROR{throwstd::runtime_error(LogException::EncodeWIN32(ETW(Exception),GetLastError()));}#defineTHROW_CONFIGRET(result){throwstd::runtime_error(LogException::EncodeCONFIGRET(ETW(Exception),result));}

所有 Windows API 调用失败时,通过宏抛出包含文件名、行号和错误描述的std::runtime_error

2.3 异常捕获与日志记录宏

#defineLOGEXC_AND_RETURN_WIN32{\LogException(LogException::ExceptionMessage()).Log(ETW(Exception)).ToWIN32();\}

catch块中使用,将异常信息记录到 ETW,然后返回对应的 WIN32 错误码。

2.4 顶层异常处理

MainApplication函数使用三层异常捕获:

DWORDMainApplication()noexcept{try{HidHide::CommandInterpreter(false).Start(HidHide::CommandLineArguments());returnERROR_SUCCESS;}catch(std::exceptionconst&exc){std::wcerr<<exc.what()<<std::endl;LOGEXC_AND_RETURN_WIN32;}catch(...){std::wcerr<<L"Unhandled exception"<<std::endl;LOGEXC_AND_RETURN_WIN32;}}
  • 第一层:std::exception捕获所有标准 C++ 异常,包括通过THROW_WIN32抛出的std::runtime_error
  • 第二层:...捕获所有非标准异常(如结构化异常),作为最后的兜底。
  • 错误信息同时输出到std::wcerr(便于脚本捕获错误输出)和 ETW。

三、配置管理:提交与取消

3.1 配置提交(ApplyConfigurationChanges)

交互模式下,用户退出时调用ApplyConfigurationChanges

m_FilterDriverProxy.ApplyConfigurationChanges();

此方法比对缓存与驱动状态,仅提交有变更的配置。

3.2 取消操作(–cancel)

--cancel命令设置m_Cancel = true,在Start方法的主循环中检查此标志:

if(m_Cancel)return;

取消后配置变更不会被提交,所有修改在退出时丢弃。

四、实用工具函数

4.1 字符串表加载

StringTable(UINT resourceId)从资源文件中加载本地化字符串:

std::wstringStringTable(UINT stringTableResourceId){autoconsthInstance=GetModuleHandleW(nullptr);std::vector<WCHAR>buffer(UNICODE_STRING_MAX_CHARS);if(0==LoadStringW(hInstance,stringTableResourceId,buffer.data(),buffer.size())&&ERROR_SUCCESS!=GetLastError())THROW_WIN32_LAST_ERROR;returnbuffer.data();}

所有用户可见的文本均通过此函数加载,支持国际化。

4.2 多字符串转换

Windows 注册表REG_MULTI_SZ格式与std::vector<std::wstring>的相互转换:

std::vector<std::wstring>MultiStringToStringList(std::vector<WCHAR>const&multiString){for(size_t index=0,start=0;index<multiString.size();index++){if(0==multiString.at(index)){std::wstringstring(&multiString.at(start),0,index-start);if(!string.empty())result.emplace_back(string);start=index+1;}}returnresult;}

注意处理双空终止符:空字符串被跳过(!string.empty()判断),避免将列表终止符误认为有效条目。

4.3 命令行参数提取

CommandLineArguments()从完整的命令行字符串中提取参数部分:

std::wstringCommandLineArguments(){std::wstring commandLine=GetCommandLineW();if(L'"'==commandLine.at(0)){autoindex=commandLine.find(L'"',1);returnTrim(commandLine.erase(0,index+1));}else{autoindex=commandLine.find(L' ');returnTrim(commandLine.erase(0,index));}}

先跳过可执行文件路径(可能带引号),返回剩余的命令行参数。

五、CLI 与内核驱动的完整交互链路

  1. 用户输入命令(如--app-reg "C:\Tools\mapper.exe"
  2. 命令解释器解析:提取命令名app-reg和参数"C:\Tools\mapper.exe"
  3. 参数验证ValOneFullyQualifiedExecutablePath检查文件存在性和可执行性
  4. 路径转换FileNameToFullImageNameC:\Tools\mapper.exe转换为\Device\HarddiskVolume1\Tools\mapper.exe
  5. 代理调用FilterDriverProxy.WhitelistAddEntry(fullImageName)更新内存缓存
  6. 提交(交互退出时)ApplyConfigurationChanges调用DeviceIoControl(IOCTL_SET_WHITELIST)
  7. 内核处理:驱动更新注册表和运行时集合,新配置生效
  8. 反馈输出:命令执行结果输出到 stdout/stderr,成功返回 ERROR_LEVEL 0,失败返回非 0

这一完整链路实现了从用户输入到内核生效的全流程自动化,使得 HidHideCLI 成为自动化部署和高级管理的理想工具。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/12 18:16:41

OPA衍生化原理详解:从氨基酸分析到蛋白定量的高灵敏度荧光方案

做蛋白定量&#xff0c;实验室里第一反应多半是BCA或者考马斯亮蓝&#xff1b;做氨基酸分析&#xff0c;很多老方法会用到茚三酮。但有一类场景&#xff0c;这两套常用方案都不太顺手——样品浓度低到微克级以下、样品是多肽水解物、又或者你需要把二十种氨基酸一次性搞定。这时…

作者头像 李华
网站建设 2026/9/12 18:12:53

AI智能问卷设计:技术架构与效率提升解析

1. 传统问卷设计的痛点与瓶颈 传统问卷设计流程通常包含需求分析、问题设计、格式编排、测试调整和分发回收五个阶段。以某市场调研公司2023年的内部统计为例&#xff0c;从问卷立项到最终回收数据平均需要21个工作日&#xff0c;其中仅问题设计环节就占用了37%的时间成本。这种…

作者头像 李华
网站建设 2026/9/12 18:12:20

ESP32-S3 N16R8开发实战:PSRAM内存布局与UVC+Micro-ROS工程落地

1. 为什么选ESP32-S3 N16R8&#xff1f;不是参数堆砌&#xff0c;而是真实开发场景的硬需求 刚拿到这块板子时&#xff0c;我把它放在桌上看了足足十分钟——不是因为惊艳&#xff0c;而是因为困惑。市面上标着“ESP32-S3”的开发板少说二十种&#xff0c;为什么偏偏是N16R8这个…

作者头像 李华