> Windows Syscalls
ntoskrnl.exeT1562.001T1106

NtCancelSynchronousIoFile

Cancels a synchronous I/O call that is currently blocking another thread.

Prototype

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

Arguments

NameTypeDirDescription
ThreadHandleHANDLEinHandle to the thread currently blocked inside a synchronous I/O call. Requires THREAD_TERMINATE access.
IoRequestToCancelPIO_STATUS_BLOCKinOptional pointer to the IO_STATUS_BLOCK of a specific synchronous IRP. NULL cancels whichever synchronous I/O the thread is currently blocked on.
IoStatusBlockPIO_STATUS_BLOCKoutReceives the result of the cancel operation itself (Status, Information).

Syscall IDs by 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 module

ntoskrnl.exeNtCancelSynchronousIoFile

Related 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

Undocumented notes

The cousin to NtCancelIoFileEx that targets *synchronous* I/O — calls like `ReadFile` against a handle that was opened without `FILE_FLAG_OVERLAPPED`, where the caller thread is sitting inside the kernel waiting for the IRP to complete and cannot itself service a cancel. The Win32 wrapper `CancelSynchronousIo` takes only the thread handle; the IO_STATUS_BLOCK targeting variant is reachable only through the native API. The SSN drifts modestly (`0x91` 1803–1909, `0x93` 2004–23H2, `0x95` 24H2 / Server 2025). Calling on a thread that isn't currently in a synchronous I/O wait returns `STATUS_NOT_FOUND` (0xC0000225) — benign.

Common malware usage

Less commonly weaponised than NtCancelIoFileEx but with a distinctive use case: **unblocking an EDR scanner thread**. Some user-mode EDRs run a worker that synchronously reads its IPC pipe (`\\.\pipe\<vendor>`) waiting for command messages. If an attacker can obtain a THREAD_TERMINATE-equivalent handle to that worker and the EDR's protection is already neutered, `NtCancelSynchronousIoFile` returns the worker to user mode with `STATUS_CANCELLED` — potentially knocking the sensor into a degraded state without killing the process (which would trip self-protection). Also used by red-team tooling to forcibly unstick a thread it injected into the target that ended up blocked on a long synchronous I/O (e.g. a NetUserGetInfo against a slow DC) so the injection can be unwound cleanly without orphaning the thread.

Detection opportunities

Genuinely rare in benign software. The flagship legitimate use is Explorer's pinned-network-drive disconnect path (Explorer fires NtCancelSynchronousIoFile to unstick the I/O thread blocking on a dead SMB share). Outside that narrow context, calls are suspicious. The high-fidelity signal is *cross-process*: thread A in process X calls NtCancelSynchronousIoFile with a handle to thread B in process Y, where Y is a service or EDR component. ETW `Microsoft-Windows-Kernel-Process` plus the cross-process handle-open events from `Microsoft-Windows-Kernel-Audit-API-Calls` correlate the open + cancel pair. Tightening ACLs on EDR service threads (so non-SYSTEM can't acquire THREAD_TERMINATE) defeats the bypass before the syscall is reachable.

Direct syscall examples

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