Chapter 28

ETW — Event Tracing for Windows

The architecture of ETW, key security-relevant providers, ETW patching as an evasion technique, and the Threat Intelligence provider that operates below user-mode evasion

Scenario

A sample patches ntdll!EtwEventWrite with a two-byte xor eax, eax; ret stub. From that point, every ETW event that would have been written from this process silently returns success without writing anything. The EDR's Microsoft-Defender-Windows and .NET runtime events go dark. But the ETW Threat Intelligence provider (ETW-TI) operates in the kernel and isn't affected — it still sees the VirtualAlloc and WriteProcessMemory calls. The defender who knows about ETW-TI wins; the one relying only on user-mode ETW is blind.

ETW Architecture

ETW is the Windows tracing infrastructure used by the OS, security tools, and applications for high-performance event logging. Three roles:

  ┌──────────────────────────────────────────────────────────┐
  │   Provider (ntdll, kernel, driver)                        │
  │   EtwEventWrite() / EtwWriteEx()                         │
  └───────────────┬──────────────────────────────────────────┘
                  │ if session has subscribed to this provider
  ┌───────────────▼──────────────────────────────────────────┐
  │   ETW Session (kernel buffer)                            │
  │   Real-time or file-backed (.etl)                        │
  │   Named: NT Kernel Logger, Circular Kernel Context Log.. │
  └───────────────┬──────────────────────────────────────────┘
                  │
  ┌───────────────▼──────────────────────────────────────────┐
  │   Consumer (EDR, logman, perfview, WinDefender)          │
  │   OpenTrace() + ProcessTrace() API                       │
  └──────────────────────────────────────────────────────────┘
  

Key Security-Relevant ETW Providers

Provider NameGUID (short)Key eventsLayer
Microsoft-Windows-Kernel-Process 22fb2cd6-... Process/thread create, image load Kernel
Microsoft-Windows-Kernel-File edd08927-... File create/read/write/rename/delete Kernel
Microsoft-Windows-Kernel-Network 7dd42a49-... TCP/UDP connect/accept/send/recv Kernel
Microsoft-Windows-Threat-Intelligence f4e1897c-... VirtualAlloc, WriteProcessMemory, SetThreadContext, injection-class operations; full stacks Kernel (PPL)
Microsoft-Windows-DotNETRuntime e13c0d23-... .NET JIT, GC, exceptions, assemblies loaded User (CLR)
Microsoft-Windows-PowerShell a0c1853b-... Script block logging, command invocation User (PS)
Microsoft-Antimalware-Engine 0a002690-... Defender scan results, detection events User (defender)

Consuming ETW in Python

"""
Subscribe to the kernel process provider and print process-create events.
Requires admin rights; uses pyetw or pywintrace wrappers.
"""
from pywintrace import ETWConsumer, TraceProperties

KERNEL_PROCESS_GUID = "{22fb2cd6-0e7b-422b-a0c7-2fad1fd0e716}"

def on_event(event):
    if event.task_name == "ProcessStart":
        pid       = event.properties.get("ProcessID")
        image     = event.properties.get("ImageName")
        ppid      = event.properties.get("ParentProcessID")
        print(f"[+] PID={pid} PPID={ppid} Image={image}")

consumer = ETWConsumer()
consumer.add_provider(KERNEL_PROCESS_GUID, any_keywords=0x10)
consumer.start(on_event)

ETW Patching

User-mode ETW events pass through ntdll!EtwEventWrite. If malware overwrites this function with a no-op, all user-mode ETW events from that process are silently discarded:

// ETW patching: replace EtwEventWrite prologue with xor eax,eax; ret
void PatchETW()
{
    PVOID pEtw = GetProcAddress(GetModuleHandleA("ntdll.dll"), "EtwEventWrite");
    if (!pEtw) return;

    // Patch: 33 C0 C3 = xor eax,eax; ret
    BYTE patch[] = { 0x33, 0xC0, 0xC3 };
    DWORD oldProt;
    VirtualProtect(pEtw, sizeof(patch), PAGE_EXECUTE_READWRITE, &oldProt);
    memcpy(pEtw, patch, sizeof(patch));
    VirtualProtect(pEtw, sizeof(patch), oldProt, &oldProt);
}
// After this: PowerShell AMSI events, .NET JIT events, user-mode
// security events all silently fail. Kernel-mode ETW unaffected.

ETW Threat Intelligence (ETW-TI)

