> Windows Syscalls
ntoskrnl.exeT1562.001T1106

NtCancelIoFileEx

Cancela una solicitud de E/S específica pendiente sobre un archivo, sin importar qué hilo la emitió.

Prototipo

NTSTATUS NtCancelIoFileEx(
  HANDLE           FileHandle,
  PIO_STATUS_BLOCK IoRequestToCancel,
  PIO_STATUS_BLOCK IoStatusBlock
);

Argumentos

NameTypeDirDescription
FileHandleHANDLEinHandle al archivo / dispositivo / pipe cuya E/S debe ser cancelada.
IoRequestToCancelPIO_STATUS_BLOCKinPuntero opcional al IO_STATUS_BLOCK que identifica un IRP en vuelo específico. NULL cancela todo IRP pendiente del handle desde cualquier hilo.
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 15070x8Dwin10-1507
Win10 16070x8Dwin10-1607
Win10 17030x8Ewin10-1703
Win10 17090x8Fwin10-1709
Win10 18030x90win10-1803
Win10 18090x90win10-1809
Win10 19030x90win10-1903
Win10 19090x90win10-1909
Win10 20040x92win10-2004
Win10 20H20x92win10-20h2
Win10 21H10x92win10-21h1
Win10 21H20x92win10-21h2
Win10 22H20x92win10-22h2
Win11 21H20x92win11-21h2
Win11 22H20x92win11-22h2
Win11 23H20x92win11-23h2
Win11 24H20x94win11-24h2
Server 20160x8Dwinserver-2016
Server 20190x90winserver-2019
Server 20220x92winserver-2022
Server 20250x94winserver-2025

Módulo del kernel

ntoskrnl.exeNtCancelIoFileEx

APIs relacionadas

CancelIoExNtCancelIoFileNtCancelSynchronousIoFileNtReadFileNtWriteFileNtDeviceIoControlFile

Stub del syscall

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

Introducido en Vista para corregir la limitación de alcance single-thread de NtCancelIoFile. Con `IoRequestToCancel == NULL` se comporta como CancelIo pero cross-thread; con un puntero IO_STATUS_BLOCK no NULL, el kernel recorre la lista de IRP del archivo, hace match con el IRP cuyo `UserIosb` igual al puntero proporcionado y cancela exactamente ése. El thunk Win32 `CancelIoEx` expone los dos modos. El SSN se mueve modestamente: `0x90` en Win10 1803–1909, `0x92` en 2004–23H2, `0x94` en Win11 24H2 / Server 2025.

Uso común por malware

El más rico del triplete de cancel, y el que es genuinamente útil para patrones de **bypass de hook EDR / race-cancel**. El ataque de manual: el hook user-mode `NtReadFile` de un EDR realiza inspección síncrona del contenido antes de reenviar la llamada. Despachando la lectura overlapped (`FILE_FLAG_OVERLAPPED`) y llamando inmediatamente `NtCancelIoFileEx` contra el IO_STATUS_BLOCK *específico* desde otro hilo helper, la lectura se arranca antes de que dispare la rutina de finalización del EDR — dejando al EDR con `STATUS_CANCELLED` y sin datos para inspeccionar. Implants reales también lo usan para teardown limpio de workers pipe / socket multihilo en canales C2, y como watchdog para abortar llamadas `DeviceIoControl` colgadas contra drivers minifilter no responsivos.

Oportunidades de detección

Mayor señal que NtCancelIoFile porque la forma *dirigida* es mucho menos común en software benigno. Hooks sobre `CancelIoEx` user-mode más ETW `Microsoft-Windows-Kernel-File` y trazas WPP IRP pueden correlacionar un patrón sub-milisegundo emisión-luego-cancelación sobre el mismo handle. Heurística fiable: un hilo que llama NtCancelIoFileEx con un `IoRequestToCancel` no NULL apuntando a un IO_STATUS_BLOCK *cuyo NtReadFile / NtWriteFile / NtDeviceIoControlFile emparejado se completó en otro hilo menos de un milisegundo antes*, mientras el file object es uno de los handles protegidos por EDR (mfeavfk, csagent, MsSecFlt, etc.). Los EDR que mueven la inspección a un minifilter de kernel (WdFilter de Defender, CSAgent de CrowdStrike) son inmunes a esta carrera porque su hook corre *antes* de que el IRP se complete de vuelta a user mode.

Ejemplos de syscalls directos

asmx64 stub (Win11 24H2 SSN 0x94)

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

cRace-cancel against an EDR user-mode hook

// PoC: issue an overlapped read, then yank the IRP from a helper thread
// targeting the specific IO_STATUS_BLOCK before any synchronous EDR
// completion inspection can fire.
#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 *pNtCancelIoFileEx)(HANDLE, PIO_STATUS_BLOCK, PIO_STATUS_BLOCK);

typedef struct { HANDLE h; PIO_STATUS_BLOCK iosb; } cancel_ctx;

static DWORD WINAPI Yanker(LPVOID p) {
    cancel_ctx *c = (cancel_ctx*)p;
    pNtCancelIoFileEx NtCancelIoFileEx = (pNtCancelIoFileEx)GetProcAddress(
        GetModuleHandleA("ntdll.dll"), "NtCancelIoFileEx");
    IO_STATUS_BLOCK out = {0};
    return NtCancelIoFileEx(c->h, c->iosb, &out);
}

NTSTATUS RaceCancelRead(HANDLE h, PVOID buf, ULONG len) {
    OVERLAPPED ov = {0};
    DWORD got = 0;
    ReadFile(h, buf, len, &got, &ov);                  // hooked path
    cancel_ctx c = { h, (PIO_STATUS_BLOCK)&ov };       // OVERLAPPED layout == IO_STATUS_BLOCK
    HANDLE t = CreateThread(NULL, 0, Yanker, &c, 0, NULL);
    WaitForSingleObject(t, INFINITE);
    CloseHandle(t);
    return 0;
}

rustCross-thread cancel-all-on-handle

// Tear down every outstanding IRP on a handle from any thread.
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::IO::CancelIoEx;

pub unsafe fn cancel_all(h: HANDLE) -> bool {
    // CancelIoEx(h, NULL) — under the hood NtCancelIoFileEx with NULL IRP.
    CancelIoEx(h, std::ptr::null_mut()) != 0
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20