NtCreateDirectoryObject
Creates a new directory object in the Windows object manager namespace.
Prototype
NTSTATUS NtCreateDirectoryObject( PHANDLE DirectoryHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| DirectoryHandle | PHANDLE | out | Receives the handle to the newly created directory object. |
| DesiredAccess | ACCESS_MASK | in | Combination of DIRECTORY_QUERY, DIRECTORY_TRAVERSE, DIRECTORY_CREATE_OBJECT, DIRECTORY_CREATE_SUBDIRECTORY, DIRECTORY_ALL_ACCESS. |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | Name, RootDirectory and attribute flags (OBJ_CASE_INSENSITIVE, OBJ_PERMANENT, OBJ_OPENIF, …). |
Syscall IDs by Windows version
| Windows version | Syscall ID | Build |
|---|---|---|
| Win10 1507 | 0x9B | win10-1507 |
| Win10 1607 | 0x9C | win10-1607 |
| Win10 1703 | 0x9F | win10-1703 |
| Win10 1709 | 0xA0 | win10-1709 |
| Win10 1803 | 0xA1 | win10-1803 |
| Win10 1809 | 0xA1 | win10-1809 |
| Win10 1903 | 0xA2 | win10-1903 |
| Win10 1909 | 0xA2 | win10-1909 |
| Win10 2004 | 0xA6 | win10-2004 |
| Win10 20H2 | 0xA6 | win10-20h2 |
| Win10 21H1 | 0xA6 | win10-21h1 |
| Win10 21H2 | 0xA7 | win10-21h2 |
| Win10 22H2 | 0xA7 | win10-22h2 |
| Win11 21H2 | 0xA9 | win11-21h2 |
| Win11 22H2 | 0xAA | win11-22h2 |
| Win11 23H2 | 0xAA | win11-23h2 |
| Win11 24H2 | 0xAC | win11-24h2 |
| Server 2016 | 0x9C | winserver-2016 |
| Server 2019 | 0xA1 | winserver-2019 |
| Server 2022 | 0xA9 | winserver-2022 |
| Server 2025 | 0xAC | winserver-2025 |
Kernel module
Related APIs
Syscall stub
4C 8B D1 mov r10, rcx B8 AC 00 00 00 mov eax, 0xAC 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
Undocumented notes
The SSN drifts steadily — `0x9B` on 1507 up to `0xAC` on Win11 24H2 — so direct-syscall stubs targeting NtCreateDirectoryObject across builds need dynamic resolution. The kernel object manager organizes named objects in a tree of `Directory` objects rooted at `\` (the global root). Standard children include `\BaseNamedObjects` (Session 0 IPC), `\Sessions\<n>\BaseNamedObjects` (per-session IPC, where most user-mode named events/mutants live), `\??` (current process DOS device link directory), `\KnownDlls`, `\Device`, `\Driver`. NtCreateDirectoryObject lets a caller (with the right access) plant new subdirectories — only the global root requires SeCreatePermanentPrivilege for OBJ_PERMANENT.
Common malware usage
Three offensive angles. **Sandbox-escape staging**: many user-mode sandboxes virtualize the BNO namespace by giving the sandboxed process a private `\Sessions\<n>\AppContainerNamedObjects\<sid>` root; creating a custom directory inside or alongside that root is a step in containing-escape chains. **C2 / inter-implant rendezvous**: multiple implants in the same session create a shared subdirectory under `\Sessions\<n>\BaseNamedObjects\<random>` and use it as a discovery board for named events and ALPC port symlinks — invisible to disk and to most EDRs which only watch filesystem and registry. **Sandbox-escape via planted symlink**: pair NtCreateDirectoryObject with NtCreateSymbolicLinkObject to overlay a fake namespace directory containing redirecting links (CVE-2019-0808 family, AppContainer escape research from James Forshaw / Project Zero).
Detection opportunities
Object-manager namespace creation is not surfaced by Sysmon. The authoritative source is kernel-mode ObRegisterCallbacks on `IoDirectoryObjectType` (`ObGetObjectType(L"Directory")`), or the ETW Microsoft-Windows-Kernel-Audit-API-Calls provider. The strongest signal is **a non-system, non-CSRSS process creating a directory under `\Sessions\<n>\BaseNamedObjects` with OBJ_PERMANENT or directly under `\` with OBJ_OPENIF**. WinObj from Sysinternals or `tools\objview` can be used live for hunt-mode; defensive products like SpecterOps's `ObjectExplorer` automate the same enumeration. Look also for processes that create a directory and then immediately create a symlink inside it — strong correlation with namespace-redirection abuse.
Direct syscall examples
asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtCreateDirectoryObject (SSN 0xAC, Win11 24H2)
NtCreateDirectoryObject PROC
mov r10, rcx ; syscall convention
mov eax, 0ACh ; SSN (BUILD-SPECIFIC, resolve dynamically)
syscall
ret
NtCreateDirectoryObject ENDPcCreate a shared rendezvous directory under BNO
// Plant \Sessions\<n>\BaseNamedObjects\Acme to act as a per-session
// inter-implant rendezvous. Named events and ALPC ports go in here.
#include <windows.h>
#include <winternl.h>
#define DIRECTORY_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0xF)
#define OBJ_CASE_INSENSITIVE 0x00000040
typedef NTSTATUS (NTAPI *pNtCreateDirectoryObject)(
PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES);
HANDLE CreateRendezvousDir(PCWSTR fullPath) {
pNtCreateDirectoryObject NtCreateDirectoryObject = (pNtCreateDirectoryObject)
GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateDirectoryObject");
UNICODE_STRING us;
RtlInitUnicodeString(&us, fullPath);
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, &us, OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE hDir = NULL;
if (NT_SUCCESS(NtCreateDirectoryObject(&hDir, DIRECTORY_ALL_ACCESS, &oa)))
return hDir;
return NULL;
}rustSandbox-overlay scaffolding
// Cargo: ntapi = "0.4", widestring = "1"
// Create an overlay directory that we will later populate with a symbolic
// link redirecting \??\X: to a sensitive path. Combined with the symlink call
// this is the standard pattern for namespace-redirection sandbox escape PoCs.
use ntapi::ntobapi::NtCreateDirectoryObject;
use ntapi::ntrtl::RtlInitUnicodeString;
use ntapi::ntobapi::OBJ_CASE_INSENSITIVE;
use winapi::shared::ntdef::{OBJECT_ATTRIBUTES, UNICODE_STRING};
use widestring::U16CString;
pub unsafe fn create_overlay(path: &str) -> Option<isize> {
let w = U16CString::from_str(path).ok()?;
let mut us: UNICODE_STRING = std::mem::zeroed();
RtlInitUnicodeString(&mut us, w.as_ptr());
let mut oa: OBJECT_ATTRIBUTES = std::mem::zeroed();
oa.Length = std::mem::size_of::<OBJECT_ATTRIBUTES>() as u32;
oa.ObjectName = &mut us;
oa.Attributes = OBJ_CASE_INSENSITIVE;
let mut h: isize = 0;
let s = NtCreateDirectoryObject(&mut h as *mut _ as _, 0xF000F, &mut oa);
if s >= 0 { Some(h) } else { None }
}MITRE ATT&CK mappings
Last verified: 2026-05-20