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
| Name | Type | Dir | Description |
|---|---|---|---|
| ThreadHandle | HANDLE | in | Handle auf den Thread, der gerade in einem synchronen I/O-Aufruf blockiert. Erfordert THREAD_TERMINATE-Zugriff. |
| IoRequestToCancel | PIO_STATUS_BLOCK | in | Optionaler Zeiger auf den IO_STATUS_BLOCK eines bestimmten synchronen IRP. NULL bricht die synchrone I/O ab, in der der Thread aktuell blockiert. |
| IoStatusBlock | PIO_STATUS_BLOCK | out | Empfängt das Ergebnis der Abbruch-Operation selbst (Status, Information). |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x8E | win10-1507 |
| Win10 1607 | 0x8E | win10-1607 |
| Win10 1703 | 0x8F | win10-1703 |
| Win10 1709 | 0x90 | win10-1709 |
| Win10 1803 | 0x91 | win10-1803 |
| Win10 1809 | 0x91 | win10-1809 |
| Win10 1903 | 0x91 | win10-1903 |
| Win10 1909 | 0x91 | win10-1909 |
| Win10 2004 | 0x93 | win10-2004 |
| Win10 20H2 | 0x93 | win10-20h2 |
| Win10 21H1 | 0x93 | win10-21h1 |
| Win10 21H2 | 0x93 | win10-21h2 |
| Win10 22H2 | 0x93 | win10-22h2 |
| Win11 21H2 | 0x93 | win11-21h2 |
| Win11 22H2 | 0x93 | win11-22h2 |
| Win11 23H2 | 0x93 | win11-23h2 |
| Win11 24H2 | 0x95 | win11-24h2 |
| Server 2016 | 0x8E | winserver-2016 |
| Server 2019 | 0x91 | winserver-2019 |
| Server 2022 | 0x93 | winserver-2022 |
| Server 2025 | 0x95 | winserver-2025 |
Kernel-Modul
Verwandte APIs
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.
Erkennungsmö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 ENDPcUnstick 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