ETW-TI: Microsoft-Windows-Threat-Intelligence Provider Deep Dive

The Microsoft-Windows-Threat-Intelligence (ETW-TI) provider is the single most significant ETW provider from a defensive security perspective. Unlike user-mode ETW providers, ETW-TI is a kernel-mode provider registered directly by ntoskrnl.exe. Events from this provider cannot be silenced by patching user-mode ntdll functions, making it the definitive backstop against advanced in-memory attack techniques such as process injection, thread hijacking, and credential dumping.

Related posts in this blog: Understanding and Attacking EDRs Hunting the Watchers: Detecting EDR Hooks Breaking ETW and EDR EDR Bypass Roadmap Hell’s Gate, Heaven’s Gate & Tartarus Gate

What is ETW-TI?

Property Value
Provider Name Microsoft-Windows-Threat-Intelligence
GUID {F4E1897C-BB5D-5668-F1D8-040F4D8DD344}
Mode Kernel-mode (registered by ntoskrnl.exe)
Introduced Windows 10 RS2/1703
Access Requirement PPL (Protected Process Light) Anti-Malware or higher

ETW-TI was introduced in Windows 10 RS2/1703 to provide critical visibility into Memory Manager (Mm*) and Asynchronous Procedure Call (APC) operations. Since then, it has been continuously expanded to cover additional security-relevant syscalls. Microsoft’s goal was to provide security vendors with near real-time kernel telemetry about the most security-critical operations happening on a system.

Unlike regular ETW providers that any process can subscribe to, the ETW-TI provider is only accessible to Early Launch Anti-Malware (ELAM) signed drivers and processes running as SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHT / PS_PROTECTED_ANTIMALWARE_LIGHT. This is part of the so-called Secure ETW Channel.


Architecture: Why Kernel-Mode Matters

The fundamental architectural difference between ETW-TI and user-mode ETW providers is where the instrumentation executes:

User-Mode ETW:       Application -> ntdll!EtwEventWrite -> Event
                     (can be patched in user-mode)

ETW-TI (Kernel):     syscall instruction -> ntoskrnl.exe -> EtwTiLog* -> Event
                     (fires AFTER transition to ring 0, untouchable from user-mode)

or as a picture:

The ETW-TI callback executes within the kernel after the syscall instruction completes. By the time the telemetry fires, the user-mode code has already transitioned into kernel-mode. This means:

  1. No user-mode hook can intercept the event
  2. No user-mode patching (ntdll, AMSI, etc.) affects ETW-TI
  3. The kernel sees the operation regardless of how the syscall was invoked

PPL Protection Requirement

The provider is only accessible to sessions running as PPL (Protected Process Light) or PP (Protected Process). Standard user-mode processes – including non-PPL EDR agents – cannot subscribe. This is the architectural reason why a kernel-mode driver is essential for EDR products: user-mode-only EDR has a structural blind spot for injection detection.

The kernel will only deliver ETW-TI events to services/processes running as PS_PROTECTED_ANTIMALWARE_LIGHT that have been signed by the Early Launch EKU certificate, which was also used to sign the corresponding ELAM driver installed via kernel32!InstallElamCertificateInfo.

Why Direct/Indirect Syscalls Cannot Bypass ETW-TI

This is a critical insight for both red and blue teamers:

ETW-TI events fire independently of how the syscall was invoked – whether via the hooked ntdll stub, a direct syscall, or an indirect syscall. The kernel sees the operation and fires the TI event.

This is fundamentally different from user-mode hooking (inline hooks on ntdll), which can be trivially bypassed by calling syscall stubs directly. ETW-TI operates entirely in kernel-mode, making it immune to all user-land evasion techniques. To understand and enumerate which functions your EDR actively hooks at the user-mode level, see the companion article Hunting the Watchers: Detecting and Enumerating EDR User-Mode Hooks – it compares loaded DLL memory against disk baselines to produce a complete hook map.

User-Mode Hook:     ntdll!NtWriteVirtualMemory (hooked) -> EDR callback -> syscall
Direct Syscall:     mov r10, rcx; mov eax, SSN; syscall  (bypasses hook)
Indirect Syscall:   jmp ntdll!NtWriteVirtualMemory+0x12  (bypasses hook)

