NtQueryDirectoryObject
Enumeriert die Einträge (Name + Typ) innerhalb eines Object-Manager-Verzeichnisses.
Prototyp
NTSTATUS NtQueryDirectoryObject( HANDLE DirectoryHandle, PVOID Buffer, ULONG Length, BOOLEAN ReturnSingleEntry, BOOLEAN RestartScan, PULONG Context, PULONG ReturnLength );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| DirectoryHandle | HANDLE | in | Handle aus NtOpenDirectoryObject, geöffnet mit DIRECTORY_QUERY. |
| Buffer | PVOID | out | Empfängt eine oder mehrere OBJECT_DIRECTORY_INFORMATION-Einträge gefolgt von deren UNICODE_STRING-Bodies. |
| Length | ULONG | in | Größe von Buffer in Bytes. |
| ReturnSingleEntry | BOOLEAN | in | TRUE liefert einen Eintrag pro Aufruf; FALSE packt so viele wie passen. |
| RestartScan | BOOLEAN | in | TRUE startet die Enumeration neu; FALSE setzt mittels Context fort. |
| Context | PULONG | in/out | Vom Kernel verwalteter In/Out-Enumerations-Cursor. |
| ReturnLength | PULONG | out | Empfängt die tatsächlich nach Buffer geschriebenen Bytes. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | 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 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
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
Undokumentierte Hinweise
NtQueryDirectoryObject gehört zu den SSN-volatilsten Syscalls (`0x12B` → `0x14E`), weil er spät in der Tabelle liegt und Microsoft fast jedes Release neue Einträge davor einfügt. Der Ausgabepuffer ist ein gepacktes Array `OBJECT_DIRECTORY_INFORMATION { UNICODE_STRING Name; UNICODE_STRING TypeName; }`, dessen UNICODE_STRING-`Buffer`-Felder in denselben Puffer zurückzeigen; ein NULL-Name beendet die Liste. Jeder Eintrag sagt sowohl **was** benannt ist (z. B. `Cor_SxSPublic_IPCBlock_Perfmon_v4.0.30319_Global_Mutex`) als auch **welchen Typs** (`Event`, `Mutant`, `Section`, `Directory`, `SymbolicLink`, `ALPC Port`, `Job`, `Semaphore`).
Häufige Malware-Nutzung
Drei Hauptnutzungen. **Security-Software-Discovery** — `\\Sessions\\<n>\\BaseNamedObjects` und `\\BaseNamedObjects` durchgehen, Namen gegen eine kuratierte Liste AV/EDR-Mutants und -Events abgleichen (Defender, Sophos, Bitdefender, CrowdStrike Falcon, SentinelOne, Carbon Black). Eine der billigsten Sandbox-Discovery-Techniken, da keine Imports außer ntdll nötig sind und weder FS- noch Registry-Traffic anfällt. **C2/Implant-Rendezvous-Discovery** — gleiche Primitive, gesucht wird das per-Session Marker-Verzeichnis des Angreifers zur Koordination von Multi-Implant-Operationen. **FS/Device-Discovery aus einer Sandbox** — `\\??` und `\\Device` durchgehen, um zu sehen, welche Mount-Manager-Devices und DOS-Buchstaben in der aktuellen Job-/AppContainer-Sicht existieren; nötiges Intel für Sandbox-Escape via Symlink-Planting (CVE-2019-0808-Linie). Auch `\\Driver` enthüllt, welche Third-Party-Kernel-Treiber (insbesondere EDR-Minifilter) geladen sind — exakt das Intel, das BYOVD-Angriffe zu verwundbaren Treibern brauchen.
Erkennungsmöglichkeiten
Fingerabdruck: **ein Prozess öffnet ein Object-Manager-Directory (NtOpenDirectoryObject mit DIRECTORY_QUERY) und ruft sofort NtQueryDirectoryObject in einer Schleife auf**. Kaum eine legitime User-Mode-Software tut das — Process Explorer, WinObj, Process Hacker und einige EDR-Self-Checks. Detektion über kernelseitiges ObRegisterCallbacks auf `IoDirectoryObjectType` sieht die Enumeration nicht direkt, EDR-Hooks auf NtQueryDirectoryObject in ntdll fangen sie ab; ETW Microsoft-Windows-Kernel-Audit-API-Calls macht sie ebenfalls sichtbar. Die Defender-ASR-Regel 'Block process creations originating from PSExec and WMI commands' hat historisch auch bei NtQueryDirectoryObject-Bursts aus Office-Kindern alarmiert.
Direkte Syscall-Beispiele
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
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20