NtCreateIoCompletion
Erzeugt einen I/O-Completion-Port — die Kernel-Queue, die Threadpool-Arbeitsauslieferung, asynchrone I/O-Benachrichtigung und (bei PoolParty) injizierte Work-Items trägt.
Prototyp
NTSTATUS NtCreateIoCompletion( PHANDLE IoCompletionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG Count );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| IoCompletionHandle | PHANDLE | out | Erhält das neue Completion-Port-Handle bei Erfolg. |
| DesiredAccess | ACCESS_MASK | in | Angeforderter Zugriff — IO_COMPLETION_ALL_ACCESS (0xF0003) gibt query+modify+synchronize. |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | Optionale OBJECT_ATTRIBUTES — üblicherweise NULL; benannte Ports liegen unter \BaseNamedObjects. |
| Count | ULONG | in | Concurrency-Hinweis: maximale Anzahl Threads, die der Kernel gleichzeitig dequeuen lässt. 0 bedeutet NumberOfProcessors. |
Syscall-IDs pro 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-Modul
Verwandte 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
Undokumentierte Hinweise
I/O-Completion-Ports sind die hochperformante asynchrone Benachrichtigungs-Primitive, die fast jeden Windows-Server trägt: IIS, SQL Server, .NET-ThreadPool, Win32-Threadpool, RPC, WinHTTP und die meisten Netzwerk-Stacks Dritter. `CreateIoCompletionPort` in kernel32 ist ein dünner Wrapper, der NtCreateIoCompletion beim ersten Sichten eines unbekannten Handles aufruft. Das Kernel-Objekt speichert eine doppelt verkettete Queue von KQUEUE-Einträgen, ein Concurrency-Limit und eine Liste aktuell zugeordneter Threads (via NtAssociateWaitCompletionPacket oder in NtRemoveIoCompletion gestapelt). Der `Count`-Parameter ist eine weiche Obergrenze, kein hartes Limit — zusätzliche Threads dürfen dequeuen, wenn bestehende blockieren.
Häufige Malware-Nutzung
Für sich genommen meist harmlos — jede moderne Anwendung erzeugt diese. Die PoolParty-Relevanz ist strukturell: Completion-Ports sind die Queue-Hälfte der Worker-Factory-Primitive. Ein Angreifer erzeugt einen (im eigenen Prozess oder per Duplikation in ein Opfer), verdrahtet ihn via NtCreateWorkerFactory mit einer Worker Factory und liefert dann via NtSetIoCompletion gefälschte Work-Items. Der Port ist auch die Queue, die NtWaitForWorkViaWorkerFactory leert. Manche Sleep-Obfuskations-Gadgets (Ekko, foliage-artige Techniken) missbrauchen Completion-Port-Waits, um das I/O-Profil eines harmlosen Idle-Servers zu imitieren.
Erkennungsmöglichkeiten
Erzeugung ist zu häufig, um zu alarmieren. Das interessante Signal ist die *Handle-Topologie*: ein in Prozess A erzeugter Completion-Port, via NtDuplicateObject in Prozess B dupliziert und dort sofort an eine Worker Factory gebunden, ist ein Lehrbuch-PoolParty-Muster. Das Verfolgen von Handle-Duplikationen von `IoCompletion`-Objekten über Prozessgrenzen via ObRegisterCallbacks (oder ein Kernel-Callback, der PsSetCreateThreadNotifyRoutine hookt, um die gebundene Queue des neuen Threads zu inspizieren) liefert hochwertige Abdeckung. ETW Microsoft-Windows-Kernel-IoCompletion bietet begrenzte Sichtbarkeit.
Direkte Syscall-Beispiele
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