ETW-TI is a special kernel-mode ETW provider (GUID: f4e1897c-bb5d-5668-f1d8-040f4d8dd344) that fires on high-value offensive operations. Unlike ordinary ETW providers, it:

Why ETW-TI matters for detection engineering

ETW-TI is the sensor that modern EDRs (including Windows Defender) use to detect injection-class operations even when the process has patched ntdll and suppressed user-mode ETW. When you see EDR alerts for "suspicious virtual memory operations" on a process with no visible imports for VirtualAlloc, the telemetry likely came from ETW-TI. Detection engineers building custom sensors must obtain the ETW-TI GUID and feed its events into their SIEM — but subscribing requires the consumer to run at PPL level, which means a signed, protected driver component.

Detecting ETW Patching

# Python: check if EtwEventWrite in a process is patched
import ctypes, ctypes.wintypes

PROCESS_VM_READ = 0x0010
KNOWN_CLEAN_PROLOGUE = bytes([0x4C, 0x8B, 0xDC, 0x53, 0x56])  # Win10 EtwEventWrite

def check_etw_patch(pid: int) -> bool:
    hProc = ctypes.windll.kernel32.OpenProcess(PROCESS_VM_READ, 0, pid)
    if not hProc:
        return False

    # Get EtwEventWrite address (same in all processes via ntdll ASLR)
    ntdll_base = ctypes.windll.kernel32.GetModuleHandleW("ntdll.dll")
    etw_offset  = ctypes.windll.kernel32.GetProcAddress(ntdll_base, b"EtwEventWrite")
    if not etw_offset:
        ctypes.windll.kernel32.CloseHandle(hProc)
        return False

    buf  = (ctypes.c_ubyte * 5)()
    read = ctypes.c_size_t()
    ctypes.windll.kernel32.ReadProcessMemory(
        hProc, ctypes.c_void_p(etw_offset),
        buf, 5, ctypes.byref(read)
    )
    ctypes.windll.kernel32.CloseHandle(hProc)

    actual = bytes(buf[:5])
    if actual != KNOWN_CLEAN_PROLOGUE:
        print(f"[!] PID {pid}: EtwEventWrite patched: {actual.hex()}")
        return True
    return False

Q & A

What's the difference between ETW and Windows Event Log — don't they both log security events?

ETW and Windows Event Log are related but distinct. ETW is the low-level tracing infrastructure: a high-performance, binary kernel buffer that can process millions of events per second. It's designed for performance tracing (perfview, xperf) and security monitoring. Events are binary-encoded and require decoding with a manifest. Windows Event Log (the EVTX format you view in eventvwr.msc) is built on top of ETW for some events, but is a higher-level system with structured XML formatting, access control, log rotation, and the Security/System/Application channel model. Some ETW events are forwarded to the Windows Event Log (e.g., Security audit events like 4688 Process Creation appear in the Security EVTX channel). Others remain ETW-only (e.g., ETW-TI events never appear in Event Log — they go directly to PPL consumers). For detection: (1) Windows Event Log (Sysmon, Security channel 4688, 4624) is what SIEM agents typically forward to your SIEM — it's accessible and structured. (2) ETW-only providers (Kernel-Process, Threat Intelligence) require dedicated ETW consumers (like a Sysmon driver with ETW forwarding or a custom EDR). The practical difference for detection engineering: Event Log is your always-on, structured, SIEM-ready source. ETW gives you higher fidelity and lower latency but requires custom consumers.

Can malware disable ETW globally for the entire system rather than just per-process patching?

Patching EtwEventWrite in a process affects only that process's user-mode ETW output — it's a per-process operation. For system-wide ETW suppression, an attacker would need kernel access and would target the kernel-mode ETW infrastructure directly: the EtwpReserveTraceBuffer function, or the NtTraceEvent syscall, or the trace session control block in kernel memory. This is firmly in kernel rootkit territory (Chapter 45). Without kernel access, the most an attacker can do is: (1) Patch EtwEventWrite in each process they inject into (per-process, affects that process's user-mode ETW). (2) Disable specific event log services (the Windows Event Log service, Sysmon driver) — but these actions are themselves highly detectable via service control events. (3) Corrupt or manipulate .etl session buffers in user space, but kernel-mode buffers are inaccessible from user mode. The key asymmetry: kernel-mode ETW providers (including ETW-TI and the NT Kernel Logger) cannot be disabled by user-mode attackers without a kernel exploit. This is why detection engineering should prioritize kernel-sourced telemetry over user-mode-only telemetry for high-fidelity detection of advanced attackers.