概述
- 微软开发的
C++ ETW
(Event Tracing for Windows
)库,专为简化Windows
事件跟踪开发而生
核心功能
- 封装底层
ETW API
,提供面向对象接口 - 支持用户态和内核态事件跟踪
- 提供事件模式解析、数据提取等高级功能
- 支持同步/异步事件处理模式
kernel_trace
类
作用
- 管理内核事件跟踪会话
核心方法
enable():
激活事件提供者start():
启动跟踪会话stop():
安全终止跟踪
process_provider
组件
作用
- 内核提供者
事件范围:
ProcessStart(Opcode 3)
ProcessStop(Opcode 4)
关键字段:
ProcessId
:进程PID
ImageFileName
:可执行文件路径
schema
解析器
作用
- 将原始事件数据转换为结构化格式
重要方法:
task_name():
获取事件任务名称opcode_name():
获取操作码说明
parser
解析器
示例
vcpkg
安装的- 示例代码,监控进程启动,文件相关操作
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 |
#include <iostream> #include <thread> #include <krabs.hpp> #define PRINT_LIMIT 3 HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); void process_rundown_callback(const EVENT_RECORD& record, const krabs::trace_context& trace_context) { if (record.EventHeader.EventDescriptor.Opcode == 1 || record.EventHeader.EventDescriptor.Opcode == 2) { krabs::schema schema(record, trace_context.schema_locator); std::wcout << schema.task_name() << L"_" << schema.opcode_name(); std::wcout << L" (" << schema.event_opcode() << L") "; krabs::parser parser(schema); std::uint32_t pid = parser.parse<std::uint32_t>(L"ProcessId"); std::wcout << L" <ProcessId>=" << pid; std::string imagefilename = parser.parse<std::string>(L"ImageFileName"); SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN); std::cout << " <ImageFileName>=" << imagefilename; SetConsoleTextAttribute( hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); std::wstring commandLine = parser.parse<std::wstring>(L"CommandLine"); std::wcout << " <CommandLine>=" << commandLine; std::wcout << std::endl; std::wcout << std::endl; std::wcout << std::endl; } } void file_rundown_callback(const EVENT_RECORD& record, const krabs::trace_context& trace_context){ if (record.EventHeader.EventDescriptor.Opcode == 32 || record.EventHeader.EventDescriptor.Opcode == 36) { // FileRundown krabs::schema schema(record, trace_context.schema_locator); std::wcout << schema.task_name() << L"_" << schema.opcode_name(); std::wcout << L" (" << schema.event_opcode() << L") "; krabs::parser parser(schema); std::wstring filename = parser.parse<std::wstring>(L"FileName"); SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE); std::wcout << L" FileName=" << filename; SetConsoleTextAttribute( hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); std::wcout << std::endl; } } void MonitorProcess() { krabs::kernel_trace trace(L"process"); const DWORD bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + (MAX_PATH * sizeof(wchar_t)) * 2; // 为名称预留空间 // 使用 _aligned_malloc 确保内存对齐 PEVENT_TRACE_PROPERTIES props = (PEVENT_TRACE_PROPERTIES)_aligned_malloc( bufferSize, MEMORY_ALLOCATION_ALIGNMENT); SecureZeroMemory(props, bufferSize); props->Wnode.BufferSize = bufferSize; //props->Wnode.Flags = WNODE_FLAG_TRACED_GUID; //props->Wnode.ClientContext = 1; //props->BufferSize = 64; //props->MinimumBuffers = 128; //props->MaximumBuffers = 1024; props->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_NO_PER_PROCESSOR_BUFFERING; //props->FlushTimer = 5; props->EnableFlags = EVENT_TRACE_FLAG_PROCESS | EVENT_TRACE_FLAG_DISK_IO | EVENT_TRACE_FLAG_REGISTRY; trace.set_trace_properties(props); krabs::kernel::process_provider process_provider; process_provider.add_on_event_callback(process_rundown_callback); trace.enable(process_provider); //krabs::kernel::registry_provider reg_provider; krabs::kernel::disk_file_io_provider fileio_provider; fileio_provider.add_on_event_callback(file_rundown_callback); trace.enable(fileio_provider); std::cout << " - starting trace" << std::endl; std::thread thread([&trace]() { trace.start(); }); //Sleep(1500); //std::cout << std::endl << " - stopping trace" << std::endl; //trace.stop(); thread.join(); } void MonitorRegistry() { } void MonitorFileIO() { } int main() { MonitorProcess(); MonitorRegistry(); MonitorFileIO(); // 保持监控运行 std::cout << "监控已启动,按Enter退出..." << std::endl; std::cin.get(); return 0; } |
其他
EVENT_TRACE_PROPERTIES
- 含义
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 |
typedef struct _EVENT_TRACE_PROPERTIES { WNODE_HEADER Wnode; // 系统节点头 ULONG BufferSize; // 每个缓冲区大小(KB) ULONG MinimumBuffers; // 最小缓冲数 ULONG MaximumBuffers; // 最大缓冲数 ULONG MaximumFileSize; // 最大日志文件大小(MB) ULONG LogFileMode; // 日志模式标志 ULONG FlushTimer; // 强制刷新间隔(秒) ULONG EnableFlags; // 启用的事件标志 union { LONG AgeLimit; // 文件存活时间 LONG FlushThreshold; // 强制刷新阈值 }; ULONG NumberOfBuffers; // 当前分配的缓冲区 ULONG FreeBuffers; // 空闲缓冲区 ULONG EventsLost; // 丢失事件计数 ULONG BuffersWritten; // 已写缓冲区 ULONG LogBuffersLost; // 日志缓冲区丢失 ULONG RealTimeBuffersLost; // 实时缓冲区丢失 HANDLE LoggerThreadId; // 记录器线程 ID union { ULONG LogFileNameOffset; // 文件名偏移量 ULONG LoggerNameOffset; // 记录器名偏移量 }; } EVENT_TRACE_PROPERTIES, *PEVENT_TRACE_PROPERTIES; |
- 使用
1 2 3 4 5 6 7 8 9 |
// 计算总内存需求 const DWORD bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + (MAX_PATH * sizeof(wchar_t)) * 2; // 为名称预留空间 // 使用 _aligned_malloc 确保内存对齐 PEVENT_TRACE_PROPERTIES props = (PEVENT_TRACE_PROPERTIES)_aligned_malloc(bufferSize, MEMORY_ALLOCATION_ALIGNMENT); SecureZeroMemory(props, bufferSize); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 配置实时日志模式 props->Wnode.BufferSize = bufferSize; props->Wnode.Flags = WNODE_FLAG_TRACED_GUID; props->Wnode.ClientContext = 1; // 使用QPC计时器 props->BufferSize = 64; // 64KB per buffer props->MinimumBuffers = 128; // 最小128个缓冲区 props->MaximumBuffers = 1024; // 最大1024个缓冲区 props->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_USE_PAGED_MEMORY; props->FlushTimer = 5; // 5秒强制刷新 props->EnableFlags = EVENT_TRACE_FLAG_PROCESS; // 仅进程事件 // 设置记录器名称(必须使用偏移量) wchar_t* loggerName = (wchar_t*)((BYTE*)props + sizeof(EVENT_TRACE_PROPERTIES)); wcscpy_s(loggerName, MAX_PATH, L"MyKernelLogger"); props->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void ConfigureAdvancedTrace(krabs::kernel_trace& trace) { // 创建可扩展的配置结构体 constexpr DWORD bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + (MAX_PATH * sizeof(wchar_t)) * 2; auto props = std::make_unique<BYTE[]>(bufferSize); PEVENT_TRACE_PROPERTIES pProps = reinterpret_cast<PEVENT_TRACE_PROPERTIES>(props.get()); // 结构体初始化(同上) // ... // 通过 krabs 设置高级属性 trace.set_trace_properties(pProps); // 验证配置结果 if (pProps->Wnode.Guid == krabs::details::guids::kernel_trace_guid) { std::wcout << L"成功绑定内核会话配置" << std::endl; } } |
Opcode
范围
Opcode 范围 |
类型 | 典型用途 |
0-10 |
基础操作码 | 进程、线程生命周期 |
11-198 |
自定义操作码 | 第三方应用或驱动定义的事件 |
199-239 |
保留值 | 系统保留,不可使用 |
240-255 |
跟踪控制操作码 | 如 WINEVENT_OPCODE_FLUSH (244) |
Opcode
进程
- 部分值
Opcode 值 |
名称(krabs 常量) |
说明 |
1 | krabs::sysevent::opcode::start |
进程启动(常规的进程创建,如用户启动程序) |
2 | krabs::sysevent::opcode::end |
进程终止(常规的进程结束,如程序正常退出或被终止) |
3 | krabs::sysevent::opcode::dc_start |
数据收集启动(Diagnostic Collection Start ,内核级进程初始化) |
4 | krabs::sysevent::opcode::dc_end |
数据收集结束(Diagnostic Collection End ,内核级进程资源释放) |
-
dc_start(Opcode 3)
- 触发时机:在进程的 内核对象(
EPROCESS
) 初始化时触发 - 监控意义:
比用户态进程启动(Opcode 1
)更早,用于跟踪内核级进程创建活动
适用于监控系统关键进程(如csrss.exe
、wininit.exe
)的初始化 - 事件数据:
包含进程的 父进程PID
、创建时间戳(UTC
) 等元数据
- 触发时机:在进程的 内核对象(
-
dc_end(Opcode 4)
- 触发时机:在进程的 内核对象完全释放 时触发
- 监控意义:
比用户态进程终止(Opcode 2
)更晚,确保所有资源已释放。
用于检测内核级进程泄漏(如驱动程序未正确清理进程对象)。 - 事件数据:
包含进程的 退出状态码、终止原因 等诊断信息
Opcode
线程
Opcode | 名称 | 描述 |
1 | Start |
线程开始执行 |
2 | End |
线程终止 |
3 | DC_Start |
线程内核对象(ETHREAD )初始化 |
4 | DC_End |
线程内核对象释放 |
36 | CSwitch |
上下文切换(高频事件,用于 CPU 分析) |
Opcode
注册表
Opcode | 名称 | 描述 |
32 | CreateKey |
注册表键创建 |
33 | OpenKey |
打开注册表键 |
34 | DeleteKey |
删除注册表键 |
35 | QueryKey |
查询注册表键信息 |
36 | SetValueKey |
设置注册表键值(写操作) |
Opcode
文件系统
Opcode | 名称 | 描述 |
32 | Create |
文件/目录创建 |
33 | Cleanup |
文件句柄关闭 |
34 | Close |
文件对象释放 |
36 | SetInfo |
文件元数据修改(如重命名、属性更改) |
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ C++_关于对象的具体初始化顺序11/30
- ♥ C++编程规范101规则、准则与最佳实践 一01/05
- ♥ C++_可以重载的运算符12/22
- ♥ C++标准模板库编程实战_关联容器12/07
- ♥ 51CTO:C++语言高级课程一08/07
- ♥ C++_多态、类型转换、数据段、BSS段、类型视图06/21