ETW-TI:             ALL THREE -> kernel -> EtwTiLogReadWriteVm -> Event logged

Provider Internals: Registration, Event Flow, and Kernel Structures

This section dives into the low-level mechanisms that make ETW-TI work inside ntoskrnl.exe. Understanding these internals is essential for reverse engineering detection capabilities, building custom consumers, and understanding what attackers must subvert to blind this provider.

Provider Registration in ntoskrnl.exe

Like any ETW provider, the TI provider must call EtwRegister before it can emit events. Because ETW-TI is a kernel-mode provider, this registration happens inside ntoskrnl.exe during system initialization.

The registration call is found within the EtwInitialize function, which registers many kernel ETW providers during boot:

NTSTATUS EtwRegister(
    [in]           LPCGUID            ProviderId,       // Provider GUID
    [in, optional] PETWENABLECALLBACK EnableCallback,   // Called when consumer enables/disables
    [in, optional] PVOID              CallbackContext,  // Context for callback
    [out]          PREGHANDLE         RegHandle         // Output: registration handle
);

For the TI provider, the parameters are:

Parameter Value
ProviderId ThreatIntProviderGuid = {f4e1897c-bb5d-5668-f1d8-040f4d8dd344}
RegHandle (output) Stored in the global EtwThreatIntProvRegHandle

In IDA/Ghidra, following the cross-references from EtwRegister inside ntoskrnl.exe leads to EtwInitialize, where you can observe the TI provider GUID being passed as the first argument.

The EtwThreatIntProvRegHandle Global

The registration produces a handle stored in the kernel global variable EtwThreatIntProvRegHandle. This handle is the linchpin of the entire ETW-TI system:

nt!EtwThreatIntProvRegHandle  ->  REGHANDLE (opaque kernel handle)

Every EtwTiLog* function references this handle when calling EtwWrite to emit events. By cross-referencing EtwThreatIntProvRegHandle in a disassembler, you can discover all functions that write to the TI provider:

