> Windows Syscalls
ntoskrnl.exeT1562.001T1106

NtCancelIoFile

Bricht jede ausstehende I/O-Anforderung ab, die der aufrufende Thread auf einem Datei-Handle gestellt hat.

Prototyp

NTSTATUS NtCancelIoFile(
  HANDLE           FileHandle,
  PIO_STATUS_BLOCK IoStatusBlock
);

Argumente

NameTypeDirDescription
FileHandleHANDLEinHandle auf die Datei / das Gerät / die Pipe, deren vom *aufrufenden Thread* gestellte I/O abgebrochen werden soll.
IoStatusBlockPIO_STATUS_BLOCKoutEmpfängt das Ergebnis der Abbruch-Operation selbst (Status, Information). Nicht den Status eines abgebrochenen IRP.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
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

Kernel-Modul

ntoskrnl.exeNtCancelIoFile

Verwandte APIs

CancelIoNtCancelIoFileExNtCancelSynchronousIoFileNtReadFileNtWriteFile

Syscall-Stub

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

Undokumentierte Hinweise

SSN `0x5D` über alle Windows-10-/11-/Server-Builds — außergewöhnliche Stabilität für einen Datei-I/O-Syscall. Der entscheidende Scope-Vorbehalt: NtCancelIoFile bricht nur I/O ab, die der *aufrufende Thread* auf dem gegebenen Handle gestellt hat. I/O anderer Threads auf demselben Handle ist *nicht* betroffen (dafür wurde NtCancelIoFileEx in Vista eingeführt). `CancelIo` (Win32) wrappt diesen Aufruf direkt. Der Kernel läuft die an den Thread-TCB und den FsContext der Datei angehängte IRP-Liste durch, markiert jeden `IRP->Cancel = TRUE` und ruft die Cancel-Routine des Treibers auf, die die IRPs mit `STATUS_CANCELLED` (0xC0000120) abschließen soll.

Häufige Malware-Nutzung

Wird in defensiven **EDR-Hook-Evasion**-Mustern eingesetzt: viele User-Mode-EDRs hooken `ReadFile` / `WriteFile` / `NtReadFile` zur Inhaltsinspektion; ist die Inspektionsroutine asynchron (queuet einen Callback auf I/O-Completion), kann ein Angreifer den IRP race-canceln, bevor die Completion-Routine feuert, und den EDR die Bytes nie sehen lassen. Häufiger im offensiven Einsatz ist Reliability-Code in Implants, die über Named Pipes sprechen — stirbt der Peer, ruft das Implant `NtCancelIoFile`, um blockierte Overlapped-Reads zu flushen und den Worker-Thread zurückzugewinnen, statt bis zum Kernel-TCP-/Pipe-Timeout zu hängen. Etwas Red-Team-Tooling ruft es auch beim Graceful-Shutdown, um keine Waisen-IRPs zu hinterlassen, die bei forensischer Timeline-Prüfung in `!irp`-Debugger-Output auftauchen.

Erkennungs­möglichkeiten

NtCancelIoFile ist in legitimer I/O-intensiver Software extrem häufig (Browser brechen In-Flight-HTTPS-Reads bei Navigation ab, IDEs brechen File-Watcher ab, OneDrive bricht Sync beim Offline-Toggle ab) und ist daher ein schlechtes Primärsignal. Interessante Telemetrie ist das *Triplett*: ein ausstehender Async-Read auf einem sensitiven Handle (Named Pipe \\.\pipe\protected_storage_pipe, EDR-injizierter IPC-Handle, HTTPS-Socket mit C2), der innerhalb von Mikrosekunden nach Issue vom selben Thread abgebrochen wird, der ihn ausgegeben hat. ETW `Microsoft-Windows-Kernel-File` plus Completion-IRP-Korrelation via WPP können den Pfad des abgebrochenen IRP zeigen; mit PsSetCreateProcessNotifyRoutineEx kombinieren, um Thread-zu-Prozess zuzuordnen. Die meisten EDRs exponieren kein erstklassiges Cancel-Event.

Direkte Syscall-Beispiele

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
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20