Created
March 16, 2020 19:25
-
-
Save xpn/41f193cf1bdeeee19ebd351b19cff45c to your computer and use it in GitHub Desktop.
A demo of how to collect information on basic .NET events from ETW.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define AssemblyDCStart_V1 155 | |
#define MethodLoadVerbose_V1 143 | |
#include <windows.h> | |
#include <stdio.h> | |
#include <wbemidl.h> | |
#include <wmistr.h> | |
#include <evntrace.h> | |
#include <Evntcons.h> | |
static GUID ClrRuntimeProviderGuid = { 0xe13c0d23, 0xccbc, 0x4e12, { 0x93, 0x1b, 0xd9, 0xcc, 0x2e, 0xee, 0x27, 0xe4 } }; | |
// Can be stopped with 'logman stop "dotnet trace" -etw' | |
const char name[] = "dotnet trace\0"; | |
#pragma pack(1) | |
typedef struct _AssemblyLoadUnloadRundown_V1 | |
{ | |
ULONG64 AssemblyID; | |
ULONG64 AppDomainID; | |
ULONG64 BindingID; | |
ULONG AssemblyFlags; | |
WCHAR FullyQualifiedAssemblyName[1]; | |
} AssemblyLoadUnloadRundown_V1, *PAssemblyLoadUnloadRundown_V1; | |
typedef struct _MethodLoadVerbose_V1 | |
{ | |
ULONG64 MethodID; | |
ULONG64 ModuleID; | |
ULONG64 MethodStartAddress; | |
DWORD MethodSize; | |
DWORD MethodToken; | |
DWORD MethodFlags; | |
WCHAR MethodNameSpace[1]; | |
} MethodLoadUnloadVerbose_V1, *PMethodLoadUnloadVerbose_V1; | |
#pragma pack() | |
static void NTAPI ProcessEvent(PEVENT_RECORD EventRecord) { | |
PEVENT_HEADER eventHeader = &EventRecord->EventHeader; | |
PEVENT_DESCRIPTOR eventDescriptor = &eventHeader->EventDescriptor; | |
AssemblyLoadUnloadRundown_V1* assemblyUserData; | |
MethodLoadUnloadVerbose_V1* methodUserData; | |
switch (eventDescriptor->Id) { | |
case AssemblyDCStart_V1: | |
assemblyUserData = (AssemblyLoadUnloadRundown_V1*)EventRecord->UserData; | |
wprintf(L"[%d] - Assembly: %s\n", eventHeader->ProcessId, assemblyUserData->FullyQualifiedAssemblyName); | |
break; | |
case MethodLoadVerbose_V1: | |
methodUserData = (struct _MethodLoadVerbose_V1*)EventRecord->UserData; | |
WCHAR* MethodNameSpace = methodUserData->MethodNameSpace; | |
WCHAR* MethodName = (WCHAR*)(((char*)methodUserData->MethodNameSpace) + (lstrlenW(methodUserData->MethodNameSpace) * 2) + 2); | |
WCHAR* MethodSignature = (WCHAR*)(((char*)MethodName) + (lstrlenW(MethodName) * 2) + 2); | |
wprintf(L"[%d] - MethodNameSpace: %s\n", eventHeader->ProcessId, methodUserData->MethodNameSpace); | |
wprintf(L"[%d] - MethodName: %s\n", eventHeader->ProcessId, MethodName); | |
wprintf(L"[%d] - MethodSignature: %s\n", eventHeader->ProcessId, MethodSignature); | |
break; | |
} | |
} | |
int main(void) | |
{ | |
TRACEHANDLE hTrace = 0; | |
ULONG result, bufferSize; | |
EVENT_TRACE_LOGFILEA trace; | |
EVENT_TRACE_PROPERTIES *traceProp; | |
printf("ETW .NET Trace example - @_xpn_\n\n"); | |
memset(&trace, 0, sizeof(EVENT_TRACE_LOGFILEA)); | |
trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD; | |
trace.LoggerName = (LPSTR)name; | |
trace.EventRecordCallback = (PEVENT_RECORD_CALLBACK)ProcessEvent; | |
bufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(name) + sizeof(WCHAR); | |
traceProp = (EVENT_TRACE_PROPERTIES*)LocalAlloc(LPTR, bufferSize); | |
traceProp->Wnode.BufferSize = bufferSize; | |
traceProp->Wnode.ClientContext = 2; | |
traceProp->Wnode.Flags = WNODE_FLAG_TRACED_GUID; | |
traceProp->LogFileMode = EVENT_TRACE_REAL_TIME_MODE | EVENT_TRACE_USE_PAGED_MEMORY; | |
traceProp->LogFileNameOffset = 0; | |
traceProp->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); | |
if ((result = StartTraceA(&hTrace, (LPCSTR)name, traceProp)) != ERROR_SUCCESS) { | |
printf("[!] Error starting trace: %d\n", result); | |
return 1; | |
} | |
if ((result = EnableTraceEx( | |
&ClrRuntimeProviderGuid, | |
NULL, | |
hTrace, | |
1, | |
TRACE_LEVEL_VERBOSE, | |
0x8 | 0x10, // LoaderKeyword | JITKeyword | |
0, | |
0, | |
NULL | |
)) != ERROR_SUCCESS) { | |
printf("[!] Error EnableTraceEx\n"); | |
return 2; | |
} | |
hTrace = OpenTrace(&trace); | |
if (hTrace == INVALID_PROCESSTRACE_HANDLE) { | |
printf("[!] Error OpenTrace\n"); | |
return 3; | |
} | |
result = ProcessTrace(&hTrace, 1, NULL, NULL); | |
if (result != ERROR_SUCCESS) { | |
printf("[!] Error ProcessTrace\n"); | |
return 4; | |
} | |
return 0; | |
} |
@UserXGnu The error code 183 refers to ERROR_ALREADY_EXISTS (https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-) meaning that you need to remove the collector you probably created on the first run.
logman stop "<name>" -ets
Keep getting: Even if I convert it like:
hTrace = OpenTrace((PEVENT_TRACE_LOGFILEW)&trace);The error goes away, but either way, when executing it I get:
Do you have an idea on what might be the problem?
In case anybody has the same problem: The project needs to be compiled as ASCII (Multi Byte), not Unicode
This worked for me
hTrace = OpenTraceA(&trace);
instead of
hTrace = OpenTrace(&trace);
It seems the old signature has been retired in evntrace.h (i.e. commented out):
// OpenTrace(
// _Inout_ PEVENT_TRACE_LOGFILE Logfile
// );
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Keep getting:
Even if I convert it like:
The error goes away, but either way, when executing it I get:
Do you have an idea on what might be the problem?