daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

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