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
| Name | Type | Dir | Description |
|---|---|---|---|
| IoCompletionHandle | PHANDLE | out | Receives the new completion port handle on success. |
| DesiredAccess | ACCESS_MASK | in | Requested access — IO_COMPLETION_ALL_ACCESS (0xF0003) gives query+modify+synchronize. |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | Optional OBJECT_ATTRIBUTES — usually NULL; named completion ports live under \BaseNamedObjects. |
| Count | ULONG | in | Concurrency hint: maximum threads kernel will allow to dequeue simultaneously. 0 means NumberOfProcessors. |
Syscall IDs by Windows version
| Windows version | Syscall ID | Build |
|---|---|---|
| Win10 1507 | 0xA0 | win10-1507 |
| Win10 1607 | 0xA2 | win10-1607 |
| Win10 1703 | 0xA5 | win10-1703 |
| Win10 1709 | 0xA6 | win10-1709 |
| Win10 1803 | 0xA7 | win10-1803 |
| Win10 1809 | 0xA7 | win10-1809 |
| Win10 1903 | 0xA8 | win10-1903 |
| Win10 1909 | 0xA8 | win10-1909 |
| Win10 2004 | 0xAC | win10-2004 |
| Win10 20H2 | 0xAC | win10-20h2 |
| Win10 21H1 | 0xAC | win10-21h1 |
| Win10 21H2 | 0xAD | win10-21h2 |
| Win10 22H2 | 0xAD | win10-22h2 |
| Win11 21H2 | 0xAF | win11-21h2 |
| Win11 22H2 | 0xB0 | win11-22h2 |
| Win11 23H2 | 0xB0 | win11-23h2 |
| Win11 24H2 | 0xB2 | win11-24h2 |
| Server 2016 | 0xA2 | winserver-2016 |
| Server 2019 | 0xA7 | winserver-2019 |
| Server 2022 | 0xAF | winserver-2022 |
| Server 2025 | 0xB2 | winserver-2025 |
Kernel module
Related APIs
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 ENDPMITRE ATT&CK mappings
Last verified: 2026-05-20