> Windows Syscalls
ntoskrnl.exeT1562.001T1106

NtCancelSynchronousIoFile

Cancela una llamada de E/S síncrona que está bloqueando actualmente a otro hilo.

Prototipo

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

Argumentos

NameTypeDirDescription
ThreadHandleHANDLEinHandle al hilo actualmente bloqueado en una llamada de E/S síncrona. Requiere acceso THREAD_TERMINATE.
IoRequestToCancelPIO_STATUS_BLOCKinPuntero opcional al IO_STATUS_BLOCK de un IRP síncrono específico. NULL cancela cualquier E/S síncrona en la que el hilo esté bloqueado.
IoStatusBlockPIO_STATUS_BLOCKoutRecibe el resultado de la operación de cancelación en sí (Status, Information).

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
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

Módulo del kernel

ntoskrnl.exeNtCancelSynchronousIoFile

APIs relacionadas

CancelSynchronousIoNtCancelIoFileNtCancelIoFileExNtReadFileNtWaitForSingleObject

Stub del syscall

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

Notas no documentadas

El primo de NtCancelIoFileEx que apunta a E/S *síncrona* — llamadas como `ReadFile` sobre un handle abierto sin `FILE_FLAG_OVERLAPPED`, donde el hilo llamador está sentado en el kernel esperando que el IRP se complete y no puede atender una cancelación por sí mismo. El wrapper Win32 `CancelSynchronousIo` sólo toma el handle de hilo; la variante que apunta a un IO_STATUS_BLOCK sólo es alcanzable a través de la API nativa. El SSN deriva modestamente (`0x91` 1803–1909, `0x93` 2004–23H2, `0x95` 24H2 / Server 2025). Llamarlo sobre un hilo que no está actualmente en espera de E/S síncrona retorna `STATUS_NOT_FOUND` (0xC0000225) — benigno.

Uso común por malware

Menos comúnmente armado que NtCancelIoFileEx pero con un caso de uso distintivo: **desbloquear un hilo scanner de EDR**. Algunos EDR user-mode ejecutan un worker que lee síncronamente su pipe IPC (`\\.\pipe\<vendor>`) esperando mensajes de comando. Si un atacante puede obtener un handle equivalente a THREAD_TERMINATE a ese worker y la protección del EDR ya está neutralizada, `NtCancelSynchronousIoFile` devuelve el worker a user mode con `STATUS_CANCELLED` — potencialmente dejando al sensor en estado degradado sin matar el proceso (lo que dispararía la autoprotección). También usado por tooling red-team para descongelar un hilo que inyectó en el objetivo y que terminó bloqueado en una E/S síncrona larga (p. ej. un NetUserGetInfo contra un DC lento) para que la inyección se pueda desmontar limpiamente sin orfanar el hilo.

Oportunidades de detección

Genuinamente raro en software benigno. El uso legítimo emblemático es la ruta de desconexión de unidad de red anclada de Explorer (Explorer dispara NtCancelSynchronousIoFile para desatascar el hilo de E/S bloqueado en un share SMB muerto). Fuera de ese contexto estrecho, las llamadas son sospechosas. La señal de alta fidelidad es *cross-process*: el hilo A en el proceso X llama NtCancelSynchronousIoFile con un handle al hilo B en el proceso Y, donde Y es un servicio o componente EDR. ETW `Microsoft-Windows-Kernel-Process` más los eventos de apertura de handle cross-process de `Microsoft-Windows-Kernel-Audit-API-Calls` correlacionan el par open + cancel. Endurecer las ACL en hilos de servicio EDR (para que un no-SYSTEM no pueda adquirir THREAD_TERMINATE) derrota el bypass antes de que el syscall sea alcanzable.

Ejemplos de syscalls directos

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
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20