> Windows Syscalls
ntoskrnl.exeT1106

NtRemoveIoCompletionEx

Retire jusqu'à un nombre spécifié par l'appelant de paquets de complétion d'un port I/O completion en un seul syscall.

Prototype

NTSTATUS NtRemoveIoCompletionEx(
  HANDLE                IoCompletionHandle,
  PFILE_IO_COMPLETION_INFORMATION IoCompletionInformation,
  ULONG                 Count,
  PULONG                NumEntriesRemoved,
  PLARGE_INTEGER        Timeout,
  BOOLEAN               Alertable
);

Arguments

NameTypeDirDescription
IoCompletionHandleHANDLEinHandle vers l'objet IoCompletion ouvert avec IO_COMPLETION_MODIFY_STATE.
IoCompletionInformationPFILE_IO_COMPLETION_INFORMATIONoutTableau alloué par l'appelant de Count entrées qui reçoit des tuples (KeyContext, ApcContext, IoStatusBlock).
CountULONGinNombre maximum de paquets à retirer dans cet appel.
NumEntriesRemovedPULONGoutReçoit le nombre effectif de paquets retournés (1..Count).
TimeoutPLARGE_INTEGERinTimeout optionnel en unités 100ns relatif (négatif) ou absolu (positif). NULL attend indéfiniment.
AlertableBOOLEANinTRUE autorise l'attente à être interrompue par un APC usermode (renvoie STATUS_USER_APC).

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070x156win10-1507
Win10 16070x15Dwin10-1607
Win10 17030x163win10-1703
Win10 17090x166win10-1709
Win10 18030x168win10-1803
Win10 18090x169win10-1809
Win10 19030x16Awin10-1903
Win10 19090x16Awin10-1909
Win10 20040x170win10-2004
Win10 20H20x170win10-20h2
Win10 21H10x170win10-21h1
Win10 21H20x172win10-21h2
Win10 22H20x172win10-22h2
Win11 21H20x17Awin11-21h2
Win11 22H20x17Dwin11-22h2
Win11 23H20x17Dwin11-23h2
Win11 24H20x17Fwin11-24h2
Server 20160x15Dwinserver-2016
Server 20190x169winserver-2019
Server 20220x178winserver-2022
Server 20250x17Fwinserver-2025

Module noyau

ntoskrnl.exeNtRemoveIoCompletionEx

APIs liées

GetQueuedCompletionStatusExGetQueuedCompletionStatusNtRemoveIoCompletionNtSetIoCompletionNtCreateIoCompletionCreateIoCompletionPort

Stub du syscall

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

Notes non documentées

Cousin batché de `NtRemoveIoCompletion`. Là où le syscall legacy renvoie au plus un paquet de complétion par appel, `NtRemoveIoCompletionEx` retourne jusqu'à `Count` paquets en un seul aller-retour — un gain d'efficacité significatif pour les serveurs à haut débit (IIS, SQL Server, le thread I/O completion du ThreadPool .NET, tout thread-pool Win32 avec `WT_EXECUTEINIOTHREAD`). Le wrapper Win32 est `GetQueuedCompletionStatusEx`. Renvoie STATUS_TIMEOUT si Timeout s'écoule sans paquet, STATUS_USER_APC si Alertable et qu'un APC s'est déclenché, STATUS_ABANDONED_WAIT_0 si l'objet IoCompletion est fermé pendant l'attente. Disponible depuis Windows Vista — API totalement stable depuis, seul le SSN a bougé.

Usage courant par les malwares

**Signal malware faible**. Ce syscall est massivement une primitive serveur haute-perf / runtime — le ThreadPool .NET, le worker I/O du thread-pool Win32, et tout produit serveur Windows l'appellent constamment. Les rares mentions de malware dans la littérature concernent du code C2 *côté serveur* à haute concurrence (panneaux et bots qui agissent eux-mêmes comme des serveurs), pas des implants. Un petit nombre d'implants réseau-lourds (certains ponts external-C2 de Cobalt Strike, certains transports custom Sliver bâtis sur IO completion ports) l'utilisent parce qu'ils réutilisent un modèle de threading style serveur — et là, c'est juste identique en télémétrie au trafic WCF / ASP.NET Core légitime. En tant que primitive offensive, il n'ajoute aucune furtivité par-dessus le ReadFile / WSARecv qu'il batche.

Opportunités de détection

Pas de signal de détection utile en pratique — `NtRemoveIoCompletionEx` est l'un des syscalls les plus volumétriques sur tout serveur Windows et un bruit de fond constant sur poste (chaque onglet navigateur, chaque agent de stockage cloud, chaque language server d'IDE). Aucun pivot télémétrique par-appel ne sépare malveillant de bénin de façon significative. Les défenseurs doivent se concentrer sur les *voisins* du port IO completion : quels handles y sont queuées (sockets vers des endpoints suspects, handles de fichiers vers des chemins sensibles), et le pattern de threading du consommateur (un worker qui fait des reads sur completion-port suivis immédiatement d'un NtCreateThreadEx dans un processus distant est le seul red flag faible modélisable). Les EDR ne hookent typiquement pas ce syscall à cause du volume — les variantes en syscall direct ne gagnent rien.

Exemples de syscalls directs

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtRemoveIoCompletionEx (SSN 0x17F on Win11 24H2)
NtRemoveIoCompletionEx PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 17Fh         ; SSN — varies per build
    syscall
    ret
NtRemoveIoCompletionEx ENDP

cBatched dequeue via GetQueuedCompletionStatusEx

// Standard server-thread loop: pull up to 16 packets per syscall.
// This is what nearly every IOCP-based service does.
#include <windows.h>

VOID IocpWorker(HANDLE hPort) {
    OVERLAPPED_ENTRY entries[16];
    ULONG got = 0;
    while (GetQueuedCompletionStatusEx(hPort, entries, 16, &got, INFINITE, FALSE)) {
        for (ULONG i = 0; i < got; ++i) {
            DispatchCompletion(&entries[i]);
        }
    }
}

rustDirect-syscall wrapper

// Cargo: windows-sys = "0.59"
// Minimal wrapper around the Ex syscall — used by a custom server runtime.
use std::ptr::null_mut;

#[repr(C)]
pub struct IoCompletionInfo {
    pub key_context: usize,
    pub apc_context: usize,
    pub io_status:   [usize; 2], // status + information
}

extern "system" {
    fn NtRemoveIoCompletionEx(h: isize, info: *mut IoCompletionInfo,
        count: u32, removed: *mut u32, timeout: *mut i64, alertable: u8) -> i32;
}

pub unsafe fn drain(h: isize, batch: &mut [IoCompletionInfo]) -> u32 {
    let mut n: u32 = 0;
    NtRemoveIoCompletionEx(h, batch.as_mut_ptr(), batch.len() as u32,
                           &mut n, null_mut(), 0);
    n
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20