etw-telemetry.md (12894B)
1 --- 2 title: "ETW as a Telemetry Source" 3 description: "Providers, keywords, sessions and consumers: how to stand up an Event Tracing for Windows session from the command line and read what the kernel already knows about processes and image loads." 4 category: dfir 5 subcategory: "Windows Internals" 6 tags: [etw, telemetry, windows, detection-engineering, logging] 7 tools: [logman, etwexplorer, event-viewer, traceevent] 8 difficulty: intermediate 9 updated: 2026-10-04 10 upstreamName: "ired.team" 11 upstreamUrl: "https://ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/etw-event-tracing-for-windows-101" 12 upstreamAuthor: "Mantvydas Baranauskas" 13 upstreamLicense: none 14 upstreamRelation: derived 15 references: 16 - name: "About Event Tracing — Microsoft Learn" 17 url: "https://learn.microsoft.com/en-us/windows/win32/etw/about-event-tracing" 18 relation: inspired 19 note: "Normative definitions of provider, controller, consumer and session." 20 - name: "EtwExplorer" 21 url: "https://github.com/zodiacon/EtwExplorer" 22 author: "Pavel Yosifovich" 23 relation: inspired 24 note: "Reads a provider's registered manifest, which is the only practical way to see what fields an event actually carries." 25 - name: "TraceEvent" 26 url: "https://github.com/microsoft/perfview" 27 author: "Microsoft" 28 license: MIT 29 relation: inspired 30 note: "The managed library behind the C# consumer pattern in the Walkthrough." 31 - name: "Tampering with Windows Event Tracing" 32 url: "https://medium.com/palantir/tampering-with-windows-event-tracing-background-offense-and-defense-4be7ac62ac63" 33 author: "Palantir" 34 relation: inspired 35 note: "The tamper-detection framing in What it tells you." 36 --- 37 38 ## What this is 39 40 Most of what a Windows endpoint agent knows, it learns from Event Tracing for Windows. The 41 kernel and thousands of user-mode components emit structured events continuously; ETW is the 42 plumbing that lets a process subscribe to a subset of them without a driver, without a hook, 43 and without admin rights in many cases. Sysmon's process and image-load data is ETW data. 44 Understanding the plumbing matters for two reasons: you can build a collector against the 45 same events an EDR uses, and you can reason about what happens when an attacker interferes 46 with the session rather than with the events. 47 48 Five terms carry the whole model: 49 50 | Term | What it is | 51 |---|---| 52 | Provider | A component that emits events. Registered system-wide, identified by name and GUID. | 53 | Keyword | A bitmask flag on a provider selecting a class of events it can emit. | 54 | Session | A live trace. Buffers whatever its enabled providers emit, to memory or to an `.etl` file. | 55 | Controller | Something that creates sessions and enables or disables providers in them. `logman.exe` is one. | 56 | Consumer | Something that subscribes to a session and processes events as they arrive. | 57 58 A session is independent of any consumer. It can run with no provider enabled, recording 59 nothing, and it can record to disk with nobody listening. 60 61 ## Prerequisites 62 63 - `logman.exe` ships in-box, no install. 64 - Creating a kernel-provider session needs an elevated prompt. Querying providers does not. 65 - A consumer written against `Microsoft.Diagnostics.Tracing.TraceEvent` needs that NuGet 66 package and .NET. 67 - [EtwExplorer](https://github.com/zodiacon/EtwExplorer) for reading provider manifests. The 68 keyword list `logman` prints tells you event *classes*; the manifest tells you the field 69 names, which is what you need before you can write a detection against an event. 70 71 ## Walkthrough 72 73 ### Enumerate what the machine can tell you 74 75 ```powershell 76 logman query providers 77 ``` 78 79 Several thousand providers on a modern build. The interesting ones for security work are the 80 kernel providers, because they see things a user-mode component cannot lie about. 81 82 <figure class="shot"> 83 <img src="https://raw.githubusercontent.com/mantvydasb/RedTeaming-Tactics-and-Techniques/8cdbdd60eb4a8997e689649f3911f7c893e59ed9/.gitbook/assets/image%20%28532%29.png" 84 alt="Console output of logman query providers listing registered ETW provider names beside their GUIDs" 85 loading="lazy" referrerpolicy="no-referrer"> 86 <figcaption>Every provider registered on the host, with its GUID. 87 <span class="shot-credit">ired.team · Mantvydas Baranauskas</span> 88 </figcaption> 89 </figure> 90 91 Narrow to one provider, by name or by GUID, to get its keyword table: 92 93 ```powershell 94 logman query providers Microsoft-Windows-Kernel-Process 95 logman query providers "{22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}" 96 ``` 97 98 `Microsoft-Windows-Kernel-Process` is the one to start with. Its keywords cover process 99 start and exit, thread start and exit, and image load and unload — the three event families 100 that almost every behavioural detection is built on. 101 102 ### Read the manifest, not just the keyword names 103 104 A keyword tells you an event class exists. It does not tell you whether the event carries a 105 command line, a parent PID, or an image hash. EtwExplorer parses the provider's registered 106 manifest and shows the per-event field layout. 107 108 <figure class="shot"> 109 <img src="https://raw.githubusercontent.com/mantvydasb/RedTeaming-Tactics-and-Techniques/8cdbdd60eb4a8997e689649f3911f7c893e59ed9/.gitbook/assets/image%20%28534%29.png" 110 alt="EtwExplorer window showing the Microsoft-Windows-Kernel-Process manifest with its event definitions and per-event field names" 111 loading="lazy" referrerpolicy="no-referrer"> 112 <figcaption>The <code>Microsoft-Windows-Kernel-Process</code> manifest in EtwExplorer — event definitions and the fields each one carries. 113 <span class="shot-credit">ired.team · Mantvydas Baranauskas</span> 114 </figcaption> 115 </figure> 116 117 ### Create a session and enable a provider in it 118 119 A session first, with no providers: 120 121 ```powershell 122 logman create trace daemon-trace -ets 123 logman query daemon-trace -ets 124 ``` 125 126 The query output names the `.etl` the session writes to. At this point the session is 127 running and recording nothing, which is worth seeing once — it is the state an EDR session 128 is left in when someone removes its providers rather than stopping it. 129 130 Keywords are a bitmask, so you OR the classes you want and pass the result. For 131 `Microsoft-Windows-Kernel-Process`, `WINEVENT_KEYWORD_PROCESS` is `0x10` and 132 `WINEVENT_KEYWORD_IMAGE` is `0x40`, giving `0x50`: 133 134 ```powershell 135 logman update daemon-trace -p Microsoft-Windows-Kernel-Process 0x50 -ets 136 logman query daemon-trace -ets 137 ``` 138 139 <figure class="shot"> 140 <img src="https://raw.githubusercontent.com/mantvydasb/RedTeaming-Tactics-and-Techniques/8cdbdd60eb4a8997e689649f3911f7c893e59ed9/.gitbook/assets/image%20%28538%29.png" 141 alt="logman query output for a trace session showing the Microsoft-Windows-Kernel-Process provider enabled with keyword mask 0x50" 142 loading="lazy" referrerpolicy="no-referrer"> 143 <figcaption>The session now carries one provider at keywords <code>0x50</code>. 144 <span class="shot-credit">ired.team · Mantvydas Baranauskas</span> 145 </figcaption> 146 </figure> 147 148 ### Read the .etl 149 150 Event Viewer opens a saved `.etl` directly. From this provider you get event ID 1 for 151 process start, 2 for process exit, 5 for image load and 6 for image unload. 152 153 <figure class="shot"> 154 <img src="https://raw.githubusercontent.com/mantvydasb/RedTeaming-Tactics-and-Techniques/8cdbdd60eb4a8997e689649f3911f7c893e59ed9/.gitbook/assets/image%20%28539%29.png" 155 alt="Event Viewer displaying an ETW process creation event, ID 1, read out of a saved .etl trace file" 156 loading="lazy" referrerpolicy="no-referrer"> 157 <figcaption>Process creation, event ID 1, read back out of the <code>.etl</code>. 158 <span class="shot-credit">ired.team · Mantvydas Baranauskas</span> 159 </figcaption> 160 </figure> 161 162 ### Tear the session down 163 164 Removing a provider leaves the session alive but blind. Stopping the session removes it 165 entirely. Both matter, because the two look different in an audit. 166 167 ```powershell 168 logman update trace daemon-trace --p Microsoft-Windows-Kernel-Process 0x50 -ets 169 logman stop daemon-trace -ets 170 ``` 171 172 Note the double dash on `--p`: that is the remove form, and a single dash would re-add. 173 174 ### Which providers a given process writes to 175 176 ```powershell 177 logman query providers -pid $pid 178 ``` 179 180 Useful in reverse: given a suspicious process, this says what telemetry it is itself 181 registered to emit. 182 183 ### Consuming live rather than from a file 184 185 For anything continuous you want a consumer, not a file. The managed route is the 186 `TraceEvent` library: open a `TraceEventSession`, enable the kernel provider with the 187 keywords you want, attach handlers to the parsed event stream, then pump the source. 188 189 ```csharp 190 using var session = new TraceEventSession("daemon-live"); 191 session.EnableKernelProvider( 192 KernelTraceEventParser.Keywords.Process | 193 KernelTraceEventParser.Keywords.ImageLoad); 194 195 session.Source.Kernel.ProcessStart += e => 196 Console.WriteLine($"{e.TimeStamp:O} pid={e.ProcessID} ppid={e.ParentID} {e.CommandLine}"); 197 session.Source.Kernel.ImageLoad += e => 198 Console.WriteLine($"{e.TimeStamp:O} pid={e.ProcessID} load {e.FileName}"); 199 200 session.Source.Process(); 201 ``` 202 203 `ProcessStart` carries the command line and the parent PID in the same event, which is why 204 a consumer like this is better ground truth for parent-child analysis than reconstructing it 205 from `pslist` after the fact — the parent may already be gone. 206 207 <figure class="shot"> 208 <img src="https://raw.githubusercontent.com/mantvydasb/RedTeaming-Tactics-and-Techniques/8cdbdd60eb4a8997e689649f3911f7c893e59ed9/.gitbook/assets/kernel-consumer.gif" 209 alt="Console application printing a live stream of colour-coded process start, process exit and image load events as they occur" 210 loading="lazy" referrerpolicy="no-referrer"> 211 <figcaption>A live consumer printing process and image-load events as they happen. 212 <span class="shot-credit">ired.team · Mantvydas Baranauskas</span> 213 </figcaption> 214 </figure> 215 216 ## What it tells you 217 218 **You can collect the primitives yourself.** Process start with a command line and a parent, 219 thread start, and image load are enough to build most of the behavioural detections on this 220 site without buying anything. The injection sheets' detection advice — 221 [process hollowing](/sheets/exploitation/process-hollowing), 222 [classic DLL injection](/sheets/exploitation/dll-injection), 223 [thread execution hijacking](/sheets/exploitation/thread-execution-hijacking) — leans on image 224 loads into processes that have no business loading that DLL, and on threads starting in a 225 process that nobody opened a handle to for a legitimate reason. Both are ETW events. 226 227 **Image load is the cheapest injection tripwire you have.** Keyword `0x40` on 228 `Microsoft-Windows-Kernel-Process` gives you every module every process maps, with a path. 229 A `LoadLibrary`-based injection shows up here as the target process loading a DLL from a 230 writable directory. Manual mapping does not show up here at all, which is itself the point: 231 absence of a load event for code that is plainly running is the anomaly. 232 233 **The session is the attack surface, not the event.** Nothing an attacker does to an 234 individual event is cheaper than interfering with the session carrying it. Two distinct 235 actions, two distinct artefacts: removing a provider from a session leaves the session 236 running and empty, and stopping the session removes it from `logman query -ets` entirely. So 237 monitor both — the set of sessions on the host, and the provider list inside each session 238 you care about. A session that exists but has lost its providers is the quieter of the two 239 and the one more likely to be missed. 240 241 **Baseline your own sessions.** Record, per host role, which sessions exist and which 242 providers each one enables. The diff is the detection. Without that baseline, a missing 243 provider is invisible, because nothing errors and nothing logs — events simply stop. 244 245 **`Microsoft-Windows-Threat-Intelligence` is the one you cannot have for free.** It carries 246 the memory-operation events that make in-memory injection visible from user mode, and it is 247 gated behind a protected-process-light signature. If your tooling does not have it, your 248 in-memory visibility comes from scanning, not from ETW — which is what the 249 [injected-thread hunting](/sheets/dfir/injected-thread-hunting) sheet covers. 250 251 ## References 252 253 - [About Event Tracing — Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/etw/about-event-tracing) 254 - [EtwExplorer](https://github.com/zodiacon/EtwExplorer) — Pavel Yosifovich 255 - [TraceEvent / PerfView](https://github.com/microsoft/perfview) — Microsoft, MIT 256 - [Tampering with Windows Event Tracing](https://medium.com/palantir/tampering-with-windows-event-tracing-background-offense-and-defense-4be7ac62ac63) — Palantir 257 - [Hunting injected threads](/sheets/dfir/injected-thread-hunting) — the scanning counterpart 258 to ETW collection