> Windows Syscalls
ntoskrnl.exeT1055T1027.011T1106

NtWaitForWorkViaWorkerFactory

Bloquea un worker de threadpool hasta que haya un item disponible en la cola de finalización de la factory — el bucle caliente secuestrado por PoolParty.

Prototipo

NTSTATUS NtWaitForWorkViaWorkerFactory(
  HANDLE                         WorkerFactoryHandle,
  PFILE_IO_COMPLETION_INFORMATION MiniPacket
);

Argumentos

NameTypeDirDescription
WorkerFactoryHandleHANDLEinHandle a la worker factory a la que está vinculado el hilo llamador.
MiniPacketPFILE_IO_COMPLETION_INFORMATIONoutRecibe el paquete de finalización desencolado — KeyContext, ApcContext, IoStatusBlock.Status, IoStatusBlock.Information.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070x1B6win10-1507
Win10 16070x1BFwin10-1607
Win10 17030x1C5win10-1703
Win10 17090x1C9win10-1709
Win10 18030x1CBwin10-1803
Win10 18090x1CCwin10-1809
Win10 19030x1CDwin10-1903
Win10 19090x1CDwin10-1909
Win10 20040x1D3win10-2004
Win10 20H20x1D3win10-20h2
Win10 21H10x1D3win10-21h1
Win10 21H20x1D5win10-21h2
Win10 22H20x1D5win10-22h2
Win11 21H20x1DFwin11-21h2
Win11 22H20x1E3win11-22h2
Win11 23H20x1E3win11-23h2
Win11 24H20x1E6win11-24h2
Server 20160x1BFwinserver-2016
Server 20190x1CCwinserver-2019
Server 20220x1DBwinserver-2022
Server 20250x1E6winserver-2025

Módulo del kernel

ntoskrnl.exeNtWaitForWorkViaWorkerFactory

APIs relacionadas

GetQueuedCompletionStatusNtRemoveIoCompletionNtRemoveIoCompletionExNtWorkerFactoryWorkerReadyNtReleaseWorkerFactoryWorker

Stub del syscall

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

Notas no documentadas

Internamente esto es un dequeue fusionado del IoCompletion subyacente más una actualización del `WaitingWorkerCount` de la factory. El paquete devuelto sigue la disposición de cuatro campos de `FILE_IO_COMPLETION_INFORMATION` que usa NtRemoveIoCompletion, pero la espera está *ligada* a la factory — solo los hilos cuya worker factory coincide pueden consumir de su cola, y el kernel usa esta atadura para hacer cumplir límites de hilos y timeouts de inactividad. Un retorno `STATUS_TIMEOUT` hace que el threadpool considere recoger al worker; un retorno exitoso lo lanza a su bucle de despacho.

Uso común por malware

Toda la propuesta de valor de PoolParty es que al kernel *no* le importa qué código corra en un hilo que invoca este syscall — una vez que un worker queda bloqueado aquí, cualquier cosa escrita en el puerto de finalización aguas arriba (o lo que apunten `KeyContext`/`ApcContext` si el dispatcher en modo usuario confía en ellos) se convierte en flujo de ejecución controlado por el usuario. La mayoría de variantes de PoolParty no emiten NtWaitForWorkViaWorkerFactory ellas mismas: dejan que el legítimo ntdll!TppWorkerThread siga llamándolo, y luego envenenan la StartRoutine de la factory o los items que desencola. La variante 'worker via completion port' forja explícitamente paquetes que, al ser consumidos por este syscall y despachados por TppWorkerThread, terminan invocando código controlado por el atacante a través de la indirección de callback del threadpool.

Oportunidades de detección

Como NtWorkerFactoryWorkerReady, imposible de marcar de forma aislada — todo proceso Windows moderno tiene en todo momento decenas de hilos durmiendo aquí. La detección debe centrarse en el *contenido* de los paquetes que devuelve. Los proveedores ETW IoCompletion pueden muestrear el tráfico del puerto de finalización; los desajustes entre los contextos de callback esperados (que el threadpool sigue internamente en estructuras `TP_DIRECT` / `TP_CALLBACK_INSTANCE`) y lo que realmente se entregó son la señal de mayor fidelidad. El escaneo de memoria de las pilas de hilos worker en busca de direcciones de retorno fuera de los rangos conocidos de ntdll/kernel32 es un control conductual razonable tras observar una pareja Create/Release inter-proceso sospechosa.

Ejemplos de syscalls directos

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 ENDP

rustObserve 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
        );
    }
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20