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

C++中如何从含结构体和数组的ETW数据包提取特定字段?

提取Microsoft-Windows-USB-USB3HUB ETW事件中的Fid_PortNumber和Fid_PortPath[0]字段

我刚好做过不少ETW事件解析的开发,针对你要提取的这两个字段,咱们一步步来拆解实现逻辑:

核心思路概述

ETW事件的字段是树形嵌套结构,所以必须通过递归遍历事件属性元数据,定位到目标字段后,再根据_TDH_OUT_TYPE枚举对应的类型来提取值。对于数组类型的字段(比如Fid_PortPath),还要额外处理数组长度和索引访问。

具体实现步骤

1. 先获取事件的元数据信息

首先,你需要调用TdhGetEventInformation来获取当前ETW事件的属性列表,这是解析的基础。示例代码大致如下:

PEVENT_RECORD pEventRecord; // 这是你从ETW会话中拿到的事件记录
DWORD bufferSize = 0;
// 先调用一次获取所需缓冲区大小
TdhGetEventInformation(pEventRecord, 0, nullptr, nullptr, &bufferSize);
// 分配缓冲区
std::vector<BYTE> metadataBuffer(bufferSize);
PEVENT_EVENT_INFO pEventInfo = reinterpret_cast<PEVENT_EVENT_INFO>(metadataBuffer.data());
// 再次调用获取实际元数据
if (TdhGetEventInformation(pEventRecord, 0, nullptr, pEventInfo, &bufferSize) != ERROR_SUCCESS) {
    // 处理错误
    return;
}

2. 递归遍历字段属性,定位目标字段

写一个递归函数,遍历pEventInfo->EventPropertyInfo数组里的每个属性,匹配字段名(注意字段名是PropertyName指向的字符串)。这里要区分普通字段和数组字段:

void TraverseProperties(PEVENT_PROPERTY_INFO pProps, DWORD propCount, PBYTE eventData, DWORD eventDataSize) {
    for (DWORD i = 0; i < propCount; ++i) {
        PEVENT_PROPERTY_INFO pProp = &pProps[i];
        // 获取字段名
        PCWSTR propName = reinterpret_cast<PCWSTR>(reinterpret_cast<PBYTE>(pProps) + pProp->NameOffset);
        
        // 匹配目标字段:Fid_PortNumber
        if (_wcsicmp(propName, L"Fid_PortNumber") == 0) {
            // 提取该字段的值
            PBYTE fieldData = eventData + pProp->DataOffset;
            switch (pProp->NonStructType.OutType) {
                case TDH_OUT_USHORT:
                    USHORT portNumber = *reinterpret_cast<USHORT*>(fieldData);
                    // 保存portNumber到你的变量或存储结构
                    break;
                // 根据实际类型补充其他case,比如TDH_OUT_DWORD等
                default:
                    // 处理未知类型
                    break;
            }
        }
        // 匹配目标数组字段:Fid_PortPath
        else if (_wcsicmp(propName, L"Fid_PortPath") == 0) {
            // 先获取数组长度
            DWORD arrayCount = 0;
            if (pProp->CountPropertyIndex != TDH_NO_COUNT_PROPERTY) {
                // 如果长度由另一个字段指定,需要先提取那个字段的值
                PEVENT_PROPERTY_INFO countProp = &pProps[pProp->CountPropertyIndex];
                PBYTE countData = eventData + countProp->DataOffset;
                arrayCount = *reinterpret_cast<DWORD*>(countData);
            } else {
                // 如果是固定长度数组,直接用pProp->StructType.NumOfElements
                arrayCount = pProp->StructType.NumOfElements;
            }
            
            // 访问数组的第0个元素
            if (arrayCount > 0) {
                // 计算第0个元素的偏移:基础偏移 + 单个元素大小 * 索引
                DWORD elementSize = pProp->StructType.Size;
                PBYTE elementData = eventData + pProp->DataOffset + (elementSize * 0);
                // 根据元素的类型提取值,比如如果是USHORT类型:
                USHORT portPath0 = *reinterpret_cast<USHORT*>(elementData);
                // 保存portPath0
            }
        }
        
        // 如果当前字段是嵌套结构,递归遍历子属性
        if (pProp->Flags & PropertyStruct) {
            PEVENT_PROPERTY_INFO childProps = reinterpret_cast<PEVENT_PROPERTY_INFO>(reinterpret_cast<PBYTE>(pProps) + pProp->StructType.StructStartIndex);
            DWORD childPropCount = pProp->StructType.NumOfElements;
            TraverseProperties(childProps, childPropCount, eventData, eventDataSize);
        }
    }
}

3. 调用递归函数开始解析

在拿到pEventInfo之后,调用上面的函数:

TraverseProperties(pEventInfo->EventPropertyInfo, pEventInfo->PropertyCount, pEventRecord->UserData, pEventRecord->UserDataLength);

关键注意事项

  • 字段类型匹配:一定要根据pProp->NonStructType.OutType的值来决定如何解析内存,比如TDH_OUT_USHORT对应16位无符号整数,TDH_OUT_DWORD对应32位无符号整数,不要硬转类型,否则会出现数据错误。
  • 数组长度处理:ETW数组分为动态长度(由另一个字段指定长度)和固定长度(元数据中指定NumOfElements),两种情况要分别处理。
  • 内存偏移计算:所有字段的位置都是相对于eventData(也就是pEventRecord->UserData)的偏移,不要搞错基准地址。

内容的提问来源于stack exchange,提问作者igal k

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:59:44