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
| Name | Type | Dir | Description |
|---|---|---|---|
| DirectoryHandle | HANDLE | in | Handle de NtOpenDirectoryObject abierto con acceso DIRECTORY_QUERY. |
| Buffer | PVOID | out | Recibe una o más entradas OBJECT_DIRECTORY_INFORMATION seguidas de sus cuerpos UNICODE_STRING. |
| Length | ULONG | in | Tamaño de Buffer en bytes. |
| ReturnSingleEntry | BOOLEAN | in | TRUE devuelve una entrada por llamada; FALSE empaca todas las que quepan. |
| RestartScan | BOOLEAN | in | TRUE reinicia la enumeración; FALSE reanuda usando Context. |
| Context | PULONG | in/out | Cursor de enumeración entrada/salida mantenido por el kernel. |
| ReturnLength | PULONG | out | Recibe los bytes realmente escritos en Buffer. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x12B | win10-1507 |
| Win10 1607 | 0x131 | win10-1607 |
| Win10 1703 | 0x136 | win10-1703 |
| Win10 1709 | 0x139 | win10-1709 |
| Win10 1803 | 0x13B | win10-1803 |
| Win10 1809 | 0x13C | win10-1809 |
| Win10 1903 | 0x13D | win10-1903 |
| Win10 1909 | 0x13D | win10-1909 |
| Win10 2004 | 0x143 | win10-2004 |
| Win10 20H2 | 0x143 | win10-20h2 |
| Win10 21H1 | 0x143 | win10-21h1 |
| Win10 21H2 | 0x144 | win10-21h2 |
| Win10 22H2 | 0x144 | win10-22h2 |
| Win11 21H2 | 0x14A | win11-21h2 |
| Win11 22H2 | 0x14C | win11-22h2 |
| Win11 23H2 | 0x14C | win11-23h2 |
| Win11 24H2 | 0x14E | win11-24h2 |
| Server 2016 | 0x131 | winserver-2016 |
| Server 2019 | 0x13C | winserver-2019 |
| Server 2022 | 0x149 | winserver-2022 |
| Server 2025 | 0x14E | winserver-2025 |
Módulo del kernel
APIs relacionadas
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 ENDPcBNO 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