public static void Start() { var trace = new UserTrace("UserTrace007_StackTrace"); var provider = new Provider("Microsoft-Windows-Kernel-Process"); provider.Any = 0x10; // WINEVENT_KEYWORD_PROCESS provider.TraceFlags |= TraceFlags.IncludeStackTrace; var processFilter = new EventFilter(Filter.EventIdIs(1)); // ProcessStart processFilter.OnEvent += (record) => { var pid = record.GetUInt32("ProcessID"); var imageName = record.GetUnicodeString("ImageName"); Console.WriteLine($"{record.TaskName} pid={pid} ImageName={imageName}\nCallStack:"); foreach (var returnAddress in record.GetStackTrace()) { Console.WriteLine($" 0x{returnAddress.ToUInt64():x}"); } }; provider.AddFilter(processFilter); trace.Enable(provider); trace.Start(); }
static void Main(string[] args) { var filter = new EventFilter(Filter .EventIdIs(3018) .Or(Filter.EventIdIs(3020))); filter.OnEvent += (IEventRecord r) => { var query = r.GetUnicodeString("QueryName"); var result = r.GetUnicodeString("QueryResults"); TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1); int secondsSinceEpoch = (int)t.TotalSeconds; Console.WriteLine($"{secondsSinceEpoch} | {r.Id} | {query} | {result}"); }; var provider = new Provider("Microsoft-Windows-DNS-Client"); provider.AddFilter(filter); var trace = new UserTrace(); trace.Enable(provider); Console.CancelKeyPress += (sender, eventArg) => { if (trace != null) { trace.Stop(); } }; trace.Start(); }
static void Main(string[] args) { var trace = new UserTrace(); var processProvider = new Provider("Microsoft-Windows-Kernel-Process"); processProvider.All = 0x40; // Enable the WINEVENT_KEYWORD_IMAGE flag. var filter = new EventFilter(Filter.EventIdIs(5)); filter.OnEvent += (record) => { var dllName = record.GetUnicodeString("ImageName", "<UNKNOWN>"); if (dllName.ToLower().EndsWith("mscoree.dll")) { var pid = record.GetUInt32("ProcessID", 0); var processName = string.Empty; try { processName = System.Diagnostics.Process.GetProcessById((int)pid).ProcessName; } catch (Exception) { } Console.WriteLine($"{processName} (PID: {pid}) loaded .NET runtime ({dllName})"); } }; processProvider.AddFilter(filter); trace.Enable(processProvider); Console.CancelKeyPress += (sender, eventArg) => { if (trace != null) { trace.Stop(); } }; trace.Start(); }
public static void Start() { var trace = new UserTrace("UserTrace006_Rundown"); // Rundown events are not true real-time tracing events. Instead they describe the state of the system. // Usually these are just extra events in the provider. For example, Microsoft-Windows-Kernel-Process // has ProcessRundown events as well as ProcessStart events. var provider = new Provider("Microsoft-Windows-Kernel-Process"); provider.Any = 0x10; // WINEVENT_KEYWORD_PROCESS // ...but the rundown events often cannot be enabled by keyword alone. // The trace needs to be sent EVENT_CONTROL_CODE_CAPTURE_STATE. // This is what EnableRundownEvents() does. provider.EnableRundownEvents(); // real-time process start events var processFilter = new EventFilter(Filter.EventIdIs(1)); // ProcessStart processFilter.OnEvent += ProcessEventHandler; provider.AddFilter(processFilter); // process rundown events - i.e. running processes var processRundownFilter = new EventFilter(Filter.EventIdIs(15)); // ProcessRundown processRundownFilter.OnEvent += ProcessEventHandler; provider.AddFilter(processRundownFilter); trace.Enable(provider); trace.Start(); }
static void Main(string[] args) { var trace = new UserTrace(); // The name of the PowerShell provider that gives us with detailed // method execution logging is "Microsoft-Windows-PowerShell". // // If you want to explore all the events in this provider, // you'll need to use Message Analyzer to load the trace and explore // the events. // // Download: https://www.microsoft.com/en-us/download/details.aspx?id=44226 var powershellProvider = new Provider("Microsoft-Windows-PowerShell"); var powershellFilter = new EventFilter( Filter.EventIdIs(7937) .And(UnicodeString.Contains("Payload", "Started"))); powershellFilter.OnEvent += OnEvent; // The "Any" and "All" flags can be sussed out using Microsoft Message Analyzer. powershellProvider.Any = 0x20; powershellProvider.AddFilter(powershellFilter); trace.Enable(powershellProvider); // This is a blocking call. Ctrl-C to stop. trace.Start(); }
static void Main(string[] args) { var count = 0; var cts = new CancellationTokenSource(); var trace = new UserTrace("MY AWESOME TEST THING"); //var provider = new RawProvider(EventSource.GetGuid(typeof(TestEventSource))); var provider = new Provider(Guid.Parse("{A0C1853B-5C40-4B15-8766-3CF1C58F985A}")); // Only pull in method invocations var powershellFilter = new EventFilter(Filter.EventIdIs(7937) .And(UnicodeString.Contains("Payload", "Started"))); powershellFilter.OnEvent += e => { Console.WriteLine($"{e.ProviderName} - {e.Id}: {count++}"); }; provider.AddFilter(powershellFilter); Console.CancelKeyPress += (s, e) => { cts.Cancel(); trace.Stop(); }; trace.Enable(provider); var statsLoop = Task.Run(() => PrintStats(trace, cts.Token)); Task.Run(() => trace.Start()) .ContinueWith(t => Console.WriteLine($"Task ended with status {t.Status}")); Console.WriteLine("Enter to restart trace"); Console.ReadKey(); Task.Run(() => trace.Start()) .ContinueWith(t => Console.WriteLine($"Task ended with status {t.Status}")); Console.WriteLine("Ctrl+C to quit"); statsLoop.Wait(); Console.WriteLine("Done"); }
static void Main(string[] args) { // UserTrace instances should be used for any non-kernel traces that are defined // by components or programs in Windows. They can optionally take a name -- if none // is provided, a random GUID is assigned as the name. var trace = new UserTrace("Silly Gooby"); // A trace can have any number of providers, which are identified by GUID. These // GUIDs are defined by the components that emit events, and their GUIDs can // usually be found with various ETW tools (like wevutil). var powershellProvider = new Provider(Guid.Parse("{A0C1853B-5C40-4B15-8766-3CF1C58F985A}")); // UserTrace providers typically have any and all flags, whose meanings are // unique to the specific providers that are being invoked. To understand these // flags, you'll need to look to the ETW event producer. powershellProvider.Any = Provider.AllBitsSet; // In user_trace_001.cs, we manually filter events by checking the information // in our callback functions. In this example, we're going to use a provider // filter to do this for us. // We instantiate an EventFilter first. An EventFilter is created with a predicate -- // literally just a function that does some check on an EventRecord and returns a boolean // (true when the even should be passed on to callbacks, false otherwise). // EventFilters are more than just convenient -- Lobster provides combinators for // expressing simple but powerful filters that actually execute in the underlying C++ // krabs library. This means that events can be filtered before ever running in the // CLR (saving us a ton of cost in spinning up objects on event firing). // The combinators cannot express everything a filter must do, so for complicated // filters, it's recommended to write the filters in a managed C++/CLI project and // use those to keep the perf benefits. The filters that Lobster provides are on // the Filter object (and can be combined with &&, ||, !) var filter = new EventFilter(Filter.EventIdIs(7937)); // EventFilters have attached callbacks, just like a regular provider. filter.OnEvent += (EventRecord record) => { var schema = new Schema(record); System.Diagnostics.Debug.Assert(schema.Id == 7937); Console.WriteLine("Event 7937 received"); }; // EventFilters are attached to providers. Events that are attached to the filter // will only be called when the filter allows the event through. Any events attached // to the provider directly will be called for all events that are fired by the ETW // producer. powershellProvider.AddFilter(filter); trace.Enable(powershellProvider); trace.Start(); }
static void Main(string[] args) { // UserTrace instances should be used for any non-kernel traces that are defined // by components or programs in Windows. var trace = new UserTrace(); // A trace can have any number of providers, which are identified by GUID. These // GUIDs are defined by the components that emit events, and their GUIDs can // usually be found with various ETW tools (like wevutil). var powershellProvider = new Provider(Guid.Parse("{A0C1853B-5C40-4B15-8766-3CF1C58F985A}")); // UserTrace providers typically have any and all flags, whose meanings are // unique to the specific providers that are being invoked. To understand these // flags, you'll need to look to the ETW event producer. powershellProvider.Any = Provider.AllBitsSet; // Providers should be wired up to functions that are called when // events from that provider are fired. powershellProvider.OnEvent += (EventRecord record) => { // Once an event is received, if we want krabs to help us analyze it, we need // to snap in a schema to ask it for information. var schema = new Schema(record); // We then have the ability to ask a few questions of the event. Console.WriteLine("Event " + schema.Id + " (" + schema.Name + ") received."); if (schema.Id == 7937) { // The event we're interested in has a field that contains a bunch of // info about what it's doing. We can snap in a parser to help us get // the property information out. var parser = new Parser(schema); // We need to call the specific method to parse the type we expect. // If we don't want to deal with the possibility of failure, we can // provide a default if parsing fails. var context = parser.ParseWStringWithDefault("ContextInfo", "None."); Console.WriteLine("Context: " + context); } }; // The UserTrace needs to know about the provider that we've set up. trace.Enable(powershellProvider); // Begin listening for events. This call blocks, so if you want to do other things // while this runs, you'll need to call this on another thread. trace.Start(); }
public static void Start() { // UserTrace instances should be used for any non-kernel traces that are defined // by components or programs in Windows. var trace = new UserTrace(); // A trace can have any number of providers, which are identified by GUID. These // GUIDs are defined by the components that emit events, and their GUIDs can // usually be found with various ETW tools (like wevutil). var powershellProvider = new Provider(Guid.Parse("{A0C1853B-5C40-4B15-8766-3CF1C58F985A}")); // UserTrace providers typically have any and all flags, whose meanings are // unique to the specific providers that are being invoked. To understand these // flags, you'll need to look to the ETW event producer. powershellProvider.Any = Provider.AllBitsSet; // Providers should be wired up to functions that are called when // events from that provider are fired. powershellProvider.OnEvent += (record) => { // Records have general properties that are applicable to every ETW // record regardless of schema. They give us general information. Console.WriteLine("Event " + record.Id + " (" + record.Name + ") received."); if (record.Id == 7937) { // We need to call the specific method to parse the type we expect. // If we don't want to deal with the possibility of failure, we can // provide a default if parsing fails. var context = record.GetUnicodeString("ContextInfo", "None."); Console.WriteLine("Context: " + context); } }; // The UserTrace needs to know about the provider that we've set up. trace.Enable(powershellProvider); // Begin listening for events. This call blocks, so if you want to do other things // while this runs, you'll need to call this on another thread. trace.Start(); }
static void Main(string[] args) { var filter = new EventFilter(Filter .EventIdIs(3018) .Or(Filter.EventIdIs(3020))); filter.OnEvent += (IEventRecord r) => { var query = r.GetUnicodeString("QueryName"); var result = r.GetUnicodeString("QueryResults"); Console.WriteLine($"DNS query ({r.Id}): {query} - {result}"); }; var provider = new Provider("Microsoft-Windows-DNS-Client"); provider.AddFilter(filter); var trace = new UserTrace(); trace.Enable(provider); trace.Start(); }
public static void Start() { // While Adminstrator is sufficent to view the Security EventLog, // SYSTEM is required for the Microsoft-Windows-Security-Auditing provider. if (!WindowsIdentity.GetCurrent().IsSystem) { Console.WriteLine("Microsoft-Windows-Security-Auditing can only be traced by SYSTEM"); return; } // Further, only one trace session is allowed for this provider. // This session is created by the OS and is called 'EventLog-Security'. // We can't Stop this session, but we can Open a handle to it. var trace = new UserTrace("EventLog-Security"); var provider = new Provider("Microsoft-Windows-Security-Auditing"); // We also can't modify the flags of the trace session. // This will silently fail. provider.Any = Provider.AllBitsSet; // But we can receive events - but only those configured by the audit policy. // e.g. to enable event 4703 run -> auditpol /set /subcategory:"Token Right Adjusted Events" provider.OnEvent += (record) => { Console.WriteLine($"Event {record.Id}({record.Name}) received."); if (record.Id == 4703) // "A user right was adjusted." { var enabledPrivilegeList = record.GetUnicodeString("EnabledPrivilegeList", ""); var disabledPrivilegeList = record.GetUnicodeString("DisabledPrivilegeList", ""); Console.WriteLine($"\tEnabledPrivilegeList={enabledPrivilegeList}"); Console.WriteLine($"\tDisabledPrivilegeList={disabledPrivilegeList}"); } }; trace.Enable(provider); trace.Start(); }
static void Main(string[] args) { var filter = new EventFilter( Filter.EventIdIs(5) //.Or(Filter.EventIdIs(6)) ); // Microsoft-Windows-RPC EventID 5 - Client RPC call started // EventID 6 - Server RPC call started. filter.OnEvent += (IEventRecord r) => { var endpoint = r.GetUnicodeString("Endpoint"); var opNum = r.GetUInt32("ProcNum"); var protocol = r.GetUInt32("Protocol"); Console.WriteLine($"RPC Event {r.Id}"); Console.WriteLine($"Endpoint: {endpoint}"); Console.WriteLine($"Protocol {protocol,0:X}"); Console.WriteLine($"OpNum: {opNum}"); }; var provider = new Provider("Microsoft-Windows-RPC"); provider.AddFilter(filter); var trace = new UserTrace(); trace.Enable(provider); Console.CancelKeyPress += (sender, eventArg) => { if (trace != null) { trace.Stop(); } }; trace.Start(); }
public static void Start() { // UserTrace instances should be used for any non-kernel traces that are defined // by components or programs in Windows. They can optionally take a name -- if none // is provided, a random GUID is assigned as the name. var trace = new UserTrace(); // A trace can have any number of providers, which are identified by GUID. These // GUIDs are defined by the components that emit events, and their GUIDs can // usually be found with various ETW tools (like wevutil). var powershellProvider = new Provider(Guid.Parse("{A0C1853B-5C40-4B15-8766-3CF1C58F985A}")); // UserTrace providers typically have any and all flags, whose meanings are // unique to the specific providers that are being invoked. To understand these // flags, you'll need to look to the ETW event producer. powershellProvider.Any = Provider.AllBitsSet; // In UserTrace003.cs, we use ETW-based filtering to select a specific event ID. // // We can combine ETW-based filtering with predicate filters to filter on specific // event properties without impacting performance. var filter = new EventFilter(7937, UnicodeString.Contains("ContextInfo", "Write-Host")); // EventFilters have attached callbacks, just like a regular provider. filter.OnEvent += (record) => { System.Diagnostics.Debug.Assert(record.Id == 7937); Console.WriteLine(record.GetUnicodeString("ContextInfo")); }; // EventFilters are attached to providers. Events that are attached to the filter // will only be called when the filter allows the event through. Any events attached // to the provider directly will be called for all events that are fired by the ETW // producer. powershellProvider.AddFilter(filter); trace.Enable(powershellProvider); trace.Start(); }
static void Main(string[] args) { bool start = false; Injected_Processes_IDsList.Add("0"); Injected_Processes_IDsList.Add("0"); Console.CancelKeyPress += Console_CancelKeyPress; Temp_Thread_InfoDebugMode = new DataTable(); Temp_Thread_InfoDebugMode.Columns.Add("tid", typeof(int)); Temp_Thread_InfoDebugMode.Columns.Add("Time_Negative"); Temp_Thread_InfoDebugMode.Columns.Add("status"); Temp_Thread_InfoDebugMode.Columns.Add("tid_StartAddress_x64"); Temp_Thread_InfoDebugMode.Columns.Add("StartTime"); Temp_Thread_InfoDebugMode.Columns.Add("Proc_Name"); Temp_Thread_InfoDebugMode.Columns.Add("Proc_id"); Temp_Thread_InfoDebugMode.Columns.Add("IsNewProcess"); Temp_Thread_InfoDebugMode.Columns.Add("tid_StartAddress"); Console.ForegroundColor = ConsoleColor.DarkGray; Console.WriteLine(); Console.WriteLine("ETWMonThread 1.0 (x64 only) "); Console.WriteLine("Realtime Scanning/Monitoring Thread Injection for MPD (Meterpreter Payload Detection) by ETW"); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine("Published by Damon Mohammadbagher Jan 2018"); if (args.Length == 0) { start = true; } if (args.Length == 1) { if (args[0].ToUpper() == "IPS") { IPS_IDS = true; start = true; } else { IPS_IDS = false; start = true; } if (args[0].ToUpper() == "SHOWALL") { IsShowAllRecrds = true; start = true; } } if (args.Length >= 2) { if (args[0].ToUpper() == "IPS" && args[1].ToUpper() == "DEBUG") { IPS_IDS = true; Is_DebugMode = true; start = true; } if (args[0].ToUpper() == "SHOWALL" && args[1].ToUpper() == "DEBUG") { IsShowAllRecrds = true; Is_DebugMode = true; start = true; } } if (args.Length >= 1) { if (args[0].ToUpper() == "HELP") { start = false; Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine(); Console.WriteLine("[!] ETWMonThread , Realtime Scanning/Monitoring Thread Injection for MPD (Meterpreter Payload Detection) by ETW"); Console.ForegroundColor = ConsoleColor.DarkCyan; Console.WriteLine("[!] Syntax 1: Realtime Scanning/Monitoring IPS Mode (Killing Meterpreter Injected Threads)"); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("[!] Syntax 1: ETWMonThread.exe \"IPS\" [optional] \"DEBUG\""); Console.WriteLine("[!] Example1: ETWMonThread.exe IPS "); Console.WriteLine("[!] Example2: ETWMonThread.exe IPS DEBUG"); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.DarkCyan; Console.WriteLine("[!] Syntax 2: Realtime Monitoring IDS Mode"); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("[!] Syntax 2: ETWMonThread.exe [optional] \"SHOWALL\" [optional] \"DEBUG\" "); Console.WriteLine("[!] Example1: ETWMonThread.exe"); Console.WriteLine("[!] Example2: ETWMonThread.exe SHOWALL"); Console.WriteLine("[!] Example3: ETWMonThread.exe SHOWALL DEBUG"); Console.ForegroundColor = ConsoleColor.Gray; } } if (start) { Console.WriteLine(); if (IPS_IDS) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("[!] Realtime Scanning/Monitoring IPS Mode (warning : Killing Threads)"); Console.ForegroundColor = ConsoleColor.Gray; } else { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("[!] Realtime Monitoring IDS Mode"); Console.ForegroundColor = ConsoleColor.Gray; } /// EventID 3 is for "Thread Created" var ETWEventsFilter = new EventFilter(Filter.EventIdIs(3)); ETWEventsFilter.OnEvent += ETWEventsFilter_OnEvent; P.OnError += P_OnError; P.AddFilter(ETWEventsFilter); T.Enable(P); T.Start(); } }
public static void Start() { var trace = new UserTrace(); // WPP providers are basically legacy providers without a registered MOF. // They are intended for (internal) debugging purposes only. // Note - WPP software tracing has been superceded by TraceLogging. // // Instead of a manifest or MOF, a separate trace message format (TMF) // file is required to interpret the WPP event data. // https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/trace-message-format-file // // In some cases, the TMF is included in the PDB. // https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/tracepdb // // Otherwise, you can attempt to reconstruct the TMF by hand. // https://posts.specterops.io/data-source-analysis-and-dynamic-windows-re-using-wpp-and-tracelogging-e465f8b653f7 // // Luckily, WPP tracing is usually added using Microsoft's convenience macros. // And, when you have symbols available, WPP metadata is then fairly straightfoward to extract. // https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/adding-wpp-software-tracing-to-a-windows-driver // Each WPP trace provider defines a control GUID that uniquely identifies that provider. // https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/control-guid // // The WPP macros generate control GUID globals named "WPP_ThisDir_CTLGUID_<name>" // // For example, this control GUID in the symbols for combase.dll // WPP_ThisDir_CTLGUID_OLE32 = bda92ae8-9f11-4d49-ba1d-a4c2abca692e var ole32WppProvider = new Provider(Guid.Parse("{bda92ae8-9f11-4d49-ba1d-a4c2abca692e}")); // We use the control GUID to enable WPP tracing for the provider, and to set // the filtering level and flags. // // In evntrace.h there are ten defined trace levels - // TRACE_LEVEL_NONE 0 // Tracing is not on // TRACE_LEVEL_CRITICAL 1 // Abnormal exit or termination // TRACE_LEVEL_ERROR 2 // Severe errors that need logging // TRACE_LEVEL_WARNING 3 // Warnings such as allocation failure // TRACE_LEVEL_INFORMATION 4 // Includes non-error cases(e.g.,Entry-Exit) // TRACE_LEVEL_VERBOSE 5 // Detailed traces from intermediate steps // TRACE_LEVEL_RESERVED6 6 // TRACE_LEVEL_RESERVED7 7 // TRACE_LEVEL_RESERVED8 8 // TRACE_LEVEL_RESERVED9 9 // // Microsoft WPP providers are known to use the reserved levels. // Internally, these levels have names like CHATTY, GARRULOUS and LOQUACIOUS. // // Everything at or below the configured level will be traced. // Technically 9 means trace everything, but the field is a UCHAR // so 0xFF means definitely trace everything. ole32WppProvider.Level = 0xFF; // 'TRACE_LEVEL_ALL' // Flags is a user-defined bitmask field the developer can use to group // related messages. // Again, it is a UCHAR for WPP providers so 0xFF means trace everything. ole32WppProvider.Any = 0xFF; // 'TRACE_FLAGS_ALL' // WPP events are also a slightly different format to the modern ETW events. In particular, // they include a message GUID rather than the control GUID. In order to convince krabs to // forward events to us, we need to register an extra 'provider' using the message GUID. // // The WPP macros generate message GUID globals named "WPP_<guid>_Traceguids" // // For example, these message GUIDs in the symbols for combase.dll // WPP_c0e4dd87b1523146a49921a43cd25160_Traceguids = c0e4dd87-b152-3146-a499-21a43cd25160 // WPP_c1647dce9b833d97edb9721fff5f0606_Traceguids = c1647dce-9b83-3d97-edb9-721fff5f0606 var messageGuid_S = new Provider(Guid.Parse("{c0e4dd87-b152-3146-a499-21a43cd25160}")); messageGuid_S.OnEvent += (record) => { // krabs does not currently support TMF files for parsing WPP messages. // Instead you need to manually parse the UserData. // // The WPP macros generate logging staging functions named "WPP_SF_<format specifiers>" // In this case this message GUID is always associate with a WPP_SF_S(...) call. // This tells us that the event contains a single unicode string. var message = Marshal.PtrToStringUni(record.UserData); Console.WriteLine($"Id:{record.Id} WPP_SF_S({message})"); }; var messageGuid_ssdDsS = new Provider(Guid.Parse("{c1647dce-9b83-3d97-edb9-721fff5f0606}")); messageGuid_ssdDsS.OnEvent += (record) => { // WPP_SF_ssdDsS(...) var userData = record.UserData; var string_1 = Marshal.PtrToStringAnsi(userData); userData += string_1.Length + 1; var string_2 = Marshal.PtrToStringAnsi(userData); userData += string_2.Length + 1; var int32_3 = Marshal.ReadInt32(userData); userData += sizeof(Int32); var uint32_4 = (UInt32)Marshal.ReadInt32(userData); userData += sizeof(UInt32); var string_5 = Marshal.PtrToStringAnsi(userData); userData += string_5.Length + 1; var string_6 = Marshal.PtrToStringUni(userData); Console.WriteLine($"Id:{record.Id} WPP_SF_ssdDsS({string_1}, {string_2}, {int32_3}, {uint32_4}, {string_5}, {string_6})"); }; // Side note - if you want to turn up the verbosity of your COM WPP diagnostic tracing, then enable // OLE32 tracing via the registry following the instruction here - // https://support.microsoft.com/en-us/help/926098/how-to-enable-com-and-com-diagnostic-tracing // // Alternatively call _ControlTracing (4) via combase's 18f70770-8e64-11cf-9af1-0020af6e72f4 RPC interface. trace.Enable(ole32WppProvider); trace.Enable(messageGuid_S); trace.Enable(messageGuid_ssdDsS); trace.Start(); }
public static void Start() { var trace = new UserTrace("WPP_OLE32"); // WPP providers are basically legacy providers without a registered MOF. // They are intended for (internal) debugging purposes only. // Note - WPP software tracing has been superceded by TraceLogging. // // Instead of a manifest or MOF, a separate trace message format (TMF) // file is required to interpret the WPP event data. // https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/trace-message-format-file // // In some cases, the TMF is included in the PDB. // https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/tracepdb // // Otherwise, you can attempt to reconstruct the TMF by hand. // https://posts.specterops.io/data-source-analysis-and-dynamic-windows-re-using-wpp-and-tracelogging-e465f8b653f7 // // Luckily, WPP tracing is usually added using Microsoft's convenience macros. // And, when you have symbols available, WPP metadata is then fairly straightfoward to extract. // https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/adding-wpp-software-tracing-to-a-windows-driver // Each WPP trace provider defines a control GUID that uniquely identifies that provider. // https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/control-guid // // The WPP macros generate control GUID globals named "WPP_ThisDir_CTLGUID_<name>" // // For example, this control GUID is in the symbols for combase.dll // WPP_ThisDir_CTLGUID_OLE32 = bda92ae8-9f11-4d49-ba1d-a4c2abca692e var ole32WppProvider = new Provider(Guid.Parse("{bda92ae8-9f11-4d49-ba1d-a4c2abca692e}")); // In evntrace.h there are ten defined trace levels - // TRACE_LEVEL_NONE 0 // Tracing is not on // TRACE_LEVEL_CRITICAL 1 // Abnormal exit or termination // TRACE_LEVEL_ERROR 2 // Severe errors that need logging // TRACE_LEVEL_WARNING 3 // Warnings such as allocation failure // TRACE_LEVEL_INFORMATION 4 // Includes non-error cases(e.g.,Entry-Exit) // TRACE_LEVEL_VERBOSE 5 // Detailed traces from intermediate steps // TRACE_LEVEL_RESERVED6 6 // TRACE_LEVEL_RESERVED7 7 // TRACE_LEVEL_RESERVED8 8 // TRACE_LEVEL_RESERVED9 9 // // Microsoft WPP providers are known to use the reserved levels. // Internally, these levels have names like CHATTY, GARRULOUS and LOQUACIOUS. // // Everything at or below the configured level will be traced. // Technically 9 means trace everything, but the field is a UCHAR // so 0xFF means definitely trace everything. ole32WppProvider.Level = 0xFF; // 'TRACE_LEVEL_ALL' // Flags is a user-defined bitmask field the developer can use to group // related messages. // Again, it is a UCHAR for WPP providers so 0xFF means trace everything. ole32WppProvider.Any = 0xFF; // 'TRACE_FLAGS_ALL' // We need to enable this provider in order for krabs to correctly enable the OLE32 WPP events. trace.Enable(ole32WppProvider); // But we can't add any callbacks directly to krabs WPP providers though. Without the TMF // information, krabs cannot determine which provider the event belongs to. // // WPP providers, like MOF providers, return the message GUID in the ProviderId field. // So firstly krabs checks if the message GUID matches a provider GUID. // If you know the message GUIDs then you can create individual dummy providers for those. // // Secondly krabs queries TDH to see if it knows the provider GUID for the message GUID. // https://docs.microsoft.com/en-us/windows/win32/etw/retrieving-event-data-using-tdh // This works for registered MOF providers - but not for WPP providers. In this case, TDH returns // an all zero GUID - so we can create a dummy provider for that and add our callbacks there instead. // If you subscribe to multiple WPP providers, the events from *all* of them will be delivered to this dummy provider. var allWppDummyProvider = new Provider(Guid.Empty); allWppDummyProvider.OnEvent += (record) => { // Here be dragons. // // krabs does not currently support TMF files for parsing WPP messages. // Instead you need to manually parse the UserData. // // The WPP macros generate message GUID globals named "WPP_<guid>_Traceguids" // They also generate logging staging functions named "WPP_SF_<format specifiers>" // // There seems to be a one-to-one mapping between message GUIDs and staging functions. // WPP events are a slightly different format to the modern ETW events. In particular, // they include this message GUID rather than the provider's control GUID. // // So message GUIDs would be a good candidate for filtering... // ... but my experience is that they may change between builds. // So I've subscribed to the zero GUID instead. // // Event ids seem more stable, but they are only unique per message GUID. // // In this case, combase.dll only has two logging staging functions. // WPP_SF_S(...) - which tells us that the event contains a single unicode string. // WPP_SF_ssdDsS(...) - which tells us that there are 3 ansi strings, a unicode string and dword. // // So we can brute force the format... var message = $"Message:{record.ProviderId} Id:{record.Id} "; var userData = record.UserData; var string_1 = Marshal.PtrToStringAnsi(record.UserData); if (string_1.Length != 1) // definitely an ansi string... { // WPP_SF_ssdDsS(...) userData += string_1.Length + 1; var string_2 = Marshal.PtrToStringAnsi(userData); userData += string_2.Length + 1; var int32_3 = Marshal.ReadInt32(userData); userData += sizeof(Int32); var uint32_4 = (UInt32)Marshal.ReadInt32(userData); userData += sizeof(UInt32); var string_5 = Marshal.PtrToStringAnsi(userData); userData += string_5.Length + 1; var string_6 = Marshal.PtrToStringUni(userData); message += $"WPP_SF_ssdDsS({string_1}, {string_2}, {int32_3}, {uint32_4}, {string_5}, {string_6})"; } else // probably a unicode string... (but possibly a single character ansi string) { // WPP_SF_S(...) string_1 = Marshal.PtrToStringUni(record.UserData); message += $"WPP_SF_S({string_1})"; } // In this example we only print messages that contain COM class ids. if (message.Contains(" clsid")) { Console.WriteLine(message); } }; trace.Enable(allWppDummyProvider); // Side note - if you want to turn up the verbosity of your COM WPP diagnostic tracing, then enable // OLE32 tracing via the registry following the instruction here - // https://support.microsoft.com/en-us/help/926098/how-to-enable-com-and-com-diagnostic-tracing // // Alternatively call _ControlTracing (4) via combase's 18f70770-8e64-11cf-9af1-0020af6e72f4 RPC interface. trace.Start(); }
internal void Reset() { _task = new Task(() => { _trace.Start(); }, _cts.Token, TaskCreationOptions.LongRunning); }