ETW(Event Tracing for Windows)
概述
ETW
是Windows
提供的高性能内核级事件追踪机制,能够以极低开销(通常<5% CPU
)实时捕获系统和应用程序事件
ETW架构核心组件
事件生产者
- 内核级
- 系统组件(如进程管理器、TCP/IP协议栈)
- 用户级
- 通过
EventRegister
注册的应用程序
- 通过
1 2 3 |
// 注册ETW Provider REGHANDLE hProvider; EventRegister(&MyProviderId, nullptr, nullptr, &hProvider); |
控制器
- 使用
logman
或StartTrace
API管理会话
1 |
logman create trace "MySession" -p {MyProviderId} -o mytrace.etl |
消费者
- 实时处理(
OpenTrace
+ProcessTrace
)
1 2 3 |
EVENT_TRACE_LOGFILE log = {0}; log.LogFileName = NULL; // 实时模式 log.EventRecordCallback = EventCallback; |
事件数据生命周期
事件生成
1 2 3 4 5 6 7 8 9 10 |
// 用户模式事件写入 EVENT_DESCRIPTOR eventDesc = {0x1, 0x0, 0x0, 0x4, 0xFF, 0xFF}; EventWrite(hProvider, &eventDesc, 0, NULL); // 内核模式事件(如进程创建) EtwWriteKernelEvent( &ProcessEventGuid, EVENT_TRACE_FLAG_PROCESS, ProcessStartOpcode, pid, exePath); |
缓冲机制
- 每
CPU
核心独立缓冲区:避免锁竞争 flush
策略- 缓冲区满(默认
32KB
) - 定时器触发(可配置最低
1ms
) - 显式调用
EtwFlush
- 缓冲区满(默认
- 数据传输
DMA
直接内存访问:内核到用户态零拷贝- 事件批处理:单次系统调用传输多个事件
进程监控实现详解
启用内核进程事件
1 2 3 4 5 6 |
// 配置会话属性 EVENT_TRACE_PROPERTIES* props = ...; props->EnableFlags = EVENT_TRACE_FLAG_PROCESS; // 启动ETW会话 StartTrace(&hSession, L"ProcMonSession", props); |
事件解析关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
void ProcessEvent(PEVENT_RECORD event) { // 获取元数据 EVENT_HEADER* header = event->EventHeader; // 根据Opcode区分事件类型 switch(header->EventDescriptor.Opcode) { case PROCESS_START_OPCODE: // 1 ParseProcessStart(event); break; case PROCESS_END_OPCODE: // 2 ParseProcessEnd(event); break; } } void ParseProcessStart(PEVENT_RECORD event) { // 使用TDH解析事件数据 TDH_CONTEXT context[2] = {0}; PROPERTY_DATA_DESCRIPTOR desc[2]; // 提取PID desc[0].PropertyName = (ULONGLONG)L"ProcessId"; TdhGetProperty(event, 0, NULL, 1, &desc[0], sizeof(DWORD), (PBYTE)&pid); // 提取映像路径 desc[1].PropertyName = (ULONGLONG)L"ImageName"; TdhGetProperty(event, 0, NULL, 1, &desc[1], MAX_PATH*2, (PBYTE)path); // 转换设备路径为DOS路径 ConvertDevicePathToDosPath(path); } |
路径转换算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void ConvertDevicePathToDosPath(WCHAR* devicePath) { WCHAR driveLetters[26]; QueryDosDeviceW(NULL, driveLetters, sizeof(driveLetters)); for(WCHAR* p = driveLetters; *p; ) { WCHAR symLink[50]; wcscpy(symLink, L"\\??\\"); wcscat(symLink, p); // 构造符号链接名 if(wcsstr(devicePath, symLink)) { // 替换设备路径为盘符 WCHAR dosPath[MAX_PATH]; swprintf(dosPath, L"%c:%s", p[0], devicePath + wcslen(symLink)); wcscpy(devicePath, dosPath); break; } p += wcslen(p) + 1; // 移动到下一个设备 } } |
性能优化技巧
缓冲区调优
1 2 3 4 |
// 设置每CPU缓冲区数量和大小 props->BufferSize = 64; // KB props->MinimumBuffers = 32; props->MaximumBuffers = 1024; |
过滤无用事件
1 2 3 4 5 6 7 8 9 10 11 12 |
// 启用事件过滤 ENABLE_TRACE_PARAMETERS params = {0}; params.Version = ENABLE_TRACE_PARAMETERS_VERSION_2; params.EnableFilterDesc = &filterDesc; params.FilterDescCount = 1; EVENT_FILTER_DESCRIPTOR filterDesc = {0}; filterDesc.Ptr = (ULONGLONG)L"ProcessId>=1000"; // 过滤系统进程 filterDesc.Size = sizeof(WCHAR)*wcslen(filterDesc.Ptr); EnableTraceEx2(hSession, &ProcessProviderGuid, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_VERBOSE, 0, 0, ¶ms); |
异步消费模式
1 2 3 |
// 使用I/O完成端口提高吞吐量 logFile.ProcessTraceMode = PROCESS_TRACE_MODE_ASYNCHRONOUS; logFile.Context = hIOCP; |
安全审计增强
事件完整性校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 验证事件签名 if(!VerifyEventSignature(event)) { ReportSecurityBreach(); return; } BOOL VerifyEventSignature(PEVENT_RECORD event) { // 使用WinVerifyTrust校验事件源 return CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_SSL, pCertContext, &policyPara, &policyStatus); } |
防御日志注入
1 2 3 4 5 6 7 8 |
// 清洗非法字符 void SanitizeEventData(WCHAR* data) { WCHAR* p = data; while(*p) { if(*p < 0x20 || *p == L'\\') *p = L'_'; p++; } } |
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
#include <windows.h> #include <evntcons.h> #include <tdh.h> #include <strsafe.h> #include <iostream> #include <vector> #pragma comment(lib, "advapi32.lib") #pragma comment(lib, "tdh.lib") // Microsoft-Windows-Kernel-Process的GUID const GUID ProcessProviderGuid = { 0x22fb2cd6, 0x0e7b, 0x422b, {0xa0, 0xc7, 0x2f, 0xad, 0x1f, 0x2d, 0x4a, 0xee} }; class ProcessMonitor { private: TRACEHANDLE m_hTrace; bool m_bRunning; // 转换设备路径为DOS路径 std::wstring ConvertDevicePath(const wchar_t* devicePath) { std::vector<wchar_t> dosPaths(4096); std::vector<wchar_t> driveLetters(4096); if (QueryDosDeviceW(NULL, driveLetters.data(), static_cast<DWORD>(driveLetters.size())) == 0) return devicePath; for (wchar_t* p = driveLetters.data(); *p; p += wcslen(p) + 1) { std::wstring symLink = L"\\??\\"; symLink += p; if (_wcsnicmp(devicePath, symLink.c_str(), symLink.length()) == 0) { std::wstring dosPath = p; dosPath += L":"; dosPath += devicePath + symLink.length(); return dosPath; } } return devicePath; } // ETW事件回调 static void WINAPI EventCallback(PEVENT_RECORD eventRecord) { ProcessMonitor* pThis = reinterpret_cast<ProcessMonitor*>(eventRecord->UserContext); pThis->HandleEvent(eventRecord); } void HandleEvent(PEVENT_RECORD eventRecord) { DWORD pid = 0; std::vector<wchar_t> imagePath(MAX_PATH); // 解析事件类型 switch (eventRecord->EventHeader.EventDescriptor.Opcode) { case 1: // ProcessStart if (TdhGetProperty(eventRecord, 0, L"ProcessId", sizeof(DWORD), reinterpret_cast<PBYTE>(&pid)) == ERROR_SUCCESS) { ULONG pathSize = MAX_PATH * sizeof(wchar_t); if (TdhGetProperty(eventRecord, 0, L"ImageName", pathSize, reinterpret_cast<PBYTE>(imagePath.data())) == ERROR_SUCCESS) { std::wcout << L"[Process Start] PID: " << pid << L", Path: " << ConvertDevicePath(imagePath.data()) << std::endl; } } break; case 2: // ProcessEnd if (TdhGetProperty(eventRecord, 0, L"ProcessId", sizeof(DWORD), reinterpret_cast<PBYTE>(&pid)) == ERROR_SUCCESS) { std::wcout << L"[Process End] PID: " << pid << std::endl; } break; } } public: ProcessMonitor() : m_hTrace(0), m_bRunning(false) {} ~ProcessMonitor() { Stop(); } bool Start() { // 设置ETW会话参数 EVENT_TRACE_LOGFILEW logFile = {0}; logFile.LoggerName = L"ProcessMonitor"; logFile.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; logFile.EventRecordCallback = EventCallback; logFile.Context = this; m_hTrace = OpenTraceW(&logFile); if (m_hTrace == INVALID_PROCESSTRACE_HANDLE) { std::wcerr << L"OpenTrace failed: " << GetLastError() << std::endl; return false; } // 启用内核进程事件 EVENT_TRACE_PROPERTIES* pProps = reinterpret_cast<EVENT_TRACE_PROPERTIES*>( new BYTE[sizeof(EVENT_TRACE_PROPERTIES) + sizeof(L"KernelProcessLogger")]); ZeroMemory(pProps, sizeof(EVENT_TRACE_PROPERTIES) + sizeof(L"KernelProcessLogger")); pProps->Wnode.BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(L"KernelProcessLogger"); pProps->Wnode.Guid = ProcessProviderGuid; pProps->Wnode.Flags = WNODE_FLAG_TRACED_GUID; pProps->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; pProps->EnableFlags = EVENT_TRACE_FLAG_PROCESS; if (StartTraceW(&m_hTrace, L"KernelProcessLogger", pProps) != ERROR_SUCCESS) { std::wcerr << L"StartTrace failed: " << GetLastError() << std::endl; delete[] pProps; return false; } delete[] pProps; // 启动监控线程 m_bRunning = true; ProcessTrace(&m_hTrace, 1, NULL, NULL); return true; } void Stop() { if (m_bRunning) { m_bRunning = false; CloseTrace(m_hTrace); } } }; int main() { // 需要管理员权限运行 if (!IsUserAnAdmin()) { std::wcerr << L"Please run as administrator!" << std::endl; return 1; } ProcessMonitor monitor; if (!monitor.Start()) { return 1; } std::wcout << L"Monitoring process events. Press Ctrl+C to exit..." << std::endl; // 保持主线程运行 while (true) { Sleep(1000); } return 0; } |
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 关于多字节和宽字节一11/10
- ♥ COM组件_403/07
- ♥ macOs 解析mach-o05/11
- ♥ 预处理指令记录:一07/09
- ♥ Windows API11/11
- ♥ C++_可以重载的运算符12/22