> Windows Syscalls
ntoskrnl.exeT1562.001T1106

NtCancelSynchronousIoFile

Bricht einen synchronen I/O-Aufruf ab, der gerade einen anderen Thread blockiert.

Prototyp

NTSTATUS NtCancelSynchronousIoFile(
  HANDLE           ThreadHandle,
  PIO_STATUS_BLOCK IoRequestToCancel,
  PIO_STATUS_BLOCK IoStatusBlock
);

Argumente

NameTypeDirDescription
ThreadHandleHANDLEinHandle auf den Thread, der gerade in einem synchronen I/O-Aufruf blockiert. Erfordert THREAD_TERMINATE-Zugriff.
IoRequestToCancelPIO_STATUS_BLOCKinOptionaler Zeiger auf den IO_STATUS_BLOCK eines bestimmten synchronen IRP. NULL bricht die synchrone I/O ab, in der der Thread aktuell blockiert.
IoStatusBlockPIO_STATUS_BLOCKoutEmpfängt das Ergebnis der Abbruch-Operation selbst (Status, Information).

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x8Ewin10-1507
Win10 16070x8Ewin10-1607
Win10 17030x8Fwin10-1703
Win10 17090x90win10-1709
Win10 18030x91win10-1803
Win10 18090x91win10-1809
Win10 19030x91win10-1903
Win10 19090x91win10-1909
Win10 20040x93win10-2004
Win10 20H20x93win10-20h2
Win10 21H10x93win10-21h1
Win10 21H20x93win10-21h2
Win10 22H20x93win10-22h2
Win11 21H20x93win11-21h2
Win11 22H20x93win11-22h2
Win11 23H20x93win11-23h2
Win11 24H20x95win11-24h2
Server 20160x8Ewinserver-2016
Server 20190x91winserver-2019
Server 20220x93winserver-2022
Server 20250x95winserver-2025

Kernel-Modul

ntoskrnl.exeNtCancelSynchronousIoFile

Verwandte APIs

CancelSynchronousIoNtCancelIoFileNtCancelIoFileExNtReadFileNtWaitForSingleObject

Syscall-Stub

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

Der Cousin von NtCancelIoFileEx, der *synchrone* I/O anvisiert — Aufrufe wie `ReadFile` auf einem Handle, das ohne `FILE_FLAG_OVERLAPPED` geöffnet wurde, wo der aufrufende Thread im Kernel auf den Abschluss des IRP wartet und selbst keinen Cancel verarbeiten kann. Der Win32-Wrapper `CancelSynchronousIo` nimmt nur den Thread-Handle; die IO_STATUS_BLOCK-gezielte Variante ist nur über die native API erreichbar. Die SSN driftet moderat (`0x91` 1803–1909, `0x93` 2004–23H2, `0x95` 24H2 / Server 2025). Aufruf auf einem Thread, der aktuell nicht in einer synchronen I/O-Wartestellung ist, gibt `STATUS_NOT_FOUND` (0xC0000225) zurück — harmlos.

Häufige Malware-Nutzung

Seltener bewaffnet als NtCancelIoFileEx, aber mit einem markanten Anwendungsfall: **einen EDR-Scanner-Thread freigeben**. Manche User-Mode-EDRs lassen einen Worker laufen, der synchron seine IPC-Pipe (`\\.\pipe\<vendor>`) auf Befehlsnachrichten liest. Kann ein Angreifer ein THREAD_TERMINATE-äquivalentes Handle auf diesen Worker erlangen und ist der EDR-Schutz bereits neutralisiert, bringt `NtCancelSynchronousIoFile` den Worker mit `STATUS_CANCELLED` zurück in den User-Mode — potenziell setzt das den Sensor in einen degradierten Zustand, ohne den Prozess zu töten (was Selbstschutz auslösen würde). Auch von Red-Team-Tooling genutzt, um einen in das Ziel injizierten Thread, der in einer langen synchronen I/O hängt (z. B. ein NetUserGetInfo gegen einen langsamen DC), zwangsweise freizugeben, damit die Injektion sauber zurückgerollt werden kann, ohne den Thread zu verwaisen.

Erkennungs­möglichkeiten

In gutartiger Software wirklich selten. Der prominente legitime Einsatz ist Explorers Pinned-Network-Drive-Disconnect-Pfad (Explorer feuert NtCancelSynchronousIoFile, um den I/O-Thread freizubekommen, der auf einer toten SMB-Freigabe hängt). Außerhalb dieses engen Kontexts sind Aufrufe verdächtig. Das hochauflösende Signal ist *cross-process*: Thread A in Prozess X ruft NtCancelSynchronousIoFile mit einem Handle auf Thread B in Prozess Y auf, wobei Y ein Service oder eine EDR-Komponente ist. ETW `Microsoft-Windows-Kernel-Process` plus die cross-process-Handle-Open-Events aus `Microsoft-Windows-Kernel-Audit-API-Calls` korrelieren das Open-+-Cancel-Paar. Verschärfte ACLs auf EDR-Service-Threads (sodass Nicht-SYSTEM kein THREAD_TERMINATE bekommt) schlagen den Bypass, bevor der Syscall überhaupt erreichbar ist.

Direkte Syscall-Beispiele

asmx64 stub (Win11 24H2 SSN 0x95)

; Direct syscall stub for NtCancelSynchronousIoFile
NtCancelSynchronousIoFile PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 95h          ; SSN (Win11 24H2 / Server 2025)
    syscall
    ret
NtCancelSynchronousIoFile ENDP

cUnstick an injected worker thread

// Free a thread we injected into a target process that ended up blocked
// in a synchronous I/O wait, so the injection can be cleanly unwound.
#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 *pNtCancelSynchronousIoFile)(
    HANDLE ThreadHandle, PIO_STATUS_BLOCK IoRequestToCancel,
    PIO_STATUS_BLOCK IoStatusBlock);

NTSTATUS Unstick(HANDLE h_thread) {
    pNtCancelSynchronousIoFile NtCancelSynchronousIoFile =
        (pNtCancelSynchronousIoFile)GetProcAddress(
            GetModuleHandleA("ntdll.dll"), "NtCancelSynchronousIoFile");
    IO_STATUS_BLOCK out = {0};
    return NtCancelSynchronousIoFile(h_thread, NULL, &out);
}

rustExplorer-style dead-SMB recovery

// Defensive use: cancel a synchronous IO blocking a worker thread we own.
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::IO::CancelSynchronousIo;

pub unsafe fn cancel_blocked(h_thread: HANDLE) -> bool {
    // CancelSynchronousIo(h) wraps NtCancelSynchronousIoFile with IRP=NULL.
    CancelSynchronousIo(h_thread) != 0
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20