用户态ETW程序无法捕获进程启动事件的问题排查求助
ETW用户态程序无法捕获进程启动事件的问题排查
问题背景
目标是编写ETW用户态程序,实现记录Windows上所有用户启动程序的功能(替代已通过内核驱动PsSetCreateProcessNotifyRoutineEx实现的方案)。参考相关代码编译后以管理员权限运行,始终无法捕获程序启动事件,打开文本编辑器、浏览器等程序均未触发回调函数。
原始代码
#include <windows.h> #include <stdio.h> #include <conio.h> #include <strsafe.h> #include <wbemidl.h> #include <wmistr.h> #include <evntrace.h> #include <Evntcons.h> using namespace std; #pragma comment(lib, "Advapi32.lib") #define LOGSESSION_GUID "{b2bcc945-9eb9-4231-883c-d6455cf4d86b}" #define LOGSESSION_NAME L"Testing ETW Consumer" static const GUID SessionGuid = { 0xb2bcc945, 0x9eb9, 0x4231,{ 0x88, 0x3c, 0xd6, 0x45, 0x5c, 0xf4, 0xd8, 0x6b } }; TRACEHANDLE sessionHandle; TRACEHANDLE traceHandle; GUID guid; int bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGSESSION_NAME) + sizeof(WCHAR); auto pSessionProperties = static_cast<PEVENT_TRACE_PROPERTIES>(malloc(bufferSize)); // Microsoft-Windows-Kernel-Process // {22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716} static const GUID MSKRNL_PROVIDER_GUID = { 0x22fb2cd6, 0x0e7b, 0x422b,{ 0xa0, 0xc7, 0x2f, 0xad, 0x1f, 0xd0, 0xe7, 0x16 } }; VOID WINAPI EventRecordCallback(EVENT_RECORD* pEventRecord) { fprintf(stdout, "Event Record Received\n"); return; } ULONG WINAPI BufferCallback(EVENT_TRACE_LOGFILE* pLogFile) { fprintf(stdout, "in BufferCallback...\n"); return TRUE; } static DWORD WINAPI Win32TracingThread(LPVOID Parameter) { fprintf(stdout, "processing trace...\n"); auto ptStatus = ProcessTrace(&traceHandle, 1, NULL, NULL); if (ptStatus != ERROR_SUCCESS && ptStatus != ERROR_CANCELLED) { wprintf(L"ProcessTrace exit status: %lu\n", ptStatus); } return(0); } int main(void) { fprintf(stdout, "entering main program...\n"); // Set the session properties. You only append the log file name // to the properties structure; the StartTrace function appends // the session name for you. ZeroMemory(pSessionProperties, bufferSize); pSessionProperties->Wnode.BufferSize = bufferSize; pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; pSessionProperties->Wnode.ClientContext = 1; //QPC clock resolution pSessionProperties->Wnode.Guid = SessionGuid; pSessionProperties->FlushTimer = 0; pSessionProperties->EnableFlags = EVENT_TRACE_FLAG_PROCESS | EVENT_TRACE_FLAG_PROCESS_COUNTERS; pSessionProperties->LogFileMode = EVENT_TRACE_REAL_TIME_MODE; pSessionProperties->LogFileNameOffset = 0; pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); StringCbCopy((STRSAFE_LPWSTR)((char*)pSessionProperties + pSessionProperties->LoggerNameOffset), sizeof(LOGSESSION_NAME), LOGSESSION_NAME); // stop any previous session auto stopStatus = ControlTrace(0, LOGSESSION_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP); fprintf(stdout, "starting trace...\n"); auto sTrStatus = StartTrace(static_cast<PTRACEHANDLE>(&sessionHandle), LOGSESSION_NAME, pSessionProperties); fprintf(stdout, "enabling trace...\n"); auto eTrExstatus = EnableTraceEx2(sessionHandle, &MSKRNL_PROVIDER_GUID, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_VERBOSE, 1, 0, 0, nullptr); EVENT_TRACE_LOGFILE loggerInfo = {0}; loggerInfo.ProcessTraceMode = EVENT_TRACE_REAL_TIME_MODE | PROCESS_TRACE_MODE_EVENT_RECORD; loggerInfo.BufferCallback = BufferCallback; // provide a callback whenever we get an event record loggerInfo.EventRecordCallback = (PEVENT_RECORD_CALLBACK) EventRecordCallback; loggerInfo.Context = nullptr; // LoggerName is the sessionName that we had provided in StartTrace // For consuming events from ETL file we will provide path to ETL file. loggerInfo.LoggerName = LOGSESSION_NAME; loggerInfo.LogFileName = NULL; loggerInfo.LogFileMode = EVENT_TRACE_REAL_TIME_MODE; fprintf(stdout, "opening trace...\n"); traceHandle = OpenTrace(&loggerInfo); // calling thread will be blocked until BufferCallback returns FALSE or all events are delivered or CloseTrace is called DWORD ThreadID; HANDLE ThreadHandle = CreateThread(0, 0, Win32TracingThread, 0, 0, &ThreadID); // Waiting for you to press the ESC key.... bool exit2 = false; while (exit2 == false) { if (GetAsyncKeyState(VK_ESCAPE)) { exit2 = true; fprintf(stdout, "escape pressed, exiting loop...\n"); } } CloseHandle(ThreadHandle); if ((TRACEHANDLE)INVALID_HANDLE_VALUE != traceHandle) { fprintf(stdout, "in cleanup...\n"); fprintf(stdout, "closing trace...\n"); auto ctStatus = CloseTrace(traceHandle); } auto cTrStatus = ControlTrace(sessionHandle, nullptr, pSessionProperties, EVENT_TRACE_CONTROL_STOP); auto eTrStatus = EnableTraceEx2(sessionHandle, &MSKRNL_PROVIDER_GUID, EVENT_CONTROL_CODE_DISABLE_PROVIDER, 0, 0, 0, 0, nullptr); free(pSessionProperties); }
问题定位与修复方案
1. 核心错误:事件关键字不匹配
Microsoft-Windows-Kernel-Process提供者的进程启动事件对应关键字为0x80(PROCESS_KEYWORD_CREATE),你当前传递的MatchAnyKeyword参数为1,无法匹配目标事件。
修复代码:
// 替换原EnableTraceEx2调用,修正关键字参数 auto eTrExstatus = EnableTraceEx2(sessionHandle, &MSKRNL_PROVIDER_GUID, EVENT_CONTROL_CODE_ENABLE_PROVIDER, TRACE_LEVEL_VERBOSE, 0x80, 0, 0, nullptr);
2. 冗余设置无效
pSessionProperties->EnableFlags是旧版内核跟踪的标志,对于通过EnableTraceEx2启用的现代提供者无作用,建议设为0:
pSessionProperties->EnableFlags = 0;
3. 补充权限检查
管理员权限下仍需确保SeTracePrivilege权限启用,可在main开头添加权限启用逻辑:
// 启用SeTracePrivilege权限 HANDLE hToken; TOKEN_PRIVILEGES tp; OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken); LookupPrivilegeValue(NULL, SE_TRACE_NAME, &tp.Privileges[0].Luid); tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL); CloseHandle(hToken);
4. 缓冲区大小计算优化
原bufferSize计算可能存在误差,建议用StringCbLength准确计算会话名称所需空间:
size_t nameLen; StringCbLength(LOGSESSION_NAME, sizeof(LOGSESSION_NAME), &nameLen); int bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + nameLen + sizeof(WCHAR);
5. 增加错误校验
对StartTrace、EnableTraceEx2、OpenTrace等API的返回值进行校验,便于排查具体失败原因:
if (sTrStatus != ERROR_SUCCESS) { printf("StartTrace failed: %lu\n", sTrStatus); return 1; }
内容的提问来源于stack exchange,提问作者Tyler
相关产品推荐
相关产品推荐

