NtCancelIoFile
Cancela toda solicitud de E/S pendiente emitida por el hilo llamador sobre un handle de archivo.
Prototipo
NTSTATUS NtCancelIoFile( HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| FileHandle | HANDLE | in | Handle al archivo / dispositivo / pipe cuya E/S emitida por *el hilo llamador* debe cancelarse. |
| IoStatusBlock | PIO_STATUS_BLOCK | out | Recibe el resultado de la operación de cancelación en sí (Status, Information). No el estado de ningún IRP cancelado. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x5D | win10-1507 |
| Win10 1607 | 0x5D | win10-1607 |
| Win10 1703 | 0x5D | win10-1703 |
| Win10 1709 | 0x5D | win10-1709 |
| Win10 1803 | 0x5D | win10-1803 |
| Win10 1809 | 0x5D | win10-1809 |
| Win10 1903 | 0x5D | win10-1903 |
| Win10 1909 | 0x5D | win10-1909 |
| Win10 2004 | 0x5D | win10-2004 |
| Win10 20H2 | 0x5D | win10-20h2 |
| Win10 21H1 | 0x5D | win10-21h1 |
| Win10 21H2 | 0x5D | win10-21h2 |
| Win10 22H2 | 0x5D | win10-22h2 |
| Win11 21H2 | 0x5D | win11-21h2 |
| Win11 22H2 | 0x5D | win11-22h2 |
| Win11 23H2 | 0x5D | win11-23h2 |
| Win11 24H2 | 0x5D | win11-24h2 |
| Server 2016 | 0x5D | winserver-2016 |
| Server 2019 | 0x5D | winserver-2019 |
| Server 2022 | 0x5D | winserver-2022 |
| Server 2025 | 0x5D | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 5D 00 00 00 mov eax, 0x5D 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
SSN `0x5D` en todos los builds de Windows 10 / 11 / Server — estabilidad excepcional para un syscall de E/S de archivos. La advertencia crítica de alcance: NtCancelIoFile cancela sólo la E/S emitida por *el hilo llamador* sobre el handle dado. *No* afecta E/S emitida por otros hilos sobre el mismo handle (para eso se introdujo NtCancelIoFileEx en Vista). El `CancelIo` Win32 envuelve directamente esta llamada. El kernel recorre la lista de IRP adjunta al TCB del hilo y al FsContext del archivo, marca cada `IRP->Cancel = TRUE` y llama a la rutina de cancel del driver, que se espera complete los IRP con `STATUS_CANCELLED` (0xC0000120).
Uso común por malware
Usado en patrones defensivos de **evasión de hook de EDR**: muchos EDR user-mode hookean `ReadFile` / `WriteFile` / `NtReadFile` para inspeccionar contenido; si la rutina de inspección es asíncrona (encolando un callback en la finalización de E/S), un atacante puede race-cancelar el IRP antes de que dispare la rutina de finalización, impidiendo que el EDR observe nunca los bytes. Más común en uso ofensivo es código de fiabilidad en implants que hablan por named pipes — si el peer muere, el implant dispara `NtCancelIoFile` para flushear lecturas overlapped bloqueadas y recuperar el worker thread en lugar de quedar colgado hasta el timeout TCP / pipe del kernel. Algún tooling red-team también lo llama como parte de shutdown elegante para evitar dejar IRP huérfanas que aparezcan en salida `!irp` del debugger durante revisión forense.
Oportunidades de detección
NtCancelIoFile es extremadamente común en software legítimo intensivo en E/S (navegadores cancelando lecturas HTTPS en vuelo en navegación, IDE cancelando file watchers, OneDrive cancelando sync al offline-toggle) y por tanto es una mala señal primaria. La telemetría interesante es el *triplete*: una lectura async pendiente sobre un handle sensible (named-pipe \\.\pipe\protected_storage_pipe, handle IPC inyectado por EDR, socket HTTPS portando C2) cancelada en microsegundos tras emitirse por el mismo hilo que la emitió. ETW `Microsoft-Windows-Kernel-File` más correlación de IRP de finalización vía WPP pueden mostrar la ruta del IRP cancelado; combinar con PsSetCreateProcessNotifyRoutineEx para atribuir hilo-a-proceso. La mayoría de EDR no exponen un evento cancel de primera clase.
Ejemplos de syscalls directos
asmx64 direct stub
; Direct syscall stub for NtCancelIoFile (SSN 0x5D, all builds)
NtCancelIoFile PROC
mov r10, rcx ; syscall convention
mov eax, 5Dh ; SSN
syscall
ret
NtCancelIoFile ENDPcImplant pipe shutdown
// Implant teardown: when the C2 peer dies, free our blocked overlapped
// read on the named pipe so the worker thread can exit instead of hanging.
#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 *pNtCancelIoFile)(HANDLE, PIO_STATUS_BLOCK);
void ShutdownPipe(HANDLE h_pipe) {
pNtCancelIoFile NtCancelIoFile = (pNtCancelIoFile)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtCancelIoFile");
IO_STATUS_BLOCK iosb = {0};
NtCancelIoFile(h_pipe, &iosb); // only kills this thread's pending IRPs
CloseHandle(h_pipe);
}rustrace-cancel a hooked async read
// PoC: dispatch an async read, then cancel before the EDR's completion
// inspection runs. Same thread issues and cancels.
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::Storage::FileSystem::{ReadFile, OVERLAPPED};
use windows_sys::Win32::System::IO::CancelIo;
pub unsafe fn race_cancel(h: HANDLE, buf: &mut [u8], ov: *mut OVERLAPPED) {
let mut dummy = 0u32;
let _ = ReadFile(h, buf.as_mut_ptr() as _, buf.len() as u32, &mut dummy, ov);
// Immediately yank the IRP before any completion routine fires.
let _ = CancelIo(h); // CancelIo == NtCancelIoFile under the hood
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20