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("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(); }
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(); }
public void it_should_read_provider_id() { var provider = new Provider(PowerShellEvent.ProviderId); provider.OnEvent += e => { Assert.AreEqual(PowerShellEvent.ProviderId, e.ProviderId); }; trace.Enable(provider); proxy.PushEvent(PowerShellEvent.CreateRecord( String.Empty, String.Empty, String.Empty)); }
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(); }
public void given_invalid_property_name_parse_should_throw() { var provider = new Provider(WinINetEvent.ProviderId); provider.OnEvent += e => e.GetAnsiString("InvalidName"); trace.Enable(provider); proxy.PushEvent(WinINetEvent.CreateRecord( String.Empty, String.Empty, 0u)); }
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(); }
internal void EnableProvider(PSEtwUserProvider provider) { lock (_sync) { if (!_isRunning) { provider.EnsureDefaultHandlerSetup(DefaultEventRecordHandler); _trace.Enable(provider.Provider); } else { throw new TraceAlreadyRunningException(); } } }
public void it_should_raise_OnEvent_for_raw_provider_on_user_trace() { var called = false; var trace = new UserTrace(); var proxy = new Proxy(trace); var provider = new RawProvider(PowerShellEvent.ProviderId); provider.OnEvent += e => { called = true; }; trace.Enable(provider); proxy.PushEvent(PowerShellEvent.CreateRecord("user data", "context info", "payload")); Assert.IsTrue(called, "proxy call raised on event"); }
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"); }
public void when_requesting_mismatched_type_in_debug_it_should_throw() { var data = 200u; var prop = WinINetEvent.Status; var provider = new Provider(WinINetEvent.ProviderId); provider.OnEvent += e => Assert.AreEqual(data, e.GetInt32(prop)); trace.Enable(provider); proxy.PushEvent(WinINetEvent.CreateRecord( String.Empty, String.Empty, data)); }
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(); }
public void schema_not_found_should_raise_onerror_on_user_trace() { var onEventCalled = false; var onErrorCalled = false; var trace = new UserTrace(); var proxy = new Proxy(trace); var provider = new Provider(PowerShellEvent.ProviderId); provider.OnEvent += e => { onEventCalled = true; }; provider.OnError += e => { onErrorCalled = true; }; var record = PowerShellEvent.CreateRecord("user data", "context info", "payload"); // munge the event so the schema can't be found record.Id = (ushort)1234; trace.Enable(provider); proxy.PushEvent(record); Assert.IsFalse(onEventCalled, "schema not found raised OnEvent"); Assert.IsTrue(onErrorCalled, "schema not found raised OnError"); }
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(); } }
/// <summary> /// This function build an ETW usertrace from /// a configuration INI file and a selected IETWWriter /// </summary> /// <param name="config">configuration that come from an ini file</param> /// <param name="writer">how to writer etw</param> /// <returns></returns> public static UserTrace BuildFromConfig(IniData config, IETWWriter writer) { var providers = new Dictionary <Guid, Provider>(); foreach (var providerConfig in config.Sections) { // try to parse filtering provider var providerDeclaration = providerConfig.SectionName.Split(new string[] { "://" }, StringSplitOptions.RemoveEmptyEntries); if (providerDeclaration.Length > 2) { continue; } string providerName = providerDeclaration[0]; // Try to parse provider name ProviderGuid providerGuid = null; if (!ProviderGuid.TryParse(providerName, out providerGuid)) { continue; } Forwarder forwarder = null; if (!Forwarder.TryBuild(providerGuid, out forwarder)) { continue; } UInt16?eventId = null; if (providerDeclaration.Length == 2) { UInt16 tmp = 0; if (!UInt16.TryParse(providerDeclaration[1], out tmp)) { continue; } eventId = tmp; } if (!providers.ContainsKey(providerGuid.Guid)) { providers.Add(providerGuid.Guid, new Provider(providerGuid.Guid)); } var provider = providers[providerGuid.Guid]; var predicate = Filter.AnyEvent(); if (eventId != null) { predicate = Filter.EventIdIs(eventId.Value); } var filter = new EventFilter(predicate); foreach (var keyValue in providerConfig.Keys) { forwarder.AddFilter(keyValue.KeyName, keyValue.Value); } filter.OnEvent += (IEventRecord record) => { try { forwarder.Forward(record, writer).Wait(); } catch (System.AggregateException) { } // Some event ae not documented even for Microsoft }; provider.AddFilter(filter); } if (providers.Count == 0) { throw new Exception("Unable to create a trace without provider"); } UserTrace trace = new UserTrace(String.Format("Splunk-ETW-{0}", Guid.NewGuid().ToString())); foreach (var provider in providers.Values) { trace.Enable(provider); } return(trace); }
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(); }