NtQueryInformationWorkerFactory
Queries configuration and runtime state of a worker factory, including the current StartRoutine and worker counts.
Prototype
NTSTATUS NtQueryInformationWorkerFactory( HANDLE WorkerFactoryHandle, WORKERFACTORYINFOCLASS WorkerFactoryInformationClass, PVOID WorkerFactoryInformation, ULONG WorkerFactoryInformationLength, PULONG ReturnLength );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| WorkerFactoryHandle | HANDLE | in | Handle to the worker factory. Requires WORKER_FACTORY_QUERY_INFORMATION access. |
| WorkerFactoryInformationClass | WORKERFACTORYINFOCLASS | in | Class selector — most commonly WorkerFactoryBasicInformation (0). |
| WorkerFactoryInformation | PVOID | out | Caller-supplied buffer that receives the queried structure. |
| WorkerFactoryInformationLength | ULONG | in | Size in bytes of the supplied buffer. |
| ReturnLength | PULONG | out | Optional pointer that receives the number of bytes written. May be NULL. |
Syscall IDs by Windows version
| Windows version | Syscall ID | Build |
|---|---|---|
| Win10 1507 | 0x136 | win10-1507 |
| Win10 1607 | 0x13C | win10-1607 |
| Win10 1703 | 0x142 | win10-1703 |
| Win10 1709 | 0x145 | win10-1709 |
| Win10 1803 | 0x147 | win10-1803 |
| Win10 1809 | 0x148 | win10-1809 |
| Win10 1903 | 0x149 | win10-1903 |
| Win10 1909 | 0x149 | win10-1909 |
| Win10 2004 | 0x14F | win10-2004 |
| Win10 20H2 | 0x14F | win10-20h2 |
| Win10 21H1 | 0x14F | win10-21h1 |
| Win10 21H2 | 0x150 | win10-21h2 |
| Win10 22H2 | 0x150 | win10-22h2 |
| Win11 21H2 | 0x156 | win11-21h2 |
| Win11 22H2 | 0x159 | win11-22h2 |
| Win11 23H2 | 0x159 | win11-23h2 |
| Win11 24H2 | 0x15B | win11-24h2 |
| Server 2016 | 0x13C | winserver-2016 |
| Server 2019 | 0x148 | winserver-2019 |
| Server 2022 | 0x155 | winserver-2022 |
| Server 2025 | 0x15B | winserver-2025 |
Kernel module
Related APIs
Syscall stub
4C 8B D1 mov r10, rcx B8 5B 01 00 00 mov eax, 0x15B 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
The symmetric query side of NtSetInformationWorkerFactory. `WorkerFactoryBasicInformation` returns a `WORKER_FACTORY_BASIC_INFORMATION` (see ntexapi.h / phnt) including the current `StartRoutine`, `StartParameter`, `ProcessId`, thread counts, timeouts, and the binding count. The structure is one of the only documented ways to programmatically observe what a threadpool worker factory is currently configured to execute — useful both for diagnostics (taskmgr, PerfView) and for offensive recon of an already-targeted process.
Common malware usage
Used by PoolParty StartRoutine-overwrite variants as the read step before writing back a mutated structure via NtSetInformationWorkerFactory. Also used by toolkits that enumerate worker factories in a victim — once a handle is duplicated in (via NtDuplicateObject), querying tells the attacker which threadpool is the busiest, which target process it lives in, and what StartRoutine it currently dispatches. EDR identification is also possible in reverse: an attacker can query their *own* process's worker factories to see if a third-party DLL has hooked or replaced the StartRoutine.
Detection opportunities
Query operations are read-only and ubiquitous in normal threadpool maintenance — this syscall alone is not a useful signal. It becomes meaningful when paired with a subsequent NtSetInformationWorkerFactory on the same handle (the classic read-modify-write pattern), or when issued cross-process. Kernel callbacks on the WorkerFactory object type can correlate the query/set pair; user-mode telemetry is essentially unobtainable because EDR hooks on the query side are routinely bypassed by direct syscalls.
Direct syscall examples
cSnapshot a worker factory
WORKER_FACTORY_BASIC_INFORMATION info;
ULONG ret = 0;
NTSTATUS s = NtQueryInformationWorkerFactory(
hWf, WorkerFactoryBasicInformation,
&info, sizeof(info), &ret);
if (NT_SUCCESS(s)) {
printf("StartRoutine = %p in PID %llu, %u workers (%u waiting)\n",
info.StartRoutine, (uint64_t)info.ProcessId,
info.TotalWorkerCount, info.WaitingWorkerCount);
}rustRecon over a duplicated remote handle
// hWfRemote was opened in our process via NtDuplicateObject(target, source, ours, ...)
// and now points to the *victim's* worker factory.
use ntapi::ntexapi::{NtQueryInformationWorkerFactory, WORKER_FACTORY_BASIC_INFORMATION};
let mut info: WORKER_FACTORY_BASIC_INFORMATION = unsafe { core::mem::zeroed() };
let mut ret: u32 = 0;
let status = unsafe {
NtQueryInformationWorkerFactory(
h_wf_remote,
0, // WorkerFactoryBasicInformation
&mut info as *mut _ as *mut _,
core::mem::size_of_val(&info) as u32,
&mut ret,
)
};
assert_eq!(status, 0);
println!("victim StartRoutine: {:p}", info.StartRoutine);MITRE ATT&CK mappings
Last verified: 2026-05-20