> Windows Syscalls
ntoskrnl.exeT1083T1518.001T1106

NtQueryDirectoryObject

Enumera las entradas (nombre + tipo) dentro de un directory del object manager.

Prototipo

NTSTATUS NtQueryDirectoryObject(
  HANDLE   DirectoryHandle,
  PVOID    Buffer,
  ULONG    Length,
  BOOLEAN  ReturnSingleEntry,
  BOOLEAN  RestartScan,
  PULONG   Context,
  PULONG   ReturnLength
);

Argumentos

NameTypeDirDescription
DirectoryHandleHANDLEinHandle de NtOpenDirectoryObject abierto con acceso DIRECTORY_QUERY.
BufferPVOIDoutRecibe una o más entradas OBJECT_DIRECTORY_INFORMATION seguidas de sus cuerpos UNICODE_STRING.
LengthULONGinTamaño de Buffer en bytes.
ReturnSingleEntryBOOLEANinTRUE devuelve una entrada por llamada; FALSE empaca todas las que quepan.
RestartScanBOOLEANinTRUE reinicia la enumeración; FALSE reanuda usando Context.
ContextPULONGin/outCursor de enumeración entrada/salida mantenido por el kernel.
ReturnLengthPULONGoutRecibe los bytes realmente escritos en Buffer.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070x12Bwin10-1507
Win10 16070x131win10-1607
Win10 17030x136win10-1703
Win10 17090x139win10-1709
Win10 18030x13Bwin10-1803
Win10 18090x13Cwin10-1809
Win10 19030x13Dwin10-1903
Win10 19090x13Dwin10-1909
Win10 20040x143win10-2004
Win10 20H20x143win10-20h2
Win10 21H10x143win10-21h1
Win10 21H20x144win10-21h2
Win10 22H20x144win10-22h2
Win11 21H20x14Awin11-21h2
Win11 22H20x14Cwin11-22h2
Win11 23H20x14Cwin11-23h2
Win11 24H20x14Ewin11-24h2
Server 20160x131winserver-2016
Server 20190x13Cwinserver-2019
Server 20220x149winserver-2022
Server 20250x14Ewinserver-2025

Módulo del kernel

ntoskrnl.exeNtQueryDirectoryObject

APIs relacionadas

NtOpenDirectoryObjectNtCreateDirectoryObjectFindFirstFileNtQuerySystemInformation

Stub del syscall

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

NtQueryDirectoryObject es de los syscalls con SSN más volátiles (`0x12B` → `0x14E`) porque está tarde en la tabla y Microsoft inserta casi cada release entradas nuevas antes. El buffer de salida es un array empaquetado de `OBJECT_DIRECTORY_INFORMATION { UNICODE_STRING Name; UNICODE_STRING TypeName; }` con los `Buffer` UNICODE_STRING apuntando al mismo buffer; un Name NULL termina la lista. Cada entrada dice tanto **qué** está nombrado (p. ej. `Cor_SxSPublic_IPCBlock_Perfmon_v4.0.30319_Global_Mutex`) como **de qué tipo** (`Event`, `Mutant`, `Section`, `Directory`, `SymbolicLink`, `ALPC Port`, `Job`, `Semaphore`).

Uso común por malware

Tres usos principales. **Descubrimiento de software de seguridad** — recorrer `\\Sessions\\<n>\\BaseNamedObjects` y `\\BaseNamedObjects`, matchear nombres contra una lista curada de mutants y events AV/EDR (Defender, Sophos, Bitdefender, CrowdStrike Falcon, SentinelOne, Carbon Black). Una de las técnicas de descubrimiento de sandbox más baratas porque no requiere imports más allá de ntdll ni produce tráfico FS o de registro. **Descubrimiento de rendezvous C2/implante** — misma primitiva, buscando el subdirectorio marcador por sesión del atacante para coordinar operaciones multi-implante. **Descubrimiento FS/device desde una sandbox** — recorrer `\\??` y `\\Device` para saber qué devices Mount Manager y letras DOS existen en la vista job/AppContainer actual; inteligencia necesaria para la fuga vía symlink plantado (linaje CVE-2019-0808). La enumeración de `\\Driver` revela también qué drivers de kernel de terceros (en especial minifilters EDR) están cargados — exactamente la inteligencia que BYOVD necesita sobre drivers vulnerables.

