Skip to content

Instantly share code, notes, and snippets.

@xpn
Created March 16, 2020 19:25
Show Gist options
  • Save xpn/41f193cf1bdeeee19ebd351b19cff45c to your computer and use it in GitHub Desktop.
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.
#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
Copy link

UserXGnu commented Mar 7, 2022

Keep getting:
imagem
Even if I convert it like:

hTrace = OpenTrace((PEVENT_TRACE_LOGFILEW)&trace);

The error goes away, but either way, when executing it I get:

imagem

Do you have an idea on what might be the problem?

@sanreee
Copy link

sanreee commented Mar 25, 2022

@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

@TomSie
Copy link

TomSie commented Mar 28, 2023

Keep getting: imagem Even if I convert it like:

hTrace = OpenTrace((PEVENT_TRACE_LOGFILEW)&trace);

The error goes away, but either way, when executing it I get:

imagem

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

@pwndad
Copy link

pwndad commented Jan 24, 2024

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