> Windows Syscalls
ntoskrnl.exeT1497.001T1497T1106

NtQueryInformationJobObject

Recupera información de contabilidad, límites o restricciones de UI de un job object.

Prototipo

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

Argumentos

NameTypeDirDescription
JobHandleHANDLEinHandle al job, con derechos JOB_OBJECT_QUERY. NULL refiere al job que contiene al proceso llamador.
JobObjectInformationClassJOBOBJECTINFOCLASSinEnum de clase de info. Valores comunes: JobObjectBasicAccountingInformation = 1, JobObjectSecurityLimitInformation = 5, JobObjectExtendedLimitInformation = 9, JobObjectBasicProcessIdList = 3.
JobObjectInformationPVOIDoutBuffer proporcionado por el llamador que recibe la estructura solicitada (JOBOBJECT_BASIC_ACCOUNTING_INFORMATION, JOBOBJECT_EXTENDED_LIMIT_INFORMATION, ...).
JobObjectInformationLengthULONGinTamaño en bytes del buffer JobObjectInformation.
ReturnLengthPULONGoutOpcional; recibe el tamaño realmente requerido (útil para clases de longitud variable como JobObjectBasicProcessIdList).

IDs de syscalls por versión de Windows

Versión 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

Módulo del kernel

ntoskrnl.exeNtQueryInformationJobObject

APIs relacionadas

QueryInformationJobObjectNtSetInformationJobObjectNtIsProcessInJobNtAssignProcessToJobObjectNtCreateJobObject

Stub del 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

Notas no documentadas

La contraparte de NtSetInformationJobObject. El SSN deriva más que el de NtIsProcessInJob — `0x144` en Win10 1903 / 1909, `0x14A` en 2004–21H1, `0x156` en Win11 24H2 / Server 2025 — así que hardcodearlo a través de múltiples builds es frágil y se prefiere la resolución dinámica al estilo Hell's Gate. Las clases de info más relevantes para malware son: `JobObjectBasicAccountingInformation` (=1) para el tiempo CPU user/kernel total y conteo de procesos, `JobObjectBasicProcessIdList` (=3) para enumerar cada PID en el job, `JobObjectExtendedLimitInformation` (=9) para límites de memoria y procesos activos, y `JobObjectSecurityLimitInformation` (=5) para restricciones SID/token (heredadas, deprecadas).

Uso común por malware

Usado por loaders sandbox-aware que combinan `NtIsProcessInJob` con un seguimiento `NtQueryInformationJobObject(JobObjectExtendedLimitInformation)` para fingerprintear el *tipo* de job. Las firmas reveladoras de Cuckoo / Joe Sandbox / ANY.RUN incluyen: un `ActiveProcessLimit` muy bajo (1–3), un `PerJobUserTimeLimit` puesto al presupuesto de análisis (típicamente 60–300 segundos) y un flag JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE — ninguno coincide con el job de renderer de Chrome (límite alto de procesos, sin cap de tiempo CPU) ni con el de WSL2. En cambio, la query de lista de procesos del job (`JobObjectBasicProcessIdList`) es una primitiva popular de *enumeración* — una vez que un proceso pie en tierra está en un job sandbox / contenedor, recorrer los hermanos revela el monitor, el agente y otros objetivos a suprimir.

Oportunidades de detección

Como NtIsProcessInJob, esta llamada es mainstream — cada shell de Windows, cada navegador, cada runtime de contenedor lee info de job constantemente. El patrón de alta señal es la *clase de info*: un proceso recién dropeado y no firmado que pide `JobObjectExtendedLimitInformation` (=9) sobre su job contenedor en los primeros segundos tras el lanzamiento, luego bifurcando sobre `ActiveProcessLimit` o `PerJobUserTimeLimit`, es una huella de evasión Cuckoo / Joe casi patognomónica. ETW `Microsoft-Windows-Kernel-Process` más hooks usermode sobre `QueryInformationJobObject` en kernelbase.dll capturan el camino estándar; los syscalls directos eluden los hooks usermode. Las sandboxes maduras contrarrestan spawneando la muestra fuera de su job monitor y readquiriéndola post-facto vía PsSetCreateProcessNotifyRoutineEx, sin dejar rastro de membresía que consultar.

Ejemplos de syscalls directos

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

Mapeos MITRE ATT&CK

Last verified: 2026-05-20