You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

用户态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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 11:09:59