0: kd> x nt!EtwThreatIntProvRegHandle
fffff805`27c4a1b0 nt!EtwThreatIntProvRegHandle = <...>

Cross-referencing this symbol reveals all callers – which is precisely the list of EtwTiLog* sensor functions. This is the reverse-engineering technique used by researchers (including Jonathan Johnson) to map out the complete set of instrumented operations.

Attack relevance: One known kernel-level bypass involves zeroing EtwThreatIntProvRegHandle via a vulnerable driver. If this handle is null, subsequent calls to EtwWrite with it will silently fail, effectively disabling all TI events. However, PatchGuard periodically validates kernel data integrity and may detect this modification.

Event Writing: From Syscall to EtwWrite

When a security-relevant syscall executes, the kernel calls the corresponding EtwTiLog* function, which ultimately invokes EtwWrite:

NTSTATUS EtwWrite(
    [in]           REGHANDLE              RegHandle,        // EtwThreatIntProvRegHandle
    [in]           PCEVENT_DESCRIPTOR     EventDescriptor,  // Event metadata (ID, level, keyword)
    [in, optional] LPCGUID                ActivityId,       // Correlation GUID
    [in]           ULONG                  UserDataCount,    // Number of data items
    [in, optional] PEVENT_DATA_DESCRIPTOR UserData          // Event payload
);

The EVENT_DESCRIPTOR structure defines the event’s identity:

typedef struct _EVENT_DESCRIPTOR {
    USHORT    Id;       // Event ID (unique per event type)
    UCHAR     Version;  // Schema version
    UCHAR     Channel;  // Log channel
    UCHAR     Level;    // Severity (Informational, Warning, etc.)
    UCHAR     Opcode;   // Operation type
    USHORT    Task;     // Task category
    ULONGLONG Keyword;  // Filtering bitmask
} EVENT_DESCRIPTOR, *PEVENT_DESCRIPTOR;

Each EtwTiLog* function constructs the appropriate EVENT_DESCRIPTOR and populates UserData with contextual information (PIDs, addresses, sizes, protection flags, etc.) before calling EtwWrite.

Event Routing: Local vs Remote, Read vs Write

A detailed examination of EtwTiLogReadWriteVm reveals how the kernel decides which specific event to emit. The logic follows this decision tree:

The corresponding event descriptors (found as static data in ntoskrnl.exe):

Symbol Event ID (hex) Keyword Mask Trigger
THREATINT_READVM_LOCAL 0x0d (13) 0x8000000000004000 Same-process read
THREATINT_READVM_REMOTE 0x0e (14) 0x8000000000008000 Cross-process read
THREATINT_WRITEVM_LOCAL 0x0f (15) 0x8000000000010000 Same-process write
THREATINT_WRITEVM_REMOTE 0x10 (16) 0x8000000000020000 Cross-process write

The complete call chain for a WriteProcessMemory operation:

User-Mode:
  WriteProcessMemory() [kernel32.dll]
    └─ NtWriteVirtualMemory() [ntdll.dll]
        └─ syscall instruction -> Ring 0 transition

Kernel-Mode (ntoskrnl.exe):
  NtWriteVirtualMemory(ProcessHandle, BaseAddress, Buffer, Size, ...)
    └─ MiReadWriteVirtualMemory(SourceProcess, TargetProcess, ..., 0x20)
        ├─ [Performs the actual memory write operation]
        └─ EtwTiLogReadWriteVm(SourceProcess, TargetProcess, 0x20, ...)
            ├─ Determines: REMOTE (different process) + WRITE (0x20)
            ├─ Selects: THREATINT_WRITEVM_REMOTE descriptor
            └─ EtwWrite(EtwThreatIntProvRegHandle, &THREATINT_WRITEVM_REMOTE, ...)
                └─ Event delivered to PPL consumer sessions

This architecture means the event fires after the write completes but within the same syscall context – there is no async delay at the kernel instrumentation level.

The Secure ETW Channel and PPL Enforcement

ETW-TI uses what Microsoft calls the Secure ETW Channel. This is not a separate transport mechanism but rather an access control enforcement layer:

  1. Provider Registration: When EtwRegister is called for the TI provider GUID, the kernel marks this registration as “secure” in the internal ETW_REG_ENTRY structure.

  2. Session Subscription: When a consumer calls StartTrace / EnableTraceEx2 to subscribe to the TI provider, the kernel checks the caller’s protection level:
    if (Process->Protection.Type < PsProtectedTypeProtectedLight ||
        Process->Protection.Signer < PsProtectedSignerAntimalware)
    {
        return STATUS_ACCESS_DENIED;
    }
    
  3. ELAM Requirement: The PPL-AM protection level requires:
    • A kernel-mode ELAM driver signed with Microsoft’s Early Launch EKU
    • The user-mode service binary signed with the same certificate chain
    • Installation via InstallElamCertificateInfo during ELAM driver load
  4. Event Delivery: Events are only routed to trace sessions whose owning process meets the PPL-AM threshold. Non-qualifying sessions never receive TI events, even if they request the provider GUID.

The verification check within NtTraceControl (which backs EnableTraceEx2):

// Simplified kernel logic for ETW-TI subscription validation
if (IsSecureProvider(ProviderGuid)) {
    PEPROCESS Consumer = PsGetCurrentProcess();
    PS_PROTECTION protection = Consumer->Protection;

    if (protection.Type < PsProtectedTypeProtectedLight)
        return STATUS_ACCESS_DENIED;
    if (protection.Signer < PsProtectedSignerAntimalware)
        return STATUS_ACCESS_DENIED;
}

EPROCESS Protection Levels

The EPROCESS kernel structure contains a PS_PROTECTION field that determines the process’s protection level:

typedef struct _PS_PROTECTION {
    union {
        UCHAR Level;
        struct {
            UCHAR Type   : 3;   // PS_PROTECTED_TYPE
            UCHAR Audit  : 1;   // Reserved
            UCHAR Signer : 4;   // PS_PROTECTED_SIGNER
        };
    };
} PS_PROTECTION, *PPS_PROTECTION;

Protection Types (relevant hierarchy):

Value Type Description
0 PsProtectedTypeNone No protection (normal process)
1 PsProtectedTypeProtectedLight PPL – Protected Process Light
2 PsProtectedTypeProtected PP – Full Protected Process

Signer Levels (relevant hierarchy, highest to lowest):

Value Signer Who Uses It
6 PsProtectedSignerWinTcb Core Windows components (csrss, smss)
5 PsProtectedSignerWindows Windows services
4 PsProtectedSignerLsa LSA (credential protection)
3 PsProtectedSignerAntimalware EDR/AV (minimum for ETW-TI)
2 PsProtectedSignerCodeGen Code generation services
1 PsProtectedSignerAuthenticode Standard Authenticode
0 PsProtectedSignerNone No signer

For ETW-TI access, a process needs at minimum: Type = PsProtectedTypeProtectedLight (1) AND Signer = PsProtectedSignerAntimalware (3), giving a combined Level byte of 0x31.

This is what tools like KDU exploit: they use a vulnerable driver to directly modify the EPROCESS->Protection field in kernel memory, elevating a normal process to PPL-AM status.

Per-Process ETW-TI Enablement Flags

ETW-TI events are not globally enabled for all processes – they are controlled on a per-process basis via flags in the EPROCESS structure. These flags determine whether TI events fire when the process is involved in a monitored operation:

// Relevant EPROCESS fields (undocumented, offsets vary by build)
struct _EPROCESS {
    // ...
    union {
        ULONG MitigationFlags2;
        struct {
            ULONG EnableReadVmLogging : 1;
            ULONG EnableWriteVmLogging : 1;
            ULONG EnableProcessSuspendResumeLogging : 1;
            ULONG EnableThreadSuspendResumeLogging : 1;
            ULONG EnableLocalExecProtectVmLogging : 1;   // Win11+
            ULONG EnableRemoteExecProtectVmLogging : 1;  // Win11+
            // ...
        };
    };
    // ...
};

These flags can be queried and set via NtSetInformationProcess with:

  • ProcessEnableReadWriteVmLogging (class 0x57)
  • ProcessEnableLogging (class 0x60)

Under normal circumstances, modifying these flags requires PPL-AM privileges. The ETW-ByeBye bug (discussed later) exploits the absence of this PPL check on vulnerable Windows versions.


The Kernel Sensors: EtwTiLog Functions

The Windows kernel sensors are categorized into two groups:

  • Threat Intelligence (Ti): EtwTiLog* prefix – high-fidelity security sensors
  • Security Mitigations (Tim): EtwTimLog* prefix – mitigation-related logging

These are the kernel-internal functions that hook directly into critical operations:

Sensor Function Trigger What It Detects
EtwTiLogReadWriteVm MiReadWriteVirtualMemory Cross-process memory read/write (LSASS dumping, injection)
EtwTiLogSetContextThread PspSetContextThreadInternal Thread hijacking / context manipulation
EtwTiLogAllocExecVm RWX memory allocations Executable memory allocation (shellcode staging)
EtwTiLogProtectExecVm Memory protection changes VirtualProtect to PAGE_EXECUTE_*
EtwTiLogMapExecView Section mapping with execute Executable section mappings (module stomping)
EtwTiLogInsertQueueUserApc APC queueing APC-based injection techniques
EtwTiLogSuspendResumeProcess Process suspend/resume Process hollowing preparation
EtwTiLogSuspendResumeThread Thread suspend/resume Thread manipulation
EtwTiLogDriverObjectLoad Driver loading Malicious/vulnerable driver loading
EtwTiLogDriverObjectUnLoad Driver unloading Driver cleanup after exploitation
EtwTiLogDeviceObjectLoadUnload Device object operations Device driver manipulation

Complete Sensor List via WinDbg

In a kernel debugging session, list all ETW-TI symbols:

0: kd> x nt!EtwTiLog*
fffff805`27b75280 nt!EtwTiLogDriverObjectLoad (void)
fffff805`27a5f94c nt!EtwTiLogDeviceObjectLoadUnload (void)
fffff805`27759390 nt!EtwTiLogInsertQueueUserApc (void)
fffff805`27a79ecc nt!EtwTiLogProtectExecVm (void)
fffff805`27b6d22c nt!EtwTiLogDriverObjectUnLoad (void)
fffff805`27aaac4c nt!EtwTiLogSetContextThread (void)
fffff805`27d3f76c nt!EtwTiLogSuspendResumeProcess (void)
fffff805`27a79ce0 nt!EtwTiLogAllocExecVm (void)
fffff805`27a79b30 nt!EtwTiLogReadWriteVm (void)
fffff805`27b22544 nt!EtwTiLogMapExecView (void)
fffff805`27d3f8d4 nt!EtwTiLogSuspendResumeThread (void)

