十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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.cppLogEvent格式化并写入事件日志 追踪日志NTSTATUSLogEvent(LPCSTR fileName,UINT32 lineNumber,LPCSTR functionName,PCEVENT_DESCRIPTOR event,LPCWSTR format,...){va_list args;va_start(args,format);std::vectorWCHARmessageW(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::vectorCHARbuffer(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(...);// 获取错误描述osPrefixWIN32 0xstd::hexstd::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{autoconstdecodedDecodeExceptionData();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::exceptionconstexc){std::wcerrexc.what()std::endl;LOGEXC_AND_RETURN_WIN32;}catch(...){std::wcerrLUnhandled exceptionstd::endl;LOGEXC_AND_RETURN_WIN32;}}第一层std::exception捕获所有标准 C 异常包括通过THROW_WIN32抛出的std::runtime_error。第二层...捕获所有非标准异常如结构化异常作为最后的兜底。错误信息同时输出到std::wcerr便于脚本捕获错误输出和 ETW。三、配置管理提交与取消3.1 配置提交ApplyConfigurationChanges交互模式下用户退出时调用ApplyConfigurationChangesm_FilterDriverProxy.ApplyConfigurationChanges();此方法比对缓存与驱动状态仅提交有变更的配置。3.2 取消操作–cancel--cancel命令设置m_Cancel true在Start方法的主循环中检查此标志if(m_Cancel)return;取消后配置变更不会被提交所有修改在退出时丢弃。四、实用工具函数4.1 字符串表加载StringTable(UINT resourceId)从资源文件中加载本地化字符串std::wstringStringTable(UINT stringTableResourceId){autoconsthInstanceGetModuleHandleW(nullptr);std::vectorWCHARbuffer(UNICODE_STRING_MAX_CHARS);if(0LoadStringW(hInstance,stringTableResourceId,buffer.data(),buffer.size())ERROR_SUCCESS!GetLastError())THROW_WIN32_LAST_ERROR;returnbuffer.data();}所有用户可见的文本均通过此函数加载支持国际化。4.2 多字符串转换Windows 注册表REG_MULTI_SZ格式与std::vectorstd::wstring的相互转换std::vectorstd::wstringMultiStringToStringList(std::vectorWCHARconstmultiString){for(size_t index0,start0;indexmultiString.size();index){if(0multiString.at(index)){std::wstringstring(multiString.at(start),0,index-start);if(!string.empty())result.emplace_back(string);startindex1;}}returnresult;}注意处理双空终止符空字符串被跳过!string.empty()判断避免将列表终止符误认为有效条目。4.3 命令行参数提取CommandLineArguments()从完整的命令行字符串中提取参数部分std::wstringCommandLineArguments(){std::wstring commandLineGetCommandLineW();if(LcommandLine.at(0)){autoindexcommandLine.find(L,1);returnTrim(commandLine.erase(0,index1));}else{autoindexcommandLine.find(L );returnTrim(commandLine.erase(0,index));}}先跳过可执行文件路径可能带引号返回剩余的命令行参数。五、CLI 与内核驱动的完整交互链路用户输入命令如--app-reg C:\Tools\mapper.exe命令解释器解析提取命令名app-reg和参数C:\Tools\mapper.exe参数验证ValOneFullyQualifiedExecutablePath检查文件存在性和可执行性路径转换FileNameToFullImageName将C:\Tools\mapper.exe转换为\Device\HarddiskVolume1\Tools\mapper.exe代理调用FilterDriverProxy.WhitelistAddEntry(fullImageName)更新内存缓存提交交互退出时ApplyConfigurationChanges调用DeviceIoControl(IOCTL_SET_WHITELIST)内核处理驱动更新注册表和运行时集合新配置生效反馈输出命令执行结果输出到 stdout/stderr成功返回 ERROR_LEVEL 0失败返回非 0这一完整链路实现了从用户输入到内核生效的全流程自动化使得 HidHideCLI 成为自动化部署和高级管理的理想工具。
返回列表