Nullpath

ETW (Event Tracing for Windows) and its evasion

concept windowsmalwareetwdefense-evasionedr updated 07 Sept 2026 · 6 min

ETW (Event Tracing for Windows)

The kernel’s event-bus that EDRs subscribe to for a faithful record of what a process did — the system calls, the module loads, the registry hits, the network connects. Unlike a user-mode hook (a detour at the API’s start, which a payload can skip via windows-syscalls), ETW fires in the kernel (or at a point the user-mode caller can’t easily bypass) — so it’s the layer that survives a direct syscall. ETW evasion is the art of making the kernel’s event-bus stop seeing your events, without the EDR noticing the bus was tampered with.

How ETW works (the model)

  1. A provider (the kernel’s Microsoft-Windows-Kernel-* providers, or an EDR’s own provider) registers event IDs (a syscall = an event, a module load = an event, etc.).
  2. A consumer (the EDR’s kernel driver) subscribes to those provider/ID pairs — a session the kernel delivers events into.
  3. When the event fires (the process calls NtCreateFile), the kernel writes the event into the session’s buffer → the EDR reads it → correlates it into a timeline.

The key: the event is generated by the kernel, not by the user-mode API. A payload that skips the user-mode kernel32 wrapper and calls NtCreateFile directly (windows-syscalls) still trips the kernel’s ETW event (the kernel knows the syscall happened). That’s why ETW is the layer a direct syscall doesn’t beat — and why EDRs lean on it.

The EDR-specific ETW (the part that matters)

  • The EDR’s own ETW provider — most modern EDRs (the ones that matter) run a kernel-mode driver that registers a private provider and subscribes to the kernel’s syscall/registry/network events. The event is kernel-generated → a user-mode payload can’t not fire it (short of stopping the whole session).
  • The Microsoft-Windows-Kernel-Process / -File / -Registry / -Network — the stock providers the EDR also subscribes to; these are the ones that produce the process create, file create, registry set, network connect events that feed a Sigma rule.
  • The TCB/CS layer — the EDR’s driver sits in the kernel’s callback stack (an Ob callback, a PsSetCreateProcessNotifyRoutine), which is ahead of ETW in some cases — the EDR sees the event before ETW buffers it. ETW is the log; the callback is the tripwire.

The standard evasions (what you’ll meet)

Kill the session (the blunt one)

Find the EDR’s ETW session, close it. The EDR’s session is a kernel object (\Sessions\... / an ETW handle); a payload that NtCloses the session handle (or NtEnableTraceEx to disable the provider) stops the events. The cost: the EDR knows it has a session; a missing session is a loud tell (the EDR checks its own handle is alive). The blunt kill works against a casual EDR; a modern one re-subscribes and alerts on the session-close.

The ETW disable via NtEnableTraceEx (the targeted one)

Instead of closing the whole session, disable the specific provider the EDR cares about (the syscall provider). NtEnableTraceEx with ENABLE/DISABLE toggles a provider without killing the session. The cost: same as above — the EDR checks its provider is still enabled; a disabled provider is a tell. It’s quieter than the session-kill (the session stays alive, other providers still fire) but it’s the same class of tell.

The TCB/hook-removal (the driver-level one)

Un-hook the EDR’s kernel callback (the Ob/Ps notify routine) — a payload (running in a driver, or via a VBS-integrity token) removes the EDR’s callback from the kernel’s callback stack. The EDR then misses the callback events (but still gets the ETW-buffered events, unless you also kill the session). The cost: a VBS/ kernel-integrity check (the EDR verifies its callback is still registered; a removed callback = a tell). This is the deep evasion — it requires kernel mode (a driver, or a VBS escape), not user mode.

The ETW real-time vs. log split (the subtle one)

Some EDRs use ETW for two things: a real-time tripwire (the callback / a synchronous ETW read) and a log (the buffered ETW events, read after the fact). A payload that kills the log (the buffer) but leaves the real-time tripwire alive is caught in real-time but not in the post-hoc log — the EDR alerts now but can’t reconstruct the timeline later. The evasion has to handle both (the tripwire and the log) to be complete.

The EDR counter (why the evasion is a race)

  • The session-handle check — the EDR verifies its own ETW session handle is alive (a periodic NtDuplicateHandle/NtQueryObject); a closed/disabled session is a self-detected tell (the EDR knows it’s been tampered with). The evasion has to fake the handle (a dummy session the EDR thinks is its own) or re-enable before the check.
  • The VBS/kernel-integrity check — a Virtualization-Based Security EDR runs its driver in a protected (VBS) kernel; a user-mode payload can’t touch the VBS driver’s callback/session without a VBS escape (a hypervisor-level exploit). VBS is the modern answer to the driver-level evasion — it moves the EDR’s state into a protected kernel a user-mode payload can’t reach.
  • The “events stopped” anomaly — a process that was generating events and suddenly stops (the session was killed mid-operation) is a behavioral tell (a process doesn’t just stop making syscalls; something stopped them). The EDR correlates the “event gap” with the session-close.

Red-team notes (OPSEC)

  • ETW is the layer a direct syscall doesn’t beat — if your payload uses windows-syscalls to skip the user-mode hook, ETW still sees the syscall (it’s kernel-generated). Evasion needs a kernel-mode move (the session-kill, the callback-removal) — a user-mode-only payload can’t hide from ETW (it can only delay it, via the session-kill, before the EDR re-subscribes).
  • The VBS line is where it gets hard — against a VBS EDR, the driver-level evasion (the callback-removal) needs a VBS escape (a hypervisor exploit); without one, you’re stuck with the user-mode session-kill (which the VBS EDR expects and checks for). Decide before the op whether the target is VBS-on (the evasion changes completely).
  • The real-time vs. log split is your window — if you only need to survive the real-time tripwire (not the post-hoc log), the session-kill buys you a window (the EDR alerts, but the timeline is gone). For a one-shot op (a credential dump, a lateral), the real-time window is often enough — you don’t need to hide from the log, only from the alert.
  • The behavior is the tell, not the event — ETW evasion hides the event stream; it doesn’t hide the process (a rundll32 dumping LSASS is still a process the EDR sees via the other layers). The evasion is a layer, not a shield — it has to be combined with the process/module/behavior evasion (defense-evasion-ad).

Detection

  • The session-handle / provider-state check — the EDR self-checks its session is alive and its providers are enabled; a missing session or a disabled provider is a self-detected tell (the EDR knows it’s been tampered with).
  • The “event gap” anomaly — a process that stops generating events mid-operation (the session was killed) is a behavioral tell; the EDR correlates the gap with the session-close event.
  • The VBS integrity check — a VBS EDR verifies its protected kernel state (the callback, the session) is intact; a modified VBS-driver state is a kernel-integrity tell.
  • The correlation — the specific combination (a session-close + an event gap + a subsequent LSASS access) is the high-fidelity alert (a lone session-close is noisy; the combination is the signal).
  • defense-evasion-ad — the EDR/AMSI/ETW evasion hub this belongs to
  • amsi — the scan layer (AMSI is the script-scan side; ETW is the event-log side)
  • windows-syscalls — the user-mode API the direct syscall skips (which ETW doesn’t skip)
  • beaconing — the C2 an ETW-evasive payload talks to (the network event ETW still catches)
  • reverse-engineering-workflow — where an ETW-evasion payload is reversed (the session-kill/callback-removal is statically findable)