ETW-TI Monitored Events

Each event captures rich contextual data:

Event ID Syscall Detection Use Case
1 NtAllocateVirtualMemory Memory allocation with PAGE_EXECUTE_*
2 NtProtectVirtualMemory Changing memory to executable
3 NtMapViewOfSection Section mapping (especially SEC_IMAGE)
4 NtQueueApcThread APC-based injection
5 NtSetContextThread Thread context modification (hijacking)
6 NtCreateThreadEx Remote thread creation
7 NtOpenProcess Process handle with high privileges
8 NtReadVirtualMemory Cross-process reads (credential dumping)
9 NtWriteVirtualMemory Cross-process writes (code injection)
10 Image loads DLL injection via LoadLibrary

For example, when NtAllocateVirtualMemory is called with PAGE_EXECUTE_READWRITE, ETW-TI logs:

  • Process ID making the call
  • Target process (if cross-process)
  • Base address allocated
  • Region size
  • Protection flags (RWX is highly suspicious)
  • Call stack (user-mode portion)
  • Timestamp

Querying ETW-TI Locally

Query the full list of supported events from an elevated command prompt:

logman.exe query providers Microsoft-Windows-Threat-Intelligence

Example output:

Provider                                 GUID
-------------------------------------------------------------------------------
Microsoft-Windows-Threat-Intelligence    {F4E1897C-BB5D-5668-F1D8-040F4D8DD344}

