> Windows Syscalls
ntoskrnl.exeT1497.001T1497T1106

NtQueryInformationJobObject

Liefert Accounting-, Limit- oder UI-Restriktions-Informationen eines Job-Objekts.

Prototyp

NTSTATUS NtQueryInformationJobObject(
  HANDLE             JobHandle,
  JOBOBJECTINFOCLASS JobObjectInformationClass,
  PVOID              JobObjectInformation,
  ULONG              JobObjectInformationLength,
  PULONG             ReturnLength
);

Argumente

NameTypeDirDescription
JobHandleHANDLEinHandle auf den Job mit JOB_OBJECT_QUERY-Rechten. NULL verweist auf den Job, der den Aufruferprozess enthält.
JobObjectInformationClassJOBOBJECTINFOCLASSinInfo-Klassen-Enum. Übliche Werte: JobObjectBasicAccountingInformation = 1, JobObjectSecurityLimitInformation = 5, JobObjectExtendedLimitInformation = 9, JobObjectBasicProcessIdList = 3.
JobObjectInformationPVOIDoutVom Aufrufer bereitgestellter Puffer für die angeforderte Struktur (JOBOBJECT_BASIC_ACCOUNTING_INFORMATION, JOBOBJECT_EXTENDED_LIMIT_INFORMATION, ...).
JobObjectInformationLengthULONGinGröße des Puffers JobObjectInformation in Bytes.
ReturnLengthPULONGoutOptional; empfängt die tatsächlich benötigte Größe (nützlich bei variabel großen Klassen wie JobObjectBasicProcessIdList).

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x131win10-1507
Win10 16070x137win10-1607
Win10 17030x13Dwin10-1703
Win10 17090x140win10-1709
Win10 18030x142win10-1803
Win10 18090x143win10-1809
Win10 19030x144win10-1903
Win10 19090x144win10-1909
Win10 20040x14Awin10-2004
Win10 20H20x14Awin10-20h2
Win10 21H10x14Awin10-21h1
Win10 21H20x14Bwin10-21h2
Win10 22H20x14Bwin10-22h2
Win11 21H20x151win11-21h2
Win11 22H20x154win11-22h2
Win11 23H20x154win11-23h2
Win11 24H20x156win11-24h2
Server 20160x137winserver-2016
Server 20190x143winserver-2019
Server 20220x150winserver-2022
Server 20250x156winserver-2025

Kernel-Modul

ntoskrnl.exeNtQueryInformationJobObject

Verwandte APIs

QueryInformationJobObjectNtSetInformationJobObjectNtIsProcessInJobNtAssignProcessToJobObjectNtCreateJobObject

Syscall-Stub

4C 8B D1            mov r10, rcx
B8 56 01 00 00      mov eax, 0x156
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

Das Gegenstück zu NtSetInformationJobObject. Die SSN driftet stärker als bei NtIsProcessInJob — `0x144` auf Win10 1903 / 1909, `0x14A` auf 2004–21H1, `0x156` auf Win11 24H2 / Server 2025 — Hartkodieren über mehrere Builds ist also fragil und dynamische Auflösung im Hell's-Gate-Stil bevorzugt. Die malware-relevantesten Info-Klassen sind: `JobObjectBasicAccountingInformation` (=1) für gesamte User-/Kernel-CPU-Zeit und Prozesszählung, `JobObjectBasicProcessIdList` (=3) zur Enumeration jeder PID im Job, `JobObjectExtendedLimitInformation` (=9) für Speicher- und Aktive-Prozesse-Limits, und `JobObjectSecurityLimitInformation` (=5) für (alte, veraltete) SID-/Token-Beschränkungen.

Häufige Malware-Nutzung

Wird von sandbox-bewussten Loadern verwendet, die `NtIsProcessInJob` mit einem nachfolgenden `NtQueryInformationJobObject(JobObjectExtendedLimitInformation)` kombinieren, um die *Art* des Jobs zu fingerprinten. Verräterische Cuckoo- / Joe-Sandbox- / ANY.RUN-Signaturen sind: sehr niedriges `ActiveProcessLimit` (1–3), ein `PerJobUserTimeLimit`, das auf das Analyse-Budget gesetzt ist (typisch 60–300 Sekunden), und ein JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE-Flag — keines davon passt zum Chrome-Renderer-Job (hohes Prozesslimit, kein CPU-Zeitcap) oder zu WSL2s Job. Im Gegensatz dazu ist die Job-Prozess-Liste (`JobObjectBasicProcessIdList`) eine beliebte *Enumerations*-Primitive — sobald ein Foothold-Prozess in einem Sandbox-/Container-Job läuft, offenbart das Durchlaufen der Geschwister den Monitor, den Agent und andere zu unterdrückende Ziele.

