> Windows Syscalls
ntoskrnl.exeT1106T1562

NtTraceEvent

Writes a user-mode event to an ETW session via a registered trace handle.

Prototype

NTSTATUS NtTraceEvent(
  HANDLE TraceHandle,
  ULONG  Flags,
  ULONG  FieldSize,
  PVOID  Fields
);

Arguments

NameTypeDirDescription
TraceHandleHANDLEinTrace/registration handle obtained from EtwRegister (or kernel EtwRegister equivalent).
FlagsULONGinEvent flags. ETW_NT_FLAGS_TRACE_HEADER (0x00400000) marks Fields as a fully-formed EVENT_HEADER.
FieldSizeULONGinTotal size in bytes of the buffer pointed to by Fields.
FieldsPVOIDinPointer to an EVENT_HEADER followed by EVENT_DATA_DESCRIPTORs for the event payload.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070x5Ewin10-1507
Win10 16070x5Ewin10-1607
Win10 17030x5Ewin10-1703
Win10 17090x5Ewin10-1709
Win10 18030x5Ewin10-1803
Win10 18090x5Ewin10-1809
Win10 19030x5Ewin10-1903
Win10 19090x5Ewin10-1909
Win10 20040x5Ewin10-2004
Win10 20H20x5Ewin10-20h2
Win10 21H10x5Ewin10-21h1
Win10 21H20x5Ewin10-21h2
Win10 22H20x5Ewin10-22h2
Win11 21H20x5Ewin11-21h2
Win11 22H20x5Ewin11-22h2
Win11 23H20x5Ewin11-23h2
Win11 24H20x5Ewin11-24h2
Server 20160x5Ewinserver-2016
Server 20190x5Ewinserver-2019
Server 20220x5Ewinserver-2022
Server 20250x5Ewinserver-2025

Kernel module

ntoskrnl.exeNtTraceEvent

Related APIs

EventWriteEventWriteExEventWriteTransferEventRegisterEtwEventWriteTraceEventNtTraceControl

Syscall stub

4C 8B D1                  mov r10, rcx
B8 5E 00 00 00            mov eax, 0x5E
F6 04 25 08 03 FE 7F 01   test byte ptr [0x7FFE0308], 1
75 03                     jne short +3
0F 05                     syscall
C3                        ret
CD 2E                     int 2Eh
C3                        ret

Undocumented notes

The kernel entry point behind every user-mode ETW *write*. `EventWrite`, `EventWriteEx`, `EventWriteTransfer`, `EtwEventWrite`, and the auto-generated TraceLogging emitters all funnel through `ntdll!EtwEventWriteFull` → `NtTraceEvent`. The TraceHandle is *not* a session handle — it is the per-provider registration handle returned by `EtwRegister`, and the kernel walks the provider's enable mask to decide which sessions receive the event. Its SSN has been `0x5E` from Windows 10 1507 all the way through Windows 11 24H2 and Server 2025, one of the most stable syscall numbers in the system, presumably because it is on the hot path of every Microsoft telemetry stack.

Common malware usage

Limited direct offensive value — calling it writes events that defenders *may* see. The interesting reverse is the opposite: implants that want a private logging channel will deliberately register a provider GUID nobody collects and call `NtTraceEvent` to scribble runtime debug into a session no SOC subscribes to. Some loaders also use it to emit decoy `Microsoft-Windows-*` events to dilute hunt queries. Far more attention goes to *suppressing* writes (see `NtTraceControl`) than to abusing this call directly. A handful of EDR-evasion notes mention overwriting `EtwEventWrite` with `ret` in ntdll, which collapses the user-mode side of this exact code path.

Detection opportunities

On its own, `NtTraceEvent` calls are pure noise — every Office app, browser tab, and Windows service emits thousands per second. What matters is the *absence* of expected events from a process whose ntdll has been patched (e.g. `EtwEventWrite` first byte = `0xC3`). Compare in-memory ntdll bytes against on-disk for the four classic ETW write stubs (`EtwEventWrite`, `EtwEventWriteFull`, `EtwEventWriteEx`, `EtwEventWriteTransfer`); any divergence is high-fidelity. At the session level, log gaps for the `Microsoft-Windows-Threat-Intelligence` and `Microsoft-Windows-Kernel-Process` providers correlated with new process creations are the classic ETW-bypass tell.

Direct syscall examples

cTraceLogging write via EventWriteTransfer

// Standard TraceLogging path — ultimately calls NtTraceEvent.
#include <windows.h>
#include <evntprov.h>

static const GUID kImplantProvider =
    { 0x11111111, 0x2222, 0x3333, { 0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB } };

REGHANDLE hReg = 0;
EventRegister(&kImplantProvider, NULL, NULL, &hReg);

EVENT_DESCRIPTOR ed = { 0 };
ed.Id = 1; ed.Level = 4; // Information

EVENT_DATA_DESCRIPTOR edd;
const wchar_t* msg = L"beacon-checkin";
EventDataDescCreate(&edd, msg, (ULONG)((wcslen(msg) + 1) * sizeof(wchar_t)));

// Funnels through ntdll!EtwEventWriteFull -> NtTraceEvent.
EventWriteTransfer(hReg, &ed, NULL, NULL, 1, &edd);

asmx64 direct stub (SSN 0x5E, stable across all builds)

NtTraceEvent PROC
    mov  r10, rcx
    mov  eax, 5Eh
    syscall
    ret
NtTraceEvent ENDP

rustStealth provider with windows-sys

// Cargo: windows-sys = { version = "0.59", features = ["Win32_System_Diagnostics_Etw"] }
use windows_sys::Win32::System::Diagnostics::Etw::*;
use windows_sys::core::GUID;

const PROVIDER: GUID = GUID::from_u128(0x11111111_2222_3333_4455_66778899AABB);

unsafe fn beacon_log(msg: &[u8]) {
    let mut hreg: u64 = 0;
    EventRegister(&PROVIDER, None, std::ptr::null_mut(), &mut hreg);

    let ed = EVENT_DESCRIPTOR { Id: 1, Level: 4, ..core::mem::zeroed() };
    let mut edd = EVENT_DATA_DESCRIPTOR { Ptr: msg.as_ptr() as u64, Size: msg.len() as u32, Reserved: 0 };
    EventWrite(hreg, &ed, 1, &mut edd);
}

MITRE ATT&CK mappings

Last verified: 2026-05-20