Value               Keyword              Description
-------------------------------------------------------------------------------
0x0000000000000001  KERNEL_THREATINT_KEYWORD_ALLOCVM_LOCAL
0x0000000000000002  KERNEL_THREATINT_KEYWORD_ALLOCVM_LOCAL_KERNEL_CALLER
0x0000000000000004  KERNEL_THREATINT_KEYWORD_ALLOCVM_REMOTE
0x0000000000000008  KERNEL_THREATINT_KEYWORD_PROTECTVM_LOCAL
0x0000000000000010  KERNEL_THREATINT_KEYWORD_PROTECTVM_LOCAL_KERNEL_CALLER
0x0000000000000020  KERNEL_THREATINT_KEYWORD_PROTECTVM_REMOTE
0x0000000000000040  KERNEL_THREATINT_KEYWORD_MAPVIEW_LOCAL
0x0000000000000080  KERNEL_THREATINT_KEYWORD_MAPVIEW_LOCAL_KERNEL_CALLER
0x0000000000000100  KERNEL_THREATINT_KEYWORD_MAPVIEW_REMOTE
0x0000000000000200  KERNEL_THREATINT_KEYWORD_QUEUEUSERAPC_REMOTE
0x0000000000000400  KERNEL_THREATINT_KEYWORD_SETTHREADCONTEXT_REMOTE
0x0000000000000800  KERNEL_THREATINT_KEYWORD_READVM_LOCAL
0x0000000000001000  KERNEL_THREATINT_KEYWORD_READVM_REMOTE
0x0000000000002000  KERNEL_THREATINT_KEYWORD_WRITEVM_LOCAL
0x0000000000004000  KERNEL_THREATINT_KEYWORD_WRITEVM_REMOTE
0x0000000000008000  KERNEL_THREATINT_KEYWORD_SUSPEND_THREAD
0x0000000000010000  KERNEL_THREATINT_KEYWORD_RESUME_THREAD
0x0000000000020000  KERNEL_THREATINT_KEYWORD_SUSPEND_PROCESS
0x0000000000040000  KERNEL_THREATINT_KEYWORD_RESUME_PROCESS
...

