Python调用WinAPI解析ETL文件:ProcessTrace未触发回调求助
用Python调用WinAPI解析ETL文件时ProcessTrace未触发回调的问题排查
我需要用Windows原生WinAPI解析ETL文件(如sample.etl),C++下可正常解析,但改用Python调用相同API时,能通过OpenTraceA打开文件,却发现ProcessTrace未触发注册的回调函数。以下是我的代码:
import ctypes from ctypes.wintypes import * advapidll = "C:\Windows\System32\advapi32.dll" advapi32 = ctypes.WinDLL(advapidll) ERROR_SUCCESS = 0 PROCESS_TRACE_MODE_EVENT_RECORD = int(hex(10000000), 16) class _GUID(ctypes.Structure): _fields_ = [ ("Data1", ULONG), ("Data2", USHORT), ("Data3", USHORT), ("Data4", BYTE * 8), ] class EVENT_TRACE_HEADER(ctypes.Structure): _fields_ = [ # Define fields for EVENT_TRACE_HEADER here ] class ETW_BUFFER_CONTEXT(ctypes.Structure): _fields_ = [ ("ProcessorIndex", USHORT), ("LoggerId", USHORT), ] class EVENT_TRACE(ctypes.Structure): _fields_ = [ ("Header", EVENT_TRACE_HEADER), ("InstanceId", ULONG), ("ParentInstanceId", ULONG), ("ParentGuid", _GUID), # Using UUID to represent GUID ("MofData", LPVOID), ("MofLength", ULONG), ("DUMMYUNIONNAME", ULONG), # This can hold either ClientContext or BufferContext ] class SYSTEMTIME(ctypes.Structure): _fields_ = [ ("wYear", WORD), ("wMonth", WORD), ("wDayOfWeek", WORD), ("wDay", WORD), ("wHour", WORD), ("wMinute", WORD), ("wSecond", WORD), ("wMilliseconds", WORD), ] class TIME_ZONE_INFORMATION(ctypes.Structure): _fields_ = [ ("Bias", LONG), ("StandardName", WCHAR * 32), # WCHAR array for StandardName ("StandardDate", SYSTEMTIME), ("StandardBias", LONG), ("DaylightName", WCHAR * 32), # WCHAR array for DaylightName ("DaylightDate", SYSTEMTIME), ("DaylightBias", LONG), ] class _TRACE_LOGFILE_HEADER(ctypes.Structure): _fields_ = [ ("BufferSize", ULONG), ("Version", ULONG), # Holds either Version or VersionDetail ("ProviderVersion", ULONG), ("NumberOfProcessors", ULONG), ("EndTime", LARGE_INTEGER), ("TimerResolution", ULONG), ("MaximumFileSize", ULONG), ("LogFileMode", ULONG), ("BuffersWritten", ULONG), ("LogInstanceGuid", _GUID), # Holds either LogInstanceGuid or DUMMYSTRUCTNAME ("LoggerName", LPWSTR), # Assuming wchar_t* for wide character string ("LogFileName", LPWSTR), # Assuming wchar_t* for wide character string ("TimeZone", TIME_ZONE_INFORMATION), # Use TIME_ZONE_INFORMATION as per your need ("BootTime", LARGE_INTEGER), ("PerfFreq", LARGE_INTEGER), ("StartTime", LARGE_INTEGER), ("ReservedFlags", ULONG), ("BuffersLost", ULONG), ] class EVENT_DESCRIPTOR(ctypes.Structure): _fields_ = [ ("Id", USHORT), # USHORT ("Version", CHAR), # UCHAR ("Channel", CHAR), # UCHAR ("Level", CHAR), # UCHAR ("Opcode", CHAR), # UCHAR ("Task", USHORT), # USHORT ("Keyword", ULARGE_INTEGER) # ULONGLONG ] class EVENT_HEADER(ctypes.Structure): _fields_ = [ ("Size", USHORT), ("HeaderType", USHORT), ("Flags", USHORT), ("EventProperty", USHORT), ("ThreadId", ULONG), ("ProcessId", ULONG), ("TimeStamp", LARGE_INTEGER), ("ProviderId", _GUID), ("EventDescriptor", EVENT_DESCRIPTOR), ("ProcessorTime", ULARGE_INTEGER), ("ActivityId", _GUID), ] class EVENT_RECORD(ctypes.Structure): _fields_ = [ ("EventHeader", EVENT_HEADER), ("BufferContext", ETW_BUFFER_CONTEXT), ("ExtendedDataCount", USHORT), ("UserDataLength", USHORT), ("ExtendedData", ctypes.POINTER(ULONG)), # Assuming PEVENT_HEADER_EXTENDED_DATA_ITEM is a pointer to ULONG ("UserData", LPVOID), ("UserContext", LPVOID), ] PEVENT_RECORD_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.POINTER(EVENT_RECORD)) class _EVENT_TRACE_LOGFILEA(ctypes.Structure): _fields_ = [ ("LogFileName", LPSTR), ("LoggerName", LPSTR), ("CurrentTime", LARGE_INTEGER), ("BuffersRead", ULONG), ("ProcessTraceMode", ULONG), ("CurrentEvent", EVENT_TRACE), ("LogfileHeader", _TRACE_LOGFILE_HEADER), ("BufferCallback", ctypes.WINFUNCTYPE(None)), ("BufferSize", ULONG), ("Filled", ULONG), ("EventsLost", ULONG), ("EventRecordCallback", PEVENT_RECORD_CALLBACK), # This can hold either EventCallback or EventRecordCallback ("IsKernelTrace", ULONG), ("Context", LPVOID), ] @PEVENT_RECORD_CALLBACK def handle_trace_event(event_record): print("hi") OpenTraceA = advapi32.OpenTraceA OpenTraceA.argtypes = [ctypes.POINTER(_EVENT_TRACE_LOGFILEA)] OpenTraceA.restype = HANDLE ProcessTrace = advapi32.ProcessTrace ProcessTrace.argtypes = [ctypes.POINTER(HANDLE), ULONG, LPVOID, LPVOID] ProcessTrace.restype = DWORD def main(): trace_logfile = _EVENT_TRACE_LOGFILEA() trace_logfile.LogFileName = b"sample.etl" trace_logfile.EventRecordCallback = handle_trace_event trace_logfile.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD trace_handle = OpenTraceA(ctypes.byref(trace_logfile)) if trace_handle == HANDLE(0): print("Failed to open trace") else: print(f"Trace opened successfully: {trace_handle}") status = ProcessTrace(ctypes.byref(HANDLE(trace_handle)), 1, 0, 0) if (status != ERROR_SUCCESS): print(f"Problem occurred during ProcessTrace Status: {status}") if __name__ == '__main__': main()
问题排查与修复
1. 未完整定义EVENT_TRACE_HEADER结构体
这是核心问题:EVENT_TRACE结构体包含EVENT_TRACE_HEADER,但你只声明了结构体却未定义字段,导致内存布局完全错误。WinAPI无法正确填充数据,自然无法触发回调。根据官方文档,EVENT_TRACE_HEADER的正确定义如下:
class EVENT_TRACE_HEADER(ctypes.Structure): _fields_ = [ ("Size", USHORT), ("FieldTypeFlags", USHORT), ("Type", UCHAR), ("Level", UCHAR), ("Version", UCHAR), ("HeaderType", UCHAR), ("Flags", USHORT), ("ThreadId", ULONG), ("ProcessId", ULONG), ("TimeStamp", LARGE_INTEGER), ("Guid", _GUID), ("KernelTime", ULONG), ("UserTime", ULONG), ("InstanceId", ULONG), ]
2. _EVENT_TRACE_LOGFILEA的字段错误
BufferCallback类型错误:官方定义为PEVENT_TRACE_BUFFER_CALLBACK,即ctypes.WINFUNCTYPE(BOOL, ctypes.POINTER(EVENT_TRACE)),不是无参数函数类型,不需要时可设为None。- 结构体字段顺序必须严格匹配WinAPI定义,否则会导致内存偏移错误。
3. ProcessTrace参数传递错误
ProcessTrace的第一个参数是HANDLE数组的指针,不是单个HANDLE的指针。正确做法是创建HANDLE数组,将trace_handle放入后传递数组指针:
# 替换原ProcessTrace调用部分 handles = (HANDLE * 1)(trace_handle) status = ProcessTrace(handles, 1, 0, 0)
4. EVENT_RECORD的ExtendedData类型错误
ExtendedData应该是指向EVENT_HEADER_EXTENDED_DATA_ITEM的指针,需要先定义该结构体:
class EVENT_HEADER_EXTENDED_DATA_ITEM(ctypes.Structure): _fields_ = [ ("Reserved1", USHORT), ("ExtType", USHORT), ("DataSize", USHORT), ("Reserved2", USHORT), ("DataPtr", LPVOID), ] # 修正EVENT_RECORD的ExtendedData字段 class EVENT_RECORD(ctypes.Structure): _fields_ = [ ("EventHeader", EVENT_HEADER), ("BufferContext", ETW_BUFFER_CONTEXT), ("ExtendedDataCount", USHORT), ("UserDataLength", USHORT), ("ExtendedData", ctypes.POINTER(EVENT_HEADER_EXTENDED_DATA_ITEM)), ("UserData", LPVOID), ("UserContext", LPVOID), ]
5. 其他细节优化
PROCESS_TRACE_MODE_EVENT_RECORD可直接用十六进制值0x10000000,无需多余转换。- 确保回调函数引用被保留,避免Python垃圾回收机制回收(装饰器已处理,也可显式保留)。
修复后的完整代码
import ctypes from ctypes.wintypes import * advapidll = "C:\\Windows\\System32\\advapi32.dll" advapi32 = ctypes.WinDLL(advapidll) ERROR_SUCCESS = 0 PROCESS_TRACE_MODE_EVENT_RECORD = 0x10000000 class _GUID(ctypes.Structure): _fields_ = [ ("Data1", ULONG), ("Data2", USHORT), ("Data3", USHORT), ("Data4", BYTE * 8), ] class EVENT_TRACE_HEADER(ctypes.Structure): _fields_ = [ ("Size", USHORT), ("FieldTypeFlags", USHORT), ("Type", UCHAR), ("Level", UCHAR), ("Version", UCHAR), ("HeaderType", UCHAR), ("Flags", USHORT), ("ThreadId", ULONG), ("ProcessId", ULONG), ("TimeStamp", LARGE_INTEGER), ("Guid", _GUID), ("KernelTime", ULONG), ("UserTime", ULONG), ("InstanceId", ULONG), ] class ETW_BUFFER_CONTEXT(ctypes.Structure): _fields_ = [ ("ProcessorIndex", USHORT), ("LoggerId", USHORT), ] class EVENT_TRACE(ctypes.Structure): _fields_ = [ ("Header", EVENT_TRACE_HEADER), ("InstanceId", ULONG), ("ParentInstanceId", ULONG), ("ParentGuid", _GUID), ("MofData", LPVOID), ("MofLength", ULONG), ("ClientContext", ULONG), ] class SYSTEMTIME(ctypes.Structure): _fields_ = [ ("wYear", WORD), ("wMonth", WORD), ("wDayOfWeek", WORD), ("wDay", WORD), ("wHour", WORD), ("wMinute", WORD), ("wSecond", WORD), ("wMilliseconds", WORD), ] class TIME_ZONE_INFORMATION(ctypes.Structure): _fields_ = [ ("Bias", LONG), ("StandardName", WCHAR * 32), ("StandardDate", SYSTEMTIME), ("StandardBias", LONG), ("DaylightName", WCHAR * 32), ("DaylightDate", SYSTEMTIME), ("DaylightBias", LONG), ] class _TRACE_LOGFILE_HEADER(ctypes.Structure): _fields_ = [ ("BufferSize", ULONG), ("Version", ULONG), ("ProviderVersion", ULONG), ("NumberOfProcessors", ULONG), ("EndTime", LARGE_INTEGER), ("TimerResolution", ULONG), ("MaximumFileSize", ULONG), ("LogFileMode", ULONG), ("BuffersWritten", ULONG), ("LogInstanceGuid", _GUID), ("LoggerName", LPWSTR), ("LogFileName", LPWSTR), ("TimeZone", TIME_ZONE_INFORMATION), ("BootTime", LARGE_INTEGER), ("PerfFreq", LARGE_INTEGER), ("StartTime", LARGE_INTEGER), ("ReservedFlags", ULONG), ("BuffersLost", ULONG), ] class EVENT_DESCRIPTOR(ctypes.Structure): _fields_ = [ ("Id", USHORT), ("Version", UCHAR), ("Channel", UCHAR), ("Level", UCHAR), ("Opcode", UCHAR), ("Task", USHORT), ("Keyword", ULARGE_INTEGER), ] class EVENT_HEADER_EXTENDED_DATA_ITEM(ctypes.Structure): _fields_ = [ ("Reserved1", USHORT), ("ExtType", USHORT), ("DataSize", USHORT), ("Reserved2", USHORT), ("DataPtr", LPVOID), ] class EVENT_HEADER(ctypes.Structure): _fields_ = [ ("Size", USHORT), ("HeaderType", USHORT), ("Flags", USHORT), ("EventProperty", USHORT), ("ThreadId", ULONG), ("ProcessId", ULONG), ("TimeStamp", LARGE_INTEGER), ("ProviderId", _GUID), ("EventDescriptor", EVENT_DESCRIPTOR), ("ProcessorTime", ULARGE_INTEGER), ("ActivityId", _GUID), ] class EVENT_RECORD(ctypes.Structure): _fields_ = [ ("EventHeader", EVENT_HEADER), ("BufferContext", ETW_BUFFER_CONTEXT), ("ExtendedDataCount", USHORT), ("UserDataLength", USHORT), ("ExtendedData", ctypes.POINTER(EVENT_HEADER_EXTENDED_DATA_ITEM)), ("UserData", LPVOID), ("UserContext", LPVOID), ] PEVENT_RECORD_CALLBACK = ctypes.WINFUNCTYPE(None, ctypes.POINTER(EVENT_RECORD)) PEVENT_TRACE_BUFFER_CALLBACK = ctypes.WINFUNCTYPE(BOOL, ctypes.POINTER(EVENT_TRACE)) class _EVENT_TRACE_LOGFILEA(ctypes.Structure): _fields_ = [ ("LogFileName", LPSTR), ("LoggerName", LPSTR), ("CurrentTime", LARGE_INTEGER), ("BuffersRead", ULONG), ("ProcessTraceMode", ULONG), ("CurrentEvent", EVENT_TRACE), ("LogfileHeader", _TRACE_LOGFILE_HEADER), ("BufferCallback", PEVENT_TRACE_BUFFER_CALLBACK), ("BufferSize", ULONG), ("Filled", ULONG), ("EventsLost", ULONG), ("EventRecordCallback", PEVENT_RECORD_CALLBACK), ("IsKernelTrace", ULONG), ("Context", LPVOID), ] @PEVENT_RECORD_CALLBACK def handle_trace_event(event_record): print(f"触发回调,事件ID: {event_record.contents.EventHeader.EventDescriptor.Id}") OpenTraceA = advapi32.OpenTraceA OpenTraceA.argtypes = [ctypes.POINTER(_EVENT_TRACE_LOGFILEA)] OpenTraceA.restype = HANDLE ProcessTrace = advapi32.ProcessTrace ProcessTrace.argtypes = [ctypes.POINTER(HANDLE), ULONG, LPVOID, LPVOID] ProcessTrace.restype = DWORD def main(): trace_logfile = _EVENT_TRACE_LOGFILEA() trace_logfile.LogFileName = b"sample.etl" trace_logfile.EventRecordCallback = handle_trace_event trace_logfile.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD trace_logfile.BufferCallback = None # 无需缓冲区回调 trace_handle = OpenTrace
相关产品推荐
相关产品推荐

