NtWriteFile
Écrit des données dans un fichier, pipe ou périphérique ouvert — pendant noyau de NtCreateFile pour déposer un payload.
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
| Name | Type | Dir | Description |
|---|---|---|---|
| FileHandle | HANDLE | in | Handle ouvert par NtCreateFile/NtOpenFile avec GENERIC_WRITE ou FILE_WRITE_DATA. |
| Event | HANDLE | in | Événement optionnel signalé à la fin d'une écriture async. NULL pour les handles synchrones. |
| ApcRoutine | PIO_APC_ROUTINE | in | APC optionnel livré à la complétion. Quasi toujours NULL en dehors d'E/S superposées. |
| ApcContext | PVOID | in | Contexte transmis à ApcRoutine. NULL si ApcRoutine est NULL. |
| IoStatusBlock | PIO_STATUS_BLOCK | out | Reçoit le NTSTATUS final et le champ Information avec le nombre d'octets écrits. |
| Buffer | PVOID | in | Pointeur vers les données à écrire. |
| Length | ULONG | in | Nombre d'octets à écrire depuis Buffer. |
| ByteOffset | PLARGE_INTEGER | in | Offset dans le fichier, ou FILE_USE_FILE_POINTER_POSITION (-2) pour la position courante. |
| Key | PULONG | in | Clé optionnelle de verrou par plage d'octets. Quasi toujours NULL. |
IDs de syscalls par version de Windows
| Version de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x8 | win10-1507 |
| Win10 1607 | 0x8 | win10-1607 |
| Win10 1703 | 0x8 | win10-1703 |
| Win10 1709 | 0x8 | win10-1709 |
| Win10 1803 | 0x8 | win10-1803 |
| Win10 1809 | 0x8 | win10-1809 |
| Win10 1903 | 0x8 | win10-1903 |
| Win10 1909 | 0x8 | win10-1909 |
| Win10 2004 | 0x8 | win10-2004 |
| Win10 20H2 | 0x8 | win10-20h2 |
| Win10 21H1 | 0x8 | win10-21h1 |
| Win10 21H2 | 0x8 | win10-21h2 |
| Win10 22H2 | 0x8 | win10-22h2 |
| Win11 21H2 | 0x8 | win11-21h2 |
| Win11 22H2 | 0x8 | win11-22h2 |
| Win11 23H2 | 0x8 | win11-23h2 |
| Win11 24H2 | 0x8 | win11-24h2 |
| Server 2016 | 0x8 | winserver-2016 |
| Server 2019 | 0x8 | winserver-2019 |
| Server 2022 | 0x8 | winserver-2022 |
| Server 2025 | 0x8 | winserver-2025 |
Module noyau
APIs liées
Stub du 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
Notes non documentées
SSN `0x8` — l'un des plus bas, inchangé depuis Windows 7 — rendant NtWriteFile facile à appeler sans résolution dynamique. L'API a 9 paramètres, dont seuls FileHandle, IoStatusBlock, Buffer, Length et ByteOffset sont couramment non-NULL. Si le handle a été ouvert avec `FILE_SYNCHRONOUS_IO_NONALERT` (cas typique), l'appel bloque jusqu'à la fin de l'écriture et l'IoStatusBlock est rempli avant le retour. L'acheminement passe par IopXxxControlFile → IRP_MJ_WRITE du driver, donc tous les filtres de système de fichiers et de stockage de la pile voient l'écriture.
Usage courant par les malwares
Couplé à NtCreateFile, il constitue à lui seul la chaîne de mise à disque : ouvrir en FILE_OVERWRITE_IF, NtWriteFile du stage suivant chiffré, fermer. Les ransomwares s'en servent pendant le chiffrement (ouvrir l'original, NtReadFile, chiffrer, NtWriteFile par-dessus ou vers un compagnon `.locked`). Les wipers l'utilisent pour zéroter `\Device\PhysicalDriveN` ou écraser les fichiers de hive du registre (HermeticWiper, CaddyWiper). Les loaders qui écrivent dans `%APPDATA%\Microsoft\Templates\Normal.dotm` ou un raccourci du dossier Startup bouclent la persistance sans jamais toucher à kernel32!WriteFile.
Opportunités de détection
Contrairement à NtCreateFile, NtWriteFile produit peu de télémétrie dédiée : la *création* déclenche Sysmon 11 / Kernel-File `Create`, mais l'événement par écriture est trop bruyant pour être logué par défaut. Les EDR se placent dans la couche minifilter et voient les données effectivement écrites, ce qui permet aux moteurs YARA-on-write et aux leurres ransomware de fonctionner. Microsoft-Windows-Kernel-File event 12 (`Write`) existe mais n'est généralement activé qu'en réponse à incident en raison du volume. L'asymétrie est opérationnelle : les défenseurs détectent les ransomwares par *ce qui* est écrit (contenu à haute entropie, extensions retournées, renommages en masse), pas par le nombre d'appels NtWriteFile. Corréler avec NtSetInformationFile/FileDispositionInformation pour saisir le renommage ou la suppression qui suit.
Exemples de syscalls directs
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 ENDPrustAppend 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(),
)
}Mappings MITRE ATT&CK
Last verified: 2026-05-20