> Windows Syscalls
ntoskrnl.exeT1486T1561.001T1106

NtWriteFile

Writes data to an open file, pipe, or device — the kernel companion to NtCreateFile for dropping payloads.

Prototype

NTSTATUS NtWriteFile(
  HANDLE           FileHandle,
  HANDLE           Event,
  PIO_APC_ROUTINE  ApcRoutine,
  PVOID            ApcContext,
  PIO_STATUS_BLOCK IoStatusBlock,
  PVOID            Buffer,
  ULONG            Length,
  PLARGE_INTEGER   ByteOffset,
  PULONG           Key
);

Arguments

NameTypeDirDescription
FileHandleHANDLEinHandle opened by NtCreateFile/NtOpenFile with GENERIC_WRITE or FILE_WRITE_DATA.
EventHANDLEinOptional event signaled when async write completes. NULL for synchronous handles.
ApcRoutinePIO_APC_ROUTINEinOptional APC delivered on completion. Almost always NULL outside of overlapped I/O.
ApcContextPVOIDinContext passed to ApcRoutine. NULL if ApcRoutine is NULL.
IoStatusBlockPIO_STATUS_BLOCKoutReceives final NTSTATUS and Information field with bytes written.
BufferPVOIDinPointer to the source data to write.
LengthULONGinNumber of bytes to write from Buffer.
ByteOffsetPLARGE_INTEGERinFile offset, or FILE_USE_FILE_POINTER_POSITION (-2) to use the current position.
KeyPULONGinOptional byte-range lock key. Almost always NULL.

Syscall IDs by Windows version

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

Kernel module

ntoskrnl.exeNtWriteFile

Related APIs

WriteFileWriteFileExNtReadFileNtCreateFileNtSetInformationFileFlushFileBuffers

Syscall stub

4C 8B D1            mov r10, rcx
B8 08 00 00 00      mov eax, 0x8
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

SSN `0x8` — one of the lowest, and unchanged since Windows 7 — making NtWriteFile easy to call without any dynamic SSN resolution. Behind the API are 9 parameters, of which only FileHandle, IoStatusBlock, Buffer, Length and ByteOffset are routinely non-NULL. If the handle was opened with `FILE_SYNCHRONOUS_IO_NONALERT` (the typical case), the call blocks until the write completes and the IoStatusBlock is filled before return. Routes through IopXxxControlFile → driver IRP_MJ_WRITE, so every filesystem and storage filter driver in the device stack sees the write.

Common malware usage

Combined with NtCreateFile this is the entirety of the on-disk payload-staging chain: open with FILE_OVERWRITE_IF, NtWriteFile the encrypted next stage, close. Ransomware uses it during encryption (open original, NtReadFile contents, encrypt, NtWriteFile back over the same file or to a `.locked` companion). Wipers use it to zero out `\Device\PhysicalDriveN` or to overwrite registry hive files (HermeticWiper, CaddyWiper). Loaders writing to `%APPDATA%\Microsoft\Templates\Normal.dotm` or to a Startup folder shortcut close the persistence loop without ever touching kernel32!WriteFile.

LockBitContiBlackCat (ALPHV)HermeticWiperCaddyWiperRoyal

Detection opportunities

Unlike NtCreateFile, NtWriteFile rarely produces dedicated telemetry on its own — file *creation* fires Sysmon 11 / Kernel-File `Create`, but per-write events are too noisy to log by default. EDRs sit in the minifilter layer and see the data being written, which is how content-based YARA-on-write and ransomware-canary engines work. Microsoft-Windows-Kernel-File event 12 (`Write`) is available but typically only enabled during incident-response captures because of volume. The asymmetry matters operationally: defenders detect ransomware by *what* is being written (high-entropy content, swapped extensions, mass renames), not by NtWriteFile invocation counts. Correlate with NtSetInformationFile/FileDispositionInformation to catch the rename or unlink that usually follows.

Direct syscall examples

cWrite payload to a freshly-created file

// After NtCreateFile gave us a synchronous, writable handle:
IO_STATUS_BLOCK iosb = {0};
LARGE_INTEGER offset = { .QuadPart = 0 };
NTSTATUS s = NtWriteFile(
    hFile,
    NULL, NULL, NULL,         // no event, no APC
    &iosb,
    payload, payloadLen,
    &offset,                  // write at start of file
    NULL);                    // no lock key
if (NT_SUCCESS(s)) {
    // iosb.Information == bytes written
}

asmDirect stub (SSN 0x8)

NtWriteFile PROC
    mov  r10, rcx
    mov  eax, 8
    syscall
    ret
NtWriteFile ENDP

rustAppend to a log device via ntapi

use ntapi::ntioapi::{NtWriteFile, IO_STATUS_BLOCK};

pub unsafe fn write_all(handle: *mut core::ffi::c_void, data: &[u8]) -> i32 {
    let mut iosb: IO_STATUS_BLOCK = core::mem::zeroed();
    // -2 == FILE_USE_FILE_POINTER_POSITION
    let mut offset: i64 = -2;
    NtWriteFile(
        handle,
        core::ptr::null_mut(),
        None,
        core::ptr::null_mut(),
        &mut iosb,
        data.as_ptr() as *mut _,
        data.len() as u32,
        &mut offset as *mut _ as *mut _,
        core::ptr::null_mut(),
    )
}

MITRE ATT&CK mappings

Last verified: 2026-05-20