> Windows Syscalls
ntoskrnl.exeT1055T1106

NtCreateIoCompletion

Creates an I/O completion port — the kernel queue that backs threadpool work delivery, async I/O notification, and (in PoolParty) injected work items.

Prototype

NTSTATUS NtCreateIoCompletion(
  PHANDLE             IoCompletionHandle,
  ACCESS_MASK         DesiredAccess,
  POBJECT_ATTRIBUTES  ObjectAttributes,
  ULONG               Count
);

Arguments

NameTypeDirDescription
IoCompletionHandlePHANDLEoutReceives the new completion port handle on success.
DesiredAccessACCESS_MASKinRequested access — IO_COMPLETION_ALL_ACCESS (0xF0003) gives query+modify+synchronize.
ObjectAttributesPOBJECT_ATTRIBUTESinOptional OBJECT_ATTRIBUTES — usually NULL; named completion ports live under \BaseNamedObjects.
CountULONGinConcurrency hint: maximum threads kernel will allow to dequeue simultaneously. 0 means NumberOfProcessors.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070xA0win10-1507
Win10 16070xA2win10-1607
Win10 17030xA5win10-1703
Win10 17090xA6win10-1709
Win10 18030xA7win10-1803
Win10 18090xA7win10-1809
Win10 19030xA8win10-1903
Win10 19090xA8win10-1909
Win10 20040xACwin10-2004
Win10 20H20xACwin10-20h2
Win10 21H10xACwin10-21h1
Win10 21H20xADwin10-21h2
Win10 22H20xADwin10-22h2
Win11 21H20xAFwin11-21h2
Win11 22H20xB0win11-22h2
Win11 23H20xB0win11-23h2
Win11 24H20xB2win11-24h2
Server 20160xA2winserver-2016
Server 20190xA7winserver-2019
Server 20220xAFwinserver-2022
Server 20250xB2winserver-2025

Kernel module

ntoskrnl.exeNtCreateIoCompletion

Related APIs

CreateIoCompletionPortGetQueuedCompletionStatusPostQueuedCompletionStatusNtSetIoCompletionNtRemoveIoCompletionNtRemoveIoCompletionExNtCreateWorkerFactory

Syscall stub

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

Undocumented notes

I/O completion ports are the high-performance asynchronous notification primitive that underpins almost every Windows server: IIS, SQL Server, .NET ThreadPool, Win32 threadpool, RPC, WinHTTP, and most third-party network stacks. `CreateIoCompletionPort` in kernel32 is a thin wrapper that calls NtCreateIoCompletion the first time it sees an unrecognised handle. The kernel object stores a doubly-linked queue of KQUEUE entries, a concurrency limit, and a list of threads currently associated via NtAssociateWaitCompletionPacket or stacked in NtRemoveIoCompletion. The `Count` parameter is a soft cap, not a hard limit — extra threads can dequeue if existing ones block.

Common malware usage

Mostly benign on its own — every modern application creates these. The PoolParty relevance is structural: completion ports are the queue half of the worker factory primitive. An attacker creates one (in their own process or by duplicating into a victim), wires it into a worker factory via NtCreateWorkerFactory, and then uses NtSetIoCompletion to deliver forged work items. The completion port is also the queue NtWaitForWorkViaWorkerFactory drains. Some sleep-obfuscation gadgets (e.g. Ekko, foliage-style techniques) abuse completion-port waits to mimic the I/O profile of a benign idle server.

Detection opportunities

Creation is too common to alert on. The interesting signal is *handle topology*: a completion port created in process A, duplicated into process B via NtDuplicateObject, and then immediately bound to a worker factory in process B is a textbook PoolParty pattern. Tracking handle duplications of `IoCompletion` objects across process boundaries via ObRegisterCallbacks (or a kernel callback hooking PsSetCreateThreadNotifyRoutine to inspect the new thread's bound queue) gives high-fidelity coverage. ETW Microsoft-Windows-Kernel-IoCompletion provides limited visibility.

Direct syscall examples

cPlain port for a threadpool

HANDLE hIoCompletion = NULL;
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL);
NTSTATUS s = NtCreateIoCompletion(
    &hIoCompletion,
    IO_COMPLETION_ALL_ACCESS,
    &oa,
    0);                                  // 0 = NumberOfProcessors concurrency
if (!NT_SUCCESS(s)) return s;

cPoolParty plumbing — port + remote duplicate

// Create locally, then duplicate INTO the victim — the victim's worker factory
// will be bound to it in the next step.
HANDLE hLocal = NULL, hRemote = NULL;
OBJECT_ATTRIBUTES oa; InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL);
NtCreateIoCompletion(&hLocal, IO_COMPLETION_ALL_ACCESS, &oa, 1);
NtDuplicateObject(NtCurrentProcess(), hLocal,
                  hVictim, &hRemote,
                  0, 0, DUPLICATE_SAME_ACCESS);

asmx64 direct stub (Win11 24H2 SSN 0xB2)

NtCreateIoCompletion PROC
    mov  r10, rcx
    mov  eax, 0B2h
    syscall
    ret
NtCreateIoCompletion ENDP

MITRE ATT&CK mappings

Last verified: 2026-05-20