> Windows Syscalls
ntoskrnl.exeT1106T1027.011

NtRemoveIoCompletion

Desencola un único paquete de finalización de un puerto de finalización de E/S, bloqueando hasta que haya uno disponible o expire el timeout.

Prototipo

NTSTATUS NtRemoveIoCompletion(
  HANDLE          IoCompletionHandle,
  PVOID          *KeyContext,
  PVOID          *ApcContext,
  PIO_STATUS_BLOCK IoStatusBlock,
  PLARGE_INTEGER  Timeout
);

Argumentos

NameTypeDirDescription
IoCompletionHandleHANDLEinHandle al puerto de finalización. Requiere IO_COMPLETION_MODIFY_STATE.
KeyContextPVOID *outRecibe el CompletionKey suministrado por el productor a NtSetIoCompletion.
ApcContextPVOID *outRecibe el ApcContext del productor — típicamente el OVERLAPPED* atado a la E/S original.
IoStatusBlockPIO_STATUS_BLOCKoutRecibe los campos Status e Information del paquete desencolado.
TimeoutPLARGE_INTEGERinTimeout relativo (negativo) o absoluto (positivo) en unidades de 100ns. NULL espera indefinidamente.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070x9win10-1507
Win10 16070x9win10-1607
Win10 17030x9win10-1703
Win10 17090x9win10-1709
Win10 18030x9win10-1803
Win10 18090x9win10-1809
Win10 19030x9win10-1903
Win10 19090x9win10-1909
Win10 20040x9win10-2004
Win10 20H20x9win10-20h2
Win10 21H10x9win10-21h1
Win10 21H20x9win10-21h2
Win10 22H20x9win10-22h2
Win11 21H20x9win11-21h2
Win11 22H20x9win11-22h2
Win11 23H20x9win11-23h2
Win11 24H20x9win11-24h2
Server 20160x9winserver-2016
Server 20190x9winserver-2019
Server 20220x9winserver-2022
Server 20250x9winserver-2025

Módulo del kernel

ntoskrnl.exeNtRemoveIoCompletion

APIs relacionadas

GetQueuedCompletionStatusGetQueuedCompletionStatusExNtRemoveIoCompletionExNtCreateIoCompletionNtSetIoCompletionNtWaitForWorkViaWorkerFactory

Stub del syscall

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

Otro syscall extraordinariamente estable — `0x9` desde Windows 7. NtRemoveIoCompletion es el lado del kernel de `GetQueuedCompletionStatus`. Saca un paquete de la KQUEUE del puerto; si la cola está vacía bloquea el hilo llamador en la lista de espera del puerto hasta que se llame NtSetIoCompletion, una E/S real termine, o expire el timeout. El envoltorio Win32 solo traduce entre el retorno de tres campos del kernel (KeyContext, ApcContext, IoStatusBlock) y la forma de cuatro argumentos de Win32 (numBytes, completionKey, lpOverlapped).

Uso común por malware

Como NtCreateIoCompletion, este syscall es tan omnipresente en código legítimo que no tiene firma de ataque aislada. La explotación PoolParty no lo invoca directamente — las variantes de worker factory usan NtWaitForWorkViaWorkerFactory en su lugar, ligada a la factory. NtRemoveIoCompletion aparece en gadgets de evasión de sleep que imitan el patrón inactivo de un servidor benigno (bucle en NtRemoveIoCompletion con timeout largo, descifrar beacon al despertar, cifrar de nuevo antes de volver a esperar) y en algunos transportes C2 personalizados que se montan sobre sockets overlapped para enterrar su tráfico en el ruido de E/S legítima.

Oportunidades de detección

Efectivamente indetectable de forma aislada. Las analíticas conductuales que correlacionan patrones de espera en puerto de finalización con la *ausencia* de E/S real correspondiente pueden marcar gadgets de sleep — un proceso que mantiene muchos hilos en NtRemoveIoCompletion sobre un puerto al que nadie fuera del proceso escribe es anómalo. El ETW Microsoft-Windows-Kernel-IoCompletion puede muestrear eventos de dequeue a alto coste. El vector de detección más realista sigue siendo aguas arriba: cazar la pareja sospechosa NtCreateIoCompletion/NtCreateWorkerFactory, y luego la actividad de NtRemoveIoCompletion se vuelve evidencia secundaria.

Ejemplos de syscalls directos

cStandard server loop

for (;;) {
    PVOID key = NULL, apc = NULL;
    IO_STATUS_BLOCK iosb;
    LARGE_INTEGER timeout; timeout.QuadPart = -10000000LL * 30; // 30s rel
    NTSTATUS s = NtRemoveIoCompletion(hIocp, &key, &apc, &iosb, &timeout);
    if (s == STATUS_TIMEOUT) continue;
    if (!NT_SUCCESS(s)) break;
    HandlePacket(key, (LPOVERLAPPED)apc, iosb.Status,
                 (DWORD)iosb.Information);
}

asmx64 direct stub (SSN 0x9)

; Stable across every Win10/Win11 build.
NtRemoveIoCompletion PROC
    mov  r10, rcx
    mov  eax, 9
    syscall
    ret
NtRemoveIoCompletion ENDP

rustSleep-evasion shaped wait

// Mimic a benign server's idle pattern. Real implants would also re-encrypt
// their RX region around the wait and timing-correlate with HTTP keep-alives.
use ntapi::ntioapi::{NtRemoveIoCompletion, IO_STATUS_BLOCK};
use winapi::shared::ntdef::LARGE_INTEGER;

unsafe fn napkin_sleep(h_iocp: windows_sys::Win32::Foundation::HANDLE, secs: i64) {
    let mut key: *mut core::ffi::c_void = core::ptr::null_mut();
    let mut apc: *mut core::ffi::c_void = core::ptr::null_mut();
    let mut iosb: IO_STATUS_BLOCK = core::mem::zeroed();
    let mut to: LARGE_INTEGER = core::mem::zeroed();
    *to.QuadPart_mut() = -10_000_000 * secs;
    let _ = NtRemoveIoCompletion(h_iocp, &mut key, &mut apc, &mut iosb, &mut to);
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20