> Windows Syscalls
ntoskrnl.exeT1497.001T1497T1106

NtQueryInformationJobObject

Récupère les informations de comptabilité, limites ou restrictions UI d'un job object.

Prototype

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

Arguments

NameTypeDirDescription
JobHandleHANDLEinHandle vers le job, avec les droits JOB_OBJECT_QUERY. NULL désigne le job contenant le processus appelant.
JobObjectInformationClassJOBOBJECTINFOCLASSinÉnumération de classe d'info. Valeurs courantes : JobObjectBasicAccountingInformation = 1, JobObjectSecurityLimitInformation = 5, JobObjectExtendedLimitInformation = 9, JobObjectBasicProcessIdList = 3.
JobObjectInformationPVOIDoutTampon fourni par l'appelant recevant la structure demandée (JOBOBJECT_BASIC_ACCOUNTING_INFORMATION, JOBOBJECT_EXTENDED_LIMIT_INFORMATION, ...).
JobObjectInformationLengthULONGinTaille en octets du tampon JobObjectInformation.
ReturnLengthPULONGoutOptionnel ; reçoit la taille réellement requise (utile pour les classes à longueur variable comme JobObjectBasicProcessIdList).

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
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

Module noyau

ntoskrnl.exeNtQueryInformationJobObject

APIs liées

QueryInformationJobObjectNtSetInformationJobObjectNtIsProcessInJobNtAssignProcessToJobObjectNtCreateJobObject

Stub du syscall

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

Notes non documentées

Le pendant de NtSetInformationJobObject. Le SSN dérive plus que NtIsProcessInJob — `0x144` sur Win10 1903 / 1909, `0x14A` sur 2004–21H1, `0x156` sur Win11 24H2 / Server 2025 — donc le coder en dur sur plusieurs builds est fragile et la résolution dynamique style Hell's Gate est préférée. Les classes d'info les plus pertinentes pour le malware sont : `JobObjectBasicAccountingInformation` (=1) pour le temps CPU user/kernel total et le nombre de processus, `JobObjectBasicProcessIdList` (=3) pour énumérer chaque PID du job, `JobObjectExtendedLimitInformation` (=9) pour les limites mémoire et de processus actifs, et `JobObjectSecurityLimitInformation` (=5) pour les restrictions SID/token (héritées, dépréciées).

Usage courant par les malwares

Utilisé par les loaders sandbox-aware qui combinent `NtIsProcessInJob` avec un `NtQueryInformationJobObject(JobObjectExtendedLimitInformation)` consécutif pour empreinter le *type* de job. Les signatures révélatrices de Cuckoo / Joe Sandbox / ANY.RUN incluent : un `ActiveProcessLimit` très bas (1–3), un `PerJobUserTimeLimit` réglé sur le budget d'analyse (typiquement 60–300 secondes), et un flag JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE — aucun ne correspond au job de renderer Chrome (limite de processus élevée, aucun cap de temps CPU) ni au job de WSL2. À l'inverse, la requête de liste de processus du job (`JobObjectBasicProcessIdList`) est une primitive populaire d'*énumération* — une fois qu'un processus pied à terre est dans un job sandbox / conteneur, parcourir les frères révèle le monitor, l'agent et d'autres cibles à neutraliser.

Opportunités de détection

Comme NtIsProcessInJob, cet appel est mainstream — chaque shell Windows, chaque navigateur, chaque runtime de conteneur lit constamment des infos de job. Le motif à fort signal est la *classe d'info* : un processus fraîchement déposé et non signé qui demande `JobObjectExtendedLimitInformation` (=9) sur son job conteneur dans les secondes après le lancement, puis branche sur `ActiveProcessLimit` ou `PerJobUserTimeLimit`, est une empreinte d'évasion Cuckoo / Joe quasi pathognomonique. ETW `Microsoft-Windows-Kernel-Process` plus les hooks usermode sur `QueryInformationJobObject` dans kernelbase.dll captent le chemin standard ; les syscalls directs contournent les hooks usermode. Les sandboxes matures contrent en spawnant l'échantillon hors de leur job monitor et en le rattachant a posteriori via PsSetCreateProcessNotifyRoutineEx, ne laissant aucune trace d'appartenance à interroger.

Exemples de syscalls directs

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())
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20