Skip to content

Instantly share code, notes, and snippets.

@xpn xpn/dotnet_etw.c Secret
Created Mar 16, 2020

Embed
What would you like to do?
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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.