Erkennungs­möglichkeiten

Wie NtIsProcessInJob ist dieser Aufruf alltäglich — jede Windows-Shell, jeder Browser, jede Container-Runtime liest ständig Job-Informationen. Das hochsignalbringende Muster ist die *Info-Klasse*: ein frisch abgelegter, unsignierter Prozess, der innerhalb von Sekunden nach dem Start `JobObjectExtendedLimitInformation` (=9) zu seinem enthaltenden Job abfragt und dann auf `ActiveProcessLimit` oder `PerJobUserTimeLimit` verzweigt, ist ein nahezu pathognomonischer Cuckoo-/Joe-Evasion-Fingerabdruck. ETW `Microsoft-Windows-Kernel-Process` plus User-Mode-Hooks auf `QueryInformationJobObject` in kernelbase.dll fangen den Standardweg ab; direkte Syscalls umgehen User-Mode-Hooks. Reife Sandboxes kontern, indem sie das Sample außerhalb ihres Monitor-Jobs starten und es post factum über PsSetCreateProcessNotifyRoutineEx erneut einfangen — so bleibt keine Job-Mitgliedschafts-Spur, die die Malware abfragen könnte.

Direkte Syscall-Beispiele

asmx64 stub (Win11 24H2 SSN 0x156)

; Direct syscall stub for NtQueryInformationJobObject
NtQueryInformationJobObject PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 156h         ; SSN (Win11 24H2 / Server 2025)
    syscall
    ret
NtQueryInformationJobObject ENDP

cCuckoo job fingerprint

// Fingerprint the containing job: tiny ActiveProcessLimit + small wall-clock
// budget == probably a sandbox.
#include <windows.h>
#include <winnt.h>

typedef NTSTATUS (NTAPI *pNtQueryInformationJobObject)(
    HANDLE, ULONG, PVOID, ULONG, PULONG);

BOOL LooksLikeCuckooJob(void) {
    pNtQueryInformationJobObject NtQueryInformationJobObject =
        (pNtQueryInformationJobObject)GetProcAddress(
            GetModuleHandleA("ntdll.dll"), "NtQueryInformationJobObject");
    if (!NtQueryInformationJobObject) return FALSE;

    JOBOBJECT_EXTENDED_LIMIT_INFORMATION ext = {0};
    NTSTATUS s = NtQueryInformationJobObject(
        NULL, /* JobObjectExtendedLimitInformation */ 9,
        &ext, sizeof ext, NULL);
    if (s != 0) return FALSE;

    BOOL tiny_active = (ext.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_ACTIVE_PROCESS)
                       && ext.BasicLimitInformation.ActiveProcessLimit > 0
                       && ext.BasicLimitInformation.ActiveProcessLimit < 5;
    BOOL short_budget = (ext.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_TIME)
                        && ext.BasicLimitInformation.PerJobUserTimeLimit.QuadPart
                            < 600LL * 10000000LL;  // < 10 min
    return tiny_active || short_budget;
}

rustenumerate sibling PIDs in the same job

// Walk JobObjectBasicProcessIdList (=3) — useful for sandbox-pivot or
// container-aware enumeration.
use std::ptr;

#[repr(C)]
struct JobBasicProcessIdList {
    number_of_assigned: u32,
    number_of_id_in_list: u32,
    process_id_list: [usize; 1],  // VLA
}

extern "system" {
    fn NtQueryInformationJobObject(
        h: *mut core::ffi::c_void, class: u32,
        info: *mut core::ffi::c_void, len: u32, ret_len: *mut u32) -> i32;
}

pub fn pids_in_my_job() -> Option<Vec<usize>> {
    let mut buf = vec![0u8; 4096];
    let mut needed = 0u32;
    let s = unsafe { NtQueryInformationJobObject(
        ptr::null_mut(), 3, buf.as_mut_ptr() as _, buf.len() as u32, &mut needed) };
    if s != 0 { return None; }
    let hdr = unsafe { &*(buf.as_ptr() as *const JobBasicProcessIdList) };
    let n = hdr.number_of_id_in_list as usize;
    let base = unsafe { (buf.as_ptr() as *const u8).add(8) as *const usize };
    Some((0..n).map(|i| unsafe { *base.add(i) }).collect())
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20