> Windows Syscalls
ntoskrnl.exeT1083T1518.001T1106

NtQueryDirectoryObject

Énumère les entrées (nom + type) à l'intérieur d'un directory de l'object manager.

Prototype

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

Arguments

NameTypeDirDescription
DirectoryHandleHANDLEinHandle issu de NtOpenDirectoryObject ouvert avec DIRECTORY_QUERY.
BufferPVOIDoutReçoit une ou plusieurs entrées OBJECT_DIRECTORY_INFORMATION suivies de leurs corps UNICODE_STRING.
LengthULONGinTaille de Buffer en octets.
ReturnSingleEntryBOOLEANinTRUE renvoie une entrée par appel ; FALSE en empile autant que possible.
RestartScanBOOLEANinTRUE réinitialise l'énumération ; FALSE reprend avec Context.
ContextPULONGin/outCurseur d'énumération entrée/sortie maintenu par le noyau.
ReturnLengthPULONGoutReçoit le nombre d'octets effectivement écrits dans Buffer.

IDs de syscalls par version de Windows

Version 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

Module noyau

ntoskrnl.exeNtQueryDirectoryObject

APIs liées

NtOpenDirectoryObjectNtCreateDirectoryObjectFindFirstFileNtQuerySystemInformation

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

Notes non documentées

NtQueryDirectoryObject fait partie des syscalls à SSN les plus volatils (`0x12B` → `0x14E`) car il vit tard dans la table et Microsoft insère presque chaque version de nouvelles entrées avant lui. Le buffer de sortie est un tableau compacté de `OBJECT_DIRECTORY_INFORMATION { UNICODE_STRING Name; UNICODE_STRING TypeName; }` dont les `Buffer` UNICODE_STRING pointent dans le même buffer ; un Name NULL termine la liste. Chaque entrée dit à la fois **quoi** est nommé (ex. `Cor_SxSPublic_IPCBlock_Perfmon_v4.0.30319_Global_Mutex`) et **de quel type** (`Event`, `Mutant`, `Section`, `Directory`, `SymbolicLink`, `ALPC Port`, `Job`, `Semaphore`).

Usage courant par les malwares

Trois usages principaux. **Découverte de logiciels de sécurité** — parcourir `\\Sessions\\<n>\\BaseNamedObjects` et `\\BaseNamedObjects`, matcher les noms contre une liste sélectionnée de mutants et events nommés AV/EDR (Defender, Sophos, Bitdefender, CrowdStrike Falcon, SentinelOne, Carbon Black). Une des techniques de découverte de sandbox les moins coûteuses car elle ne nécessite aucun import au-delà de ntdll et ne produit ni trafic FS ni trafic registre. **Découverte de rendez-vous C2/implant** — même primitive, à la recherche du sous-répertoire marqueur par session de l'attaquant pour coordonner les opérations multi-implants. **Découverte FS/device depuis une sandbox** — parcourir `\\??` et `\\Device` pour apprendre quels devices Mount Manager et lettres DOS existent dans la vue job/AppContainer actuelle ; renseignement nécessaire à l'évasion via plantation de symlink (lignée CVE-2019-0808). L'énumération de `\\Driver` révèle aussi quels drivers kernel tiers (notamment minifilters EDR) sont chargés — exactement le renseignement dont les attaques BYOVD ont besoin sur les pilotes vulnérables.

Opportunités de détection

L'empreinte : **un processus ouvre un directory d'object manager (NtOpenDirectoryObject avec DIRECTORY_QUERY) puis appelle NtQueryDirectoryObject en boucle**. Quasi aucun logiciel usermode légitime ne fait ça — Process Explorer, WinObj, Process Hacker, et quelques self-checks EDR. La détection via ObRegisterCallbacks sur `IoDirectoryObjectType` ne voit pas directement l'énumération, mais les hooks EDR sur NtQueryDirectoryObject dans ntdll l'attrapent ; ETW Microsoft-Windows-Kernel-Audit-API-Calls la remonte aussi. La règle ASR Defender 'Block process creations originating from PSExec and WMI commands' a historiquement alerté sur des rafales de NtQueryDirectoryObject depuis des enfants Office.

Exemples de syscalls directs

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
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20