Oportunidades de detección

La huella es: **un proceso abre un directory del object manager (NtOpenDirectoryObject con DIRECTORY_QUERY) e inmediatamente llama a NtQueryDirectoryObject en bucle**. Casi ningún software usermode legítimo lo hace — Process Explorer, WinObj, Process Hacker, y algunos self-checks de EDR. La detección vía ObRegisterCallbacks sobre `IoDirectoryObjectType` no ve directamente la enumeración, pero los hooks EDR sobre NtQueryDirectoryObject en ntdll la cazan; ETW Microsoft-Windows-Kernel-Audit-API-Calls también la expone. La regla ASR de Defender 'Block process creations originating from PSExec and WMI commands' ha alertado históricamente sobre ráfagas de NtQueryDirectoryObject desde hijos de Office.

Ejemplos de syscalls directos

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtQueryDirectoryObject (SSN 0x14E, Win11 24H2)
NtQueryDirectoryObject PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 14Eh         ; SSN (BUILD-SPECIFIC, resolve dynamically)
    syscall
    ret
NtQueryDirectoryObject ENDP

cBNO mutex sweep for AV/EDR fingerprints

// Walk \Sessions\<n>\BaseNamedObjects matching entries against a small
// AV/EDR mutex list. Returns the first hit's UNICODE name or NULL.
#include <windows.h>
#include <winternl.h>

typedef struct _OBJECT_DIRECTORY_INFORMATION {
    UNICODE_STRING Name;
    UNICODE_STRING TypeName;
} OBJECT_DIRECTORY_INFORMATION;

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

static const WCHAR* kHits[] = {
    L"Global\\SQMMUTEX_TIMER",         // Defender
    L"CrowdStrikeFalconRules",          // Falcon
    L"SentinelOne_Mutex",               // S1
};

BOOL ScanBNO(HANDLE hDir) {
    pNtQueryDirectoryObject NtQueryDirectoryObject = (pNtQueryDirectoryObject)
        GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryDirectoryObject");
    BYTE buf[8192]; ULONG ctx = 0, ret = 0; BOOLEAN restart = TRUE;
    while (NT_SUCCESS(NtQueryDirectoryObject(hDir, buf, sizeof(buf),
                                             FALSE, restart, &ctx, &ret))) {
        restart = FALSE;
        OBJECT_DIRECTORY_INFORMATION* p = (OBJECT_DIRECTORY_INFORMATION*)buf;
        while (p->Name.Length) {
            for (int i = 0; i < _countof(kHits); ++i) {
                if (wcsstr(p->Name.Buffer, kHits[i])) return TRUE;
            }
            p++;
        }
    }
    return FALSE;
}

rustEnumerate \Driver for BYOVD intel

// Cargo: ntapi = "0.4"
// Dump loaded kernel driver object names — exactly what BYOVD attacks need to
// know about EDR minifilters and exploitable third-party drivers on the host.
use ntapi::ntobapi::NtQueryDirectoryObject;
use winapi::shared::ntdef::UNICODE_STRING;

#[repr(C)]
struct ObjectDirectoryInformation {
    name: UNICODE_STRING,
    type_name: UNICODE_STRING,
}

pub unsafe fn list_drivers(driver_dir: isize) -> Vec<String> {
    let mut out = Vec::new();
    let mut buf = vec![0u8; 16 * 1024];
    let mut ctx: u32 = 0; let mut ret: u32 = 0; let mut restart = 1u8;
    loop {
        let s = NtQueryDirectoryObject(driver_dir as _,
            buf.as_mut_ptr() as _, buf.len() as u32, 0, restart,
            &mut ctx, &mut ret);
        if s < 0 { break; }
        restart = 0;
        let mut p = buf.as_ptr() as *const ObjectDirectoryInformation;
        while (*p).name.Length != 0 {
            let n = &(*p).name;
            let slice = std::slice::from_raw_parts(n.Buffer, (n.Length / 2) as usize);
            out.push(String::from_utf16_lossy(slice));
            p = p.add(1);
        }
        if s == 0 { break; }
    }
    out
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20