How EDRs Consume ETW-TI

While synchronous blocking (inline callbacks for these syscalls) is not yet possible, ETW-TI represents the most reliable kernel-side detection mechanism accessible to security vendors. EDRs consume ETW-TI events through their kernel-mode driver component, which runs as PPL-AntiMalware.

Elastic Security: Call Stack-Based Detections

Elastic Security (since version 8.8+) combines ETW-TI events with kernel call stack analysis. Their implementation:

  1. Subscribes to ETW-TI via their PPL kernel driver
  2. Fingerprints each event and deduplicates (unique events only emitted)
  3. Processes events on-host for behavior detection rules
  4. Optionally streams to SIEM for SOC correlation

Currently monitored APIs include:

  • VirtualAlloc / VirtualAllocEx
  • VirtualProtect / VirtualProtectEx
  • MapViewOfFile / MapViewOfFile2
  • QueueUserAPC
  • SetThreadContext
  • WriteProcessMemory
  • ReadProcessMemory (targeting lsass)

API Event Behaviors (Elastic Enrichment)

Elastic enriches API events with behavioral indicators:

Behavior Description
cross-process Activity between two different processes
native_api Direct call to undocumented Native API
direct_syscall Syscall instruction originated outside ntdll
proxy_call Proxied API call masking the true caller
shellcode Suspicious non-image executable memory calling sensitive API
image-hooked Call stack entry appears hooked
image_rop Call stack entry not preceded by a call instruction
unbacked_rwx Non-image writable+executable memory in call stack
allocate_shellcode Non-image executable memory allocating more executable memory
execute_fluctuation PAGE_EXECUTE protection unexpectedly changing
write_fluctuation PAGE_WRITE of executable memory fluctuating
hollow_image Large executable image region protection changed
hardware_breakpoint_set Hardware breakpoint potentially set

Detection Rule Examples

Direct Syscall Detection:

api where event.category == "intrusion_detection" and
    process.Ext.api.behaviors == "direct_syscall" and
    process.Ext.api.name : ("VirtualAlloc*", "VirtualProtect*",
                             "MapViewOfFile*", "WriteProcessMemory")

ETW Patching Detection:

api where process.Ext.api.name : "WriteProcessMemory*" and
    process.Ext.api.summary : ("*ntdll.dll!Etw*", "*ntdll.dll!NtTrace*") and
    not process.executable : ("?:\\Windows\\System32\\lsass.exe")

AMSI/WLDP Bypass Detection:

api where
 (
  (process.Ext.api.name : "VirtualProtect*" and
    process.Ext.api.parameters.protection : "*W*") or
  process.Ext.api.name : "WriteProcessMemory*"
 ) and
 process.Ext.api.summary : ("* amsi.dll*", "* mpoav.dll*", "* wldp.dll*")

Remote Module Hooking (ThreadlessInject detection):

api where process.Ext.api.name : "WriteProcessMemory" and
    process.Ext.api.behaviors == "cross-process" and
    process.Ext.api.summary : ("*ntdll.dll*", "*kernelbase.dll*")

Purple Team: The Bypass Landscape

Since ETW-TI is a kernel-mode provider, any suppression requires kernel-level access – via BYOVD (Bring Your Own Vulnerable Driver), a kernel exploit, or a signed kernel driver. This makes ETW-TI the defensive backstop against injection techniques: all userland patching leaves it untouched.

User-Mode Bypasses That DO NOT Work

Technique Why It Fails Against ETW-TI
Patching ntdll!EtwEventWrite Only affects user-mode ETW providers
Direct Syscalls Kernel still sees the operation
Indirect Syscalls Kernel still sees the operation
Unhooking ntdll ETW-TI has no user-mode hooks to remove
AMSI patching Completely different subsystem
Process hollowing from fresh ntdll Kernel instrumentation is independent

Kernel-Mode Bypass Vectors

These are relevant for detection engineering (knowing what to watch for):

