Skip to content

Instantly share code, notes, and snippets.

@Zhentar
Created July 3, 2019 19:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zhentar/4958d3fef6e51fbeb07e8f7c96d315d7 to your computer and use it in GitHub Desktop.
Save Zhentar/4958d3fef6e51fbeb07e8f7c96d315d7 to your computer and use it in GitHub Desktop.
C# script for exploring ETW providers
using System.ComponentModel;
using System.Runtime.InteropServices;
public static unsafe ulong StartTraceHelper(string loggerName, string filename)
{
var trace_props = NewEventTraceProperties(false);
trace_props.LogFileMode = 0x08000000; //EVENT_TRACE_INDEPENDENT_SESSION_MODE
trace_props.BufferSize = 1024;
for (int i = 0; i < loggerName.Length; i++)
{
trace_props.LoggerName[i] = loggerName[i];
}
for (int i = 0; i < filename.Length; i++)
{
trace_props.LogFileName[i] = filename[i];
}
ulong handle = 0;
var error = StartTrace(ref handle, trace_props.LoggerName, &trace_props);
if (error != 0)
{
throw new Win32Exception(error);
}
return handle;
}
public static unsafe void StopTraceHelper(ulong handle)
{
var trace_props = NewEventTraceProperties(false);
var error = StopTrace(handle, null, &trace_props);
if (error != 0)
{
throw new Win32Exception(error);
}
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
private static extern unsafe int StartTrace(ref ulong SessionHandle, void* SessionName, void* Properties);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
private static extern unsafe int StopTrace(ulong SessionHandle, void* SessionName, void* Properties);
private static unsafe EVENT_TRACE_PROPERTIES NewEventTraceProperties(bool isRealtime)
{
var trace_props = new EVENT_TRACE_PROPERTIES();
trace_props.Wnode.BufferSize = (uint)sizeof(EVENT_TRACE_PROPERTIES);
trace_props.Wnode.Flags = 0x20000;
//system trace control guid - only needed for 'NT Kernel Logger', forbidden otherwise...
//trace_props.Wnode.Guid = new Guid(0x9E814AAD, 0x3204, 0x11D2, 0x9A, 0x82, 0x00, 0x60, 0x08, 0xA8, 0x69, 0x39);
trace_props.LoggerNameOffset = 0x78 /*offsetof(LoggerName)*/;
if (!isRealtime)
{ //Left at 0 for a real time session
trace_props.LogFileNameOffset = 0x280 /*offsetof(LogFileName)*/;
}
return trace_props;
}
[StructLayout(LayoutKind.Sequential)]
private unsafe struct EVENT_TRACE_PROPERTIES
{
public WNODE_HEADER Wnode;
public uint BufferSize;
public uint MinimumBuffers;
public uint MaximumBuffers;
public uint MaximumFileSize;
public uint LogFileMode;
public uint FlushTimer;
public uint EnableFlags;
public int AgeLimit;
public uint NumberOfBuffers;
public uint FreeBuffers;
public uint EventsLost;
public uint BuffersWritten;
public uint LogBuffersLost;
public uint RealTimeBuffersLost;
public ulong LoggerThreadId;
public uint LogFileNameOffset;
public uint LoggerNameOffset;
public fixed char LoggerName[260];
public fixed char LogFileName[260];
}
[StructLayout(LayoutKind.Sequential)]
private struct WNODE_HEADER
{
public uint BufferSize;
public uint ProviderId;
public ulong HistoricalContext;
public ulong TimeStamp;
public Guid Guid;
public uint ClientContext; // Determines the time stamp resolution
public uint Flags;
}
[DllImport("advapi32.dll")]
private static extern unsaf
e int TraceSetInformation(ulong TraceHandle, TRACE_QUERY_INFO_CLASS InformationClass, void* TraceInformation, int InformationLength);
private static unsafe void TurnOnProviderBinaryTracking(ulong handle)
{
bool enabled = true;
var error = TraceSetInformation(handle, TRACE_QUERY_INFO_CLASS.TraceProviderBinaryTracking, &enabled, sizeof(bool));
if (error != 0)
{
throw new Win32Exception(error);
}
}
[DllImport("advapi32.dll")]
internal static extern unsafe int EnumerateTraceGuidsEx(TRACE_QUERY_INFO_CLASS TraceQueryInfoClass, void* InBuffer, int InBufferSize, void* OutBuffer, int OutBufferSize, ref int ReturnLength);
internal enum TRACE_QUERY_INFO_CLASS
{
TraceGuidQueryList,
TraceGuidQueryInfo,
TraceGuidQueryProcess,
TraceStackTracingInfo,
TraceSystemTraceEnableFlagsInfo,
TraceSampledProfileIntervalInfo,
TraceProfileSourceConfigInfo,
TraceProfileSourceListInfo,
TracePmcEventListInfo,
TracePmcCounterListInfo,
TraceSetDisallowList,
TraceVersionInfo,
TraceGroupQueryList,
TraceGroupQueryInfo,
TraceDisallowListQuery,
TraceCompressionInfo,
TracePeriodicCaptureStateListInfo,
TracePeriodicCaptureStateInfo,
TraceProviderBinaryTracking,
TraceMaxLoggersQuery,
MaxTraceSetInfoClass
};
[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
internal static extern unsafe int EnableTraceEx2(ulong TraceHandle, in Guid ProviderId, uint ControlCode, byte Level, ulong MatchAnyKeyword, ulong MatchAllKeyword, int Timeout, void* EnableParameters);
static unsafe Guid[] GetActiveProviderGuids()
{
int buffSize = 0;
var hr = EnumerateTraceGuidsEx(TRACE_QUERY_INFO_CLASS.TraceGuidQueryList, null, 0, null, 0, ref buffSize);
if (hr != 122 && hr != 0)
{
throw new Win32Exception(hr);
}
var buffer = new Guid[buffSize / sizeof(Guid)];
var bufferBytes = MemoryMarshal.AsBytes<Guid>(buffer);
fixed(byte* buffPtr = bufferBytes)
{
hr = EnumerateTraceGuidsEx(TRACE_QUERY_INFO_CLASS.TraceGuidQueryList, null, 0, buffPtr, buffSize, ref buffSize);
}
if (hr != 0)
{
throw new Win32Exception(hr);
}
return buffer;
}
static readonly Guid KernelEventTracing = new Guid("B675EC37-BDB6-4648-BC92-F3FDC74D3CA2");
var guids = GetActiveProviderGuids();
var handle = StartTraceHelper("BinaryTrackingTest", "test.etl");
TurnOnProviderBinaryTracking(handle);
unsafe
{
EnableTraceEx2(handle, in KernelEventTracing, 1, 0, 0 ,0, 100, null);
for (int i = 0; i < guids.Length; i++)
{
if(guids[i] == KernelEventTracing) { continue; }
//Setting level to 1 - TRACE_LEVEL_CRITICAL - because some providers have large runup/rundown events
//Also setting timeout so that providers are started synchronously - it seems if we stop before the
//start goes through, binary tracking doesn't log any event
//Enable
EnableTraceEx2(handle, in guids[i], 1, 1, 0, 0, 10, null);
//Capture State
EnableTraceEx2(handle, in guids[i], 2, 1, 0, 0, 10, null);
//Disable
EnableTraceEx2(handle, in guids[i], 0, 1, 0, 0, 0, null);
}
}
StopTraceHelper(handle);
Console.WriteLine("Job's Done");
@Zhentar
Copy link
Author

Zhentar commented Jul 3, 2019

@mattifestation as thanks for your very useful TLGMetadataParser, I wanted to share this with you :)
This does a number of things I suspect you'd find interesting....

  • Turns on "Provider Binary Tracking". This poorly named, poorly documented feature is kind of the inverse of TLGMetadataParser - once enabled, when you turn on a TraceLogging provider in a logging session, it logs an extra event that shows the binary that registered the provider (much less useful than the next bullet point, but the 'tracking' part is actually always on, so it works for providers that had already registered before you started tracing).
  • Turns on the Microsoft-Windows-Kernel-EventTracing provider. By far the most interesting event in that provider is opcode 8, ETW_OPCODE_REGISTER - which is logged every time an event provider registers itself. If you also turn on stack traces for it (not done here), you can identify the DLL behind every single provider in a process.
  • Shows how to turn on every single registered provider in the system
  • Sends a capture state command to each provider, which causes self-manifested providers (e.g. .NET EventSource providers) to emit their manifest; this lets you get some manifests that aren't available through logman & friends

@mattifestation
Copy link

Great stuff! Really cool to see others tapping in to event tracing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment