> Windows Syscalls
ntoskrnl.exeT1486T1561.001T1106

NtWriteFile

Escribe datos en un archivo, canal o dispositivo abierto — pareja kernel de NtCreateFile para soltar payloads.

Prototipo

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
);

Argumentos

NameTypeDirDescription
FileHandleHANDLEinHandle abierto por NtCreateFile/NtOpenFile con GENERIC_WRITE o FILE_WRITE_DATA.
EventHANDLEinEvento opcional señalado al completar una escritura asíncrona. NULL para handles síncronos.
ApcRoutinePIO_APC_ROUTINEinAPC opcional entregado al completar. Casi siempre NULL fuera de E/S superpuesta.
ApcContextPVOIDinContexto pasado a ApcRoutine. NULL si ApcRoutine es NULL.
IoStatusBlockPIO_STATUS_BLOCKoutRecibe el NTSTATUS final y el campo Information con los bytes escritos.
BufferPVOIDinPuntero a los datos a escribir.
LengthULONGinNúmero de bytes a escribir desde Buffer.
ByteOffsetPLARGE_INTEGERinDesplazamiento en el archivo, o FILE_USE_FILE_POINTER_POSITION (-2) para la posición actual.
KeyPULONGinClave opcional de bloqueo por rango de bytes. Casi siempre NULL.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
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

Módulo del kernel

ntoskrnl.exeNtWriteFile

APIs relacionadas

WriteFileWriteFileExNtReadFileNtCreateFileNtSetInformationFileFlushFileBuffers

Stub del syscall

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

Notas no documentadas

SSN `0x8` — uno de los más bajos, sin cambios desde Windows 7 — facilita llamar a NtWriteFile sin resolución dinámica. Tras la API hay 9 parámetros, de los cuales solo FileHandle, IoStatusBlock, Buffer, Length y ByteOffset suelen ser no nulos. Si el handle se abrió con `FILE_SYNCHRONOUS_IO_NONALERT` (caso típico), la llamada bloquea hasta completar y se rellena IoStatusBlock antes de retornar. Pasa por IopXxxControlFile → IRP_MJ_WRITE del driver, así que cada filtro de sistema de archivos y de almacenamiento ve la escritura.

Uso común por malware

Combinado con NtCreateFile constituye la cadena entera de staging en disco: abrir con FILE_OVERWRITE_IF, NtWriteFile del siguiente stage cifrado, cerrar. El ransomware lo emplea durante el cifrado (abrir original, NtReadFile, cifrar, NtWriteFile sobre el mismo archivo o sobre un compañero `.locked`). Los wipers lo usan para cerar `\Device\PhysicalDriveN` o sobrescribir hives del registro (HermeticWiper, CaddyWiper). Los loaders que escriben en `%APPDATA%\Microsoft\Templates\Normal.dotm` o en un acceso directo de la carpeta Startup cierran el bucle de persistencia sin tocar kernel32!WriteFile.

Oportunidades de detección

A diferencia de NtCreateFile, NtWriteFile rara vez produce telemetría dedicada por sí solo: la *creación* dispara Sysmon 11 / Kernel-File `Create`, pero los eventos por escritura son demasiado ruidosos para registrarlos por defecto. Los EDR residen en la capa minifilter y ven los datos efectivamente escritos, que es como funcionan los motores YARA-on-write y los honeypots anti-ransomware. Microsoft-Windows-Kernel-File event 12 (`Write`) existe pero suele activarse solo durante respuesta a incidentes por su volumen. La asimetría es operativa: los defensores detectan ransomware por *lo que* se escribe (contenido de alta entropía, extensiones cambiadas, renombrados masivos), no por contar invocaciones a NtWriteFile. Correlacionar con NtSetInformationFile/FileDispositionInformation para capturar el renombrado o borrado posterior.

Ejemplos de syscalls directos

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(),
    )
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20