> Windows Syscalls
ntoskrnl.exeT1562.001T1106

NtCancelIoFile

Annule chaque requête I/O en attente émise par le thread appelant sur un handle fichier.

Prototype

NTSTATUS NtCancelIoFile(
  HANDLE           FileHandle,
  PIO_STATUS_BLOCK IoStatusBlock
);

Arguments

NameTypeDirDescription
FileHandleHANDLEinHandle vers le fichier / périphérique / pipe dont les I/O émises par *le thread appelant* doivent être annulées.
IoStatusBlockPIO_STATUS_BLOCKoutReçoit le résultat de l'opération d'annulation elle-même (Status, Information). Pas le statut d'un IRP annulé.

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070x5Dwin10-1507
Win10 16070x5Dwin10-1607
Win10 17030x5Dwin10-1703
Win10 17090x5Dwin10-1709
Win10 18030x5Dwin10-1803
Win10 18090x5Dwin10-1809
Win10 19030x5Dwin10-1903
Win10 19090x5Dwin10-1909
Win10 20040x5Dwin10-2004
Win10 20H20x5Dwin10-20h2
Win10 21H10x5Dwin10-21h1
Win10 21H20x5Dwin10-21h2
Win10 22H20x5Dwin10-22h2
Win11 21H20x5Dwin11-21h2
Win11 22H20x5Dwin11-22h2
Win11 23H20x5Dwin11-23h2
Win11 24H20x5Dwin11-24h2
Server 20160x5Dwinserver-2016
Server 20190x5Dwinserver-2019
Server 20220x5Dwinserver-2022
Server 20250x5Dwinserver-2025

Module noyau

ntoskrnl.exeNtCancelIoFile

APIs liées

CancelIoNtCancelIoFileExNtCancelSynchronousIoFileNtReadFileNtWriteFile

Stub du syscall

4C 8B D1            mov r10, rcx
B8 5D 00 00 00      mov eax, 0x5D
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 `0x5D` sur tous les builds Windows 10 / 11 / Server — stabilité exceptionnelle pour un syscall I/O fichier. La portée critique : NtCancelIoFile n'annule que les I/O émises par *le thread appelant* sur le handle donné. Il n'affecte *pas* les I/O émises par d'autres threads sur le même handle (c'est pourquoi NtCancelIoFileEx a été introduit dans Vista). Le `CancelIo` Win32 enveloppe directement cet appel. Le noyau parcourt la liste des IRP attachée au TCB du thread et au FsContext du fichier, marque chaque `IRP->Cancel = TRUE` et appelle la routine de cancel du driver, qui est censée compléter les IRP avec `STATUS_CANCELLED` (0xC0000120).

Usage courant par les malwares

Utilisé dans des motifs défensifs d'**évasion de hook EDR** : beaucoup d'EDR user-mode hookent `ReadFile` / `WriteFile` / `NtReadFile` pour inspecter le contenu ; si la routine d'inspection est asynchrone (queuant un callback à la complétion I/O), un attaquant peut race-annuler l'IRP avant que la routine de complétion ne se déclenche, empêchant l'EDR d'observer les octets. Plus courant en usage offensif : du code de fiabilité dans les implants qui parlent par named pipes — si le peer meurt, l'implant déclenche `NtCancelIoFile` pour flusher les lectures overlapped bloquées et récupérer le worker thread plutôt que pendre jusqu'au timeout TCP / pipe noyau. Certains outillages red-team l'appellent aussi en shutdown gracieux pour éviter de laisser des IRP orphelines visibles dans `!irp` debugger lors de revue forensique.

Opportunités de détection

NtCancelIoFile est extrêmement courant dans le logiciel I/O-intensif légitime (navigateurs annulant des lectures HTTPS en vol à la navigation, IDE annulant des file watchers, OneDrive annulant la sync au offline-toggle) et constitue donc un mauvais signal primaire. La télémétrie intéressante est le *triplet* : une lecture async en attente sur un handle sensible (named-pipe \\.\pipe\protected_storage_pipe, handle IPC injecté par EDR, socket HTTPS portant du C2) annulée dans les microsecondes suivant son émission par le même thread qui l'a émise. ETW `Microsoft-Windows-Kernel-File` plus la corrélation IRP de complétion via WPP peuvent montrer le chemin de l'IRP annulée ; combiner avec PsSetCreateProcessNotifyRoutineEx pour attribuer thread-à-processus. La plupart des EDR n'exposent pas d'événement cancel de première classe.

Exemples de syscalls directs

asmx64 direct stub

; Direct syscall stub for NtCancelIoFile (SSN 0x5D, all builds)
NtCancelIoFile PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 5Dh          ; SSN
    syscall
    ret
NtCancelIoFile ENDP

cImplant pipe shutdown

// Implant teardown: when the C2 peer dies, free our blocked overlapped
// read on the named pipe so the worker thread can exit instead of hanging.
#include <windows.h>

typedef struct _IO_STATUS_BLOCK {
    union { NTSTATUS Status; PVOID Pointer; };
    ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

typedef NTSTATUS (NTAPI *pNtCancelIoFile)(HANDLE, PIO_STATUS_BLOCK);

void ShutdownPipe(HANDLE h_pipe) {
    pNtCancelIoFile NtCancelIoFile = (pNtCancelIoFile)GetProcAddress(
        GetModuleHandleA("ntdll.dll"), "NtCancelIoFile");
    IO_STATUS_BLOCK iosb = {0};
    NtCancelIoFile(h_pipe, &iosb);   // only kills this thread's pending IRPs
    CloseHandle(h_pipe);
}

rustrace-cancel a hooked async read

// PoC: dispatch an async read, then cancel before the EDR's completion
// inspection runs. Same thread issues and cancels.
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::Storage::FileSystem::{ReadFile, OVERLAPPED};
use windows_sys::Win32::System::IO::CancelIo;

pub unsafe fn race_cancel(h: HANDLE, buf: &mut [u8], ov: *mut OVERLAPPED) {
    let mut dummy = 0u32;
    let _ = ReadFile(h, buf.as_mut_ptr() as _, buf.len() as u32, &mut dummy, ov);
    // Immediately yank the IRP before any completion routine fires.
    let _ = CancelIo(h);  // CancelIo == NtCancelIoFile under the hood
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20