NtWaitForWorkViaWorkerFactory
Blockiert einen Threadpool-Worker, bis ein Work-Item in der Completion-Queue der Factory bereit ist — die Hot-Loop, die PoolParty hijackt.
Prototyp
NTSTATUS NtWaitForWorkViaWorkerFactory( HANDLE WorkerFactoryHandle, PFILE_IO_COMPLETION_INFORMATION MiniPacket );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| WorkerFactoryHandle | HANDLE | in | Handle auf die Worker Factory, an die der aufrufende Thread gebunden ist. |
| MiniPacket | PFILE_IO_COMPLETION_INFORMATION | out | Empfängt das aus der Queue genommene Completion-Paket — KeyContext, ApcContext, IoStatusBlock.Status, IoStatusBlock.Information. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x1B6 | win10-1507 |
| Win10 1607 | 0x1BF | win10-1607 |
| Win10 1703 | 0x1C5 | win10-1703 |
| Win10 1709 | 0x1C9 | win10-1709 |
| Win10 1803 | 0x1CB | win10-1803 |
| Win10 1809 | 0x1CC | win10-1809 |
| Win10 1903 | 0x1CD | win10-1903 |
| Win10 1909 | 0x1CD | win10-1909 |
| Win10 2004 | 0x1D3 | win10-2004 |
| Win10 20H2 | 0x1D3 | win10-20h2 |
| Win10 21H1 | 0x1D3 | win10-21h1 |
| Win10 21H2 | 0x1D5 | win10-21h2 |
| Win10 22H2 | 0x1D5 | win10-22h2 |
| Win11 21H2 | 0x1DF | win11-21h2 |
| Win11 22H2 | 0x1E3 | win11-22h2 |
| Win11 23H2 | 0x1E3 | win11-23h2 |
| Win11 24H2 | 0x1E6 | win11-24h2 |
| Server 2016 | 0x1BF | winserver-2016 |
| Server 2019 | 0x1CC | winserver-2019 |
| Server 2022 | 0x1DB | winserver-2022 |
| Server 2025 | 0x1E6 | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 E6 01 00 00 mov eax, 0x1E6 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
Intern ist dies ein verschmolzenes Dequeue der zugrundeliegenden IoCompletion plus eine Aktualisierung der `WaitingWorkerCount` der Factory. Das zurückgegebene Paket folgt dem Vier-Felder-Layout `FILE_IO_COMPLETION_INFORMATION`, das auch NtRemoveIoCompletion verwendet, aber das Warten ist *an die Factory gebunden* — nur Threads, deren Worker Factory passt, dürfen aus ihrer Queue konsumieren, und der Kernel nutzt diese Bindung, um Thread-Limits und Idle-Timeouts durchzusetzen. Ein `STATUS_TIMEOUT`-Return veranlasst den Threadpool, das Einsammeln des Workers zu erwägen; ein erfolgreicher Return springt den Worker in seine Dispatch-Schleife.
Häufige Malware-Nutzung
Das gesamte Wertversprechen von PoolParty besteht darin, dass es dem Kernel *egal* ist, welcher Code zufällig auf einem Thread läuft, der diesen Syscall aufruft — sobald ein Worker hier blockiert, wird alles, was in den vorgelagerten Completion-Port geschrieben wird (oder worauf `KeyContext`/`ApcContext` zeigen, sofern der User-Mode-Dispatcher ihnen vertraut), zu nutzergesteuertem Kontrollfluss. Die meisten PoolParty-Varianten rufen NtWaitForWorkViaWorkerFactory selbst gar nicht auf: sie lassen den legitimen ntdll!TppWorkerThread weiter aufrufen und vergiften entweder die StartRoutine der Factory oder die Items, die sie dequeue't. Die 'Worker via Completion Port'-Variante fälscht explizit Pakete, die — vom Syscall konsumiert und von TppWorkerThread dispatcht — über die Threadpool-Callback-Indirektion in angreifergesteuertem Code enden.
Erkennungsmöglichkeiten
Wie bei NtWorkerFactoryWorkerReady isoliert unmöglich zu markieren — jeder moderne Windows-Prozess hat ständig Dutzende Threads, die hier schlafen. Detektion muss sich auf den *Inhalt* der zurückgelieferten Pakete konzentrieren. ETW-IoCompletion-Provider können Completion-Port-Verkehr stichprobenartig erfassen; Abweichungen zwischen erwarteten Callback-Kontexten (die der Threadpool intern in `TP_DIRECT` / `TP_CALLBACK_INSTANCE`-Strukturen verfolgt) und tatsächlich geliefertem sind das hochwertigste Signal. Speicherscan der Worker-Thread-Stacks auf Rücksprungadressen außerhalb bekannter ntdll/kernel32-Bereiche ist eine vernünftige Verhaltensprüfung nach der Beobachtung eines verdächtigen cross-process Create/Release-Paares.
Direkte Syscall-Beispiele
cMinimal worker loop
// Stripped-down clone of ntdll!TppWorkerThread for educational use.
VOID WINAPI MyWorker(PVOID context) {
HANDLE hWf = (HANDLE)context;
FILE_IO_COMPLETION_INFORMATION packet;
for (;;) {
NTSTATUS s = NtWaitForWorkViaWorkerFactory(hWf, &packet);
if (s == STATUS_TIMEOUT) break; // pool decided to reap us
if (!NT_SUCCESS(s)) break;
// packet.KeyContext is *not* trusted in legitimate ntdll —
// it dispatches through internal TP_CALLBACK_INSTANCE tables.
Dispatch(&packet);
NtWorkerFactoryWorkerReady(hWf);
}
}asmx64 direct stub (Win11 24H2 SSN 0x1E6)
NtWaitForWorkViaWorkerFactory PROC
mov r10, rcx
mov eax, 1E6h
syscall
ret
NtWaitForWorkViaWorkerFactory ENDPrustObserve a hijacked completion
// Diagnostic helper used in PoolParty research — drains one packet from a
// worker factory and prints the would-be dispatch context.
use ntapi::ntioapi::FILE_IO_COMPLETION_INFORMATION;
use ntapi::ntexapi::NtWaitForWorkViaWorkerFactory;
unsafe fn observe_one(h_wf: windows_sys::Win32::Foundation::HANDLE) {
let mut p: FILE_IO_COMPLETION_INFORMATION = core::mem::zeroed();
let s = NtWaitForWorkViaWorkerFactory(h_wf, &mut p);
if s == 0 {
eprintln!(
"KeyContext={:p} ApcContext={:p} Status={:#x} Info={:#x}",
p.KeyContext, p.ApcContext,
p.IoStatusBlock.u.Status, p.IoStatusBlock.Information
);
}
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20