Vector Method Risk
Nulling EtwThreatIntProvRegHandle Zero the kernel registration handle PatchGuard detection -> BSOD
Patching nt!EtwTiLogAllocExecVm ret at function entry PatchGuard / HVCI violation
BYOVD Load vulnerable signed driver for kernel R/W Driver blocklist, detection of driver load
Kernel exploit 0-day or N-day for ring-0 code execution Highly advanced, rare
NtSetInformationProcess bug Disable per-process TI logging (patched Win11) Only works on unpatched Win10

The NtContinue Technique (Hardware Breakpoints)

Research from Praetorian demonstrates that NtContinue can be used to set hardware breakpoints (debug registers) without triggering EtwTiLogSetContextThread. This is because NtContinue updates the thread context including debug registers without invoking the same kernel path as NtSetContextThread.

The attack flow:

  1. Register a Vectored Exception Handler
  2. Capture current thread context via RtlCaptureContext
  3. Set Dr0-Dr3 and Dr7 to hook target functions (e.g., Sleep, AmsiScanBuffer)
  4. Call NtContinue to apply the context (no ETW-TI event)
  5. When the breakpoint fires, the VEH handles the exception

This is relevant because EDRs create detections around SetThreadContext/NtSetContextThread for hardware breakpoint abuse. Using NtContinue avoids this specific detection vector.

Detection approach: Monitor for NtContinue calls with CONTEXT_DEBUG_REGISTERS flag, or detect the behavioral outcome (patchless hooks on AMSI/ETW functions).

ETW-ByeBye: The ProcessEnableLogging Bug

Research from RiskInsight (2023) revealed that on certain Windows 10 versions, calling NtSetInformationProcess with ProcessEnableLogging or ProcessEnableReadWriteVmLogging could disable ETW-TI event logging for a process without the expected PPL requirement. Only SeDebug or SeTcb privileges were needed.

Vulnerable versions:

  • Win10 1709 through 22H2 (ReadVm/WriteVm operations)
  • Win10 1809 through 22H2 (Process/Thread suspend/resume)
  • Patched in Windows 11
// Simplified POC (works only on unpatched systems)
PROCESS_LOGGING_INFORMATION info = { 0 };
info.Flags = 0xf;
info.EnableReadVmLogging = 1;
info.EnableWriteVmLogging = 1;
info.EnableProcessSuspendResumeLogging = 1;
info.EnableThreadSuspendResumeLogging = 1;

NtSetInformationProcess((HANDLE)-1, ProcessEnableLogging,
                        &info, sizeof(info));
// ETW-TI events for this process are now silenced

Detection approaches:

  1. Hook NtSetInformationProcess in user-mode (fragile but layered defense)
  2. Walk KPROCESS list and check if ETW-TI was disabled for unexpected processes
  3. Alert on absence of expected ETW-TI telemetry from a process
  4. Upgrade to Windows 11 where the bug is fixed

Detection Engineering Recommendations

For CSIRT/SOC teams building detection capabilities around ETW-TI:

High-Fidelity Detection Patterns:

Pattern Detects MITRE ATT&CK
Cross-process NtWriteVirtualMemory + NtCreateThreadEx Classic process injection T1055.001
NtAllocateVirtualMemory(RWX) -> NtWriteVirtualMemory -> NtProtectVirtualMemory(RX) Staged injection T1055
NtReadVirtualMemory targeting lsass.exe Credential dumping T1003.001
NtSetContextThread on remote process Thread hijacking T1055.003
NtQueueApcThread to remote process APC injection T1055.004
NtMapViewOfSection with execute to remote process Section-based injection T1055.012
RWX allocation + immediate execution from non-image memory Shellcode execution T1620
Driver load from unusual path BYOVD attempt T1068

Behavioral Evasion Awareness:

Sophisticated adversaries may attempt to reduce detection scores by:

  • Staging operations with delays (spreading RWX alloc -> write -> protect -> execute over minutes)
  • Using existing executable memory (code caves) instead of new allocations
  • Using RW allocation first, then changing to RX (no RWX flag)
  • Leveraging legitimate processes for early injection stages

Correlation with User-Mode Hook Coverage:

ETW-TI provides kernel-level visibility, but most EDRs also maintain user-mode inline hooks on ntdll.dll for real-time interception. The two layers are complementary: ETW-TI catches operations that bypass user-mode hooks (via direct/indirect syscalls), while hooks provide synchronous blocking capability that ETW-TI lacks. Use the EDR Hook Detection Scanner to map which Nt* functions your EDR intercepts at user-mode level, then cross-reference with the ETW-TI monitored events table above to identify any operations that lack coverage at both layers.

Splunk SPL Example (if forwarding ETW-TI via Fibratus/Sealighter):

index=etw_ti sourcetype="etw:ti"
| where CallingProcessId != TargetProcessId
| where Protection="PAGE_EXECUTE_READWRITE"
| stats count by CallingProcessName, TargetProcessName, EventType
| where count > 3
| sort - count

Tools and Research Resources

Research / Consume ETW-TI

Tool Description Link
SealighterTI Combines Sealighter with exploits to subscribe to ETW-TI without a signed driver (patched on current systems) GitHub
Sealighter ETW research tool – subscribes to multiple providers, filters events, outputs JSON; forwards to Splunk/ELK GitHub
EtwTiViewer Live ETW-TI event viewer with kernel driver + ImGui frontend; displays the same signals commercial EDRs consume GitHub
TiEtwAgent ETW-based process injection detection agent GitHub
Fibratus Open-source Windows runtime security sensor for real-time kernel telemetry and threat detection via ETW fibratus.io
KDU Kernel Driver Utility – uses vulnerable drivers to gain kernel access; can launch processes as PPL-AM GitHub

Deep-Dive Blog Articles

Article Author Focus
Uncovering Windows Events: Threat Intelligence ETW Jonathan Johnson Reverse engineering EtwRegister/EtwInitialize in ntoskrnl.exe
Event Tracing for Windows (ETW) Nation State Minds Comprehensive ETW deep dive including suppression techniques
ETW Threat Intelligence and Hardware Breakpoints Praetorian NtContinue bypass, kernel debugging of EtwTiLog functions
Introduction into Microsoft Threat Intelligence Drivers Meekolab Historical context, ELAM/PPL architecture
Doubling Down: Detecting In-Memory Threats with Kernel ETW Call Stacks Elastic Security Labs Production detection rules, API event enrichment
Protecting Windows Protected Processes Elastic PPL architecture and EDR integration
Gaining Threat-Intelligence the dodgy way pat_h/to/file SealighterTI development, PPL exploit chain
Gaining Threat-Intelligence the REALLY dodgy way pat_h/to/file BYOVD approach using KDU for ETW-TI access
Getting started with ETW-TI Provider HackBalak Practical walkthrough: SealighterTI + ELK integration
ETW-ByeBye: Disabling ETW-TI Without PPL Legacyy NtSetInformationProcess bug exploitation and detection
The ETW Blind Spot Valhguard Red team tactics for blinding Windows Event Tracing
ETW-TI Limitations (SysWhispers4) SysWhispers4 Docs User-mode vs kernel-mode evasion boundaries
RedTeaming CheatSheet: EDR/ETW 0xJs Quick reference for EDR evasion techniques

References

  1. Microsoft Documentation - Event Tracing for Windows (ETW)
  2. Elastic Security Labs - Doubling Down: Detecting In-Memory Threats with Kernel ETW Call Stacks (January 2024)
  3. Praetorian - ETW Threat Intelligence and Hardware Breakpoints (January 2025)
  4. Meekolab - Introduction into Microsoft Threat Intelligence Drivers (September 2022)
  5. pathtofile - SealighterTI and KDU approach
  6. Legacyy - ETW-ByeBye: Disabling ETW-TI Without PPL (April 2024)
  7. RiskInsight Wavestone - A universal EDR bypass built in Windows 10 (October 2023)
  8. SysWhispers4 - ETW-TI Limitations
  9. jdu2600 - Windows 10 ETW Events manifest
Written on June 19, 2026


◀ Back to Defense related posts