NtIsProcessInJob
Comprueba si un proceso se ejecuta dentro de un job object específico (o cualquiera).
Prototipo
NTSTATUS NtIsProcessInJob( HANDLE ProcessHandle, HANDLE JobHandle );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle al proceso a comprobar. Requiere PROCESS_QUERY_LIMITED_INFORMATION. A menudo NtCurrentProcess() ((HANDLE)-1). |
| JobHandle | HANDLE | in | Handle opcional a un job específico. NULL pregunta «¿está el proceso en *algún* job?» — la forma canónica de detección de sandbox. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x4F | win10-1507 |
| Win10 1607 | 0x4F | win10-1607 |
| Win10 1703 | 0x4F | win10-1703 |
| Win10 1709 | 0x4F | win10-1709 |
| Win10 1803 | 0x4F | win10-1803 |
| Win10 1809 | 0x4F | win10-1809 |
| Win10 1903 | 0x4F | win10-1903 |
| Win10 1909 | 0x4F | win10-1909 |
| Win10 2004 | 0x4F | win10-2004 |
| Win10 20H2 | 0x4F | win10-20h2 |
| Win10 21H1 | 0x4F | win10-21h1 |
| Win10 21H2 | 0x4F | win10-21h2 |
| Win10 22H2 | 0x4F | win10-22h2 |
| Win11 21H2 | 0x4F | win11-21h2 |
| Win11 22H2 | 0x4F | win11-22h2 |
| Win11 23H2 | 0x4F | win11-23h2 |
| Win11 24H2 | 0x4F | win11-24h2 |
| Server 2016 | 0x4F | winserver-2016 |
| Server 2019 | 0x4F | winserver-2019 |
| Server 2022 | 0x4F | winserver-2022 |
| Server 2025 | 0x4F | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 4F 00 00 00 mov eax, 0x4F 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
NtIsProcessInJob mantiene el SSN `0x4F` en todos los builds de Windows 10 / 11 / Server — una estabilidad notable que lo hace ideal para stubs syscall hardcodeados. Pese al tipo de retorno NTSTATUS, el wrapper Win32 `IsProcessInJob` expone un BOOLEAN como parámetro de salida: el kernel retorna `STATUS_PROCESS_IN_JOB` (0x00000123) o `STATUS_PROCESS_NOT_IN_JOB` (0x00000124), ambos valores `NT_SUCCESS`, así que un `if (NT_SUCCESS(status))` ingenuo no significa nada. Con `JobHandle == NULL`, el kernel recorre el puntero EPROCESS->Job y responde sobre *cualquier* job contenedor; con un handle real, comprueba la jerarquía anidada del job específico. La implementación es `PsIsProcessInJob` en ntoskrnl.
Uso común por malware
El check anti-sandbox de manual. El monitor de Cuckoo Sandbox coloca la muestra de análisis dentro de un job object para aplicar timeouts y limpiar procesos hijo; versiones antiguas de Joe Sandbox hacían lo mismo; varios frameworks de honeypot SOC (el agente Drakvuf, el bridge de ANY.RUN) dejan artefactos de pertenencia a un job. El malware llama `NtIsProcessInJob(NtCurrentProcess(), NULL)` y, si el resultado es `STATUS_PROCESS_IN_JOB`, se comporta benignamente (salida limpia, banner falso o desvío hacia una ruta de código señuelo). El problema: los contextos legítimos *también* están en job — cada renderer Chrome / Edge / Brave, cada app UWP, cada distro WSL2, cada contenedor Docker-on-Windows y cada proceso PowerShell lanzado desde el Programador de tareas vive en un job. Hardcodear «in-job = sandbox» genera muchos falsos positivos y los loaders modernos lo evitan cada vez más a favor de un scoring multi-señal.
Oportunidades de detección
Por sí solo, NtIsProcessInJob es benigno y extremadamente común — Chrome solo lo llama miles de veces por sesión. El valor de detección viene del *contexto*: un binario no firmado, recién dropeado, que llama a NtIsProcessInJob con NULL en el primer segundo de ejecución, inmediatamente seguido de `NtTerminateProcess(NtCurrentProcess(), 0)` o de un largo `NtDelayExecution`, es una firma fuerte de evasión de sandbox. Los EDR hookean el thunk usermode en kernel32 (`IsProcessInJob`) pero un syscall directo lo elude. Sysmon no tiene un evento dedicado; la mejor telemetría es ETW Threat-Intelligence para llamadas `NtQueryInformation*` combinada con el timing del árbol de procesos. El problema de detección de Cuckoo / Joe Sandbox está bien estudiado — los sandboxes modernos lanzan la muestra *fuera* de su job monitor (vía DETACHED_PROCESS o reparenting por WMI) precisamente para vencer este check.
Ejemplos de syscalls directos
asmx64 direct stub
; Direct syscall stub for NtIsProcessInJob (SSN 0x4F, all builds)
NtIsProcessInJob PROC
mov r10, rcx ; syscall convention
mov eax, 4Fh ; SSN
syscall
ret
NtIsProcessInJob ENDPcCuckoo / Joe Sandbox detection
// Anti-sandbox: bail out if we look like we're inside a monitor job.
// Beware: Chrome renderers, UWP apps and WSL2 all also report in-job.
#include <windows.h>
#define STATUS_PROCESS_IN_JOB ((NTSTATUS)0x00000123L)
#define STATUS_PROCESS_NOT_IN_JOB ((NTSTATUS)0x00000124L)
typedef NTSTATUS (NTAPI *pNtIsProcessInJob)(HANDLE, HANDLE);
BOOL LooksSandboxed(void) {
pNtIsProcessInJob NtIsProcessInJob = (pNtIsProcessInJob)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtIsProcessInJob");
if (!NtIsProcessInJob) return FALSE;
NTSTATUS s = NtIsProcessInJob((HANDLE)-1, NULL);
return s == STATUS_PROCESS_IN_JOB;
}
int main(void) {
if (LooksSandboxed()) { return 0; } // benign exit
// ...real payload...
return 0;
}rustdecoy branch on sandbox-positive
// Cargo: windows-sys = "0.59" (Win32_Foundation, Win32_System_Threading)
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::Threading::GetCurrentProcess;
const STATUS_PROCESS_IN_JOB: i32 = 0x00000123;
extern "system" {
fn NtIsProcessInJob(Process: HANDLE, Job: HANDLE) -> i32;
}
fn in_any_job() -> bool {
unsafe { NtIsProcessInJob(GetCurrentProcess(), std::ptr::null_mut()) == STATUS_PROCESS_IN_JOB }
}
fn main() {
if in_any_job() {
// Decoy: print something boring and exit 0.
println!("hello world");
return;
}
// real_payload();
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20