CURRENT WORK ITEM - PREVIEW ONLY

OBJECT_DIRECTORY

The OBJECT_DIRECTORY structure (formally _OBJECT_DIRECTORY) is the Object Manager object that the Object Manager itself uses for representing a directory in the object namespace. The directory object is a container of other objects, including more directory objects. All versions of Windows provide for creating directory objects from both kernel mode and user mode through the functions NtCreateDirectoryObject (in user mode only) and ZwCreateDirectoryObject and all versions provide similarly for opening directory objects through NtOpenDirectoryObject and ZwOpenDirectoryObject. These functions give their callers a handle to the directory object. When kernel-mode software that has such a handle feeds it to some such function as ObReferenceObjectByHandle, then the reference it gets back is a pointer to an OBJECT_DIRECTORY.

Variability

The OBJECT_DIRECTORY is highly susceptible to changing between builds. The following changes of size give some rough indication:

Version Size (x86) Size (x64)
3.10 to 4.0 0x9C  
5.0 0xA0  
early 5.1 (before SP2) 0xA0  
late 5.1 0xA4  
5.2 0xA0 0x0140
6.0 to 6.3 0xA8 0x0150
10.0 to 1607 0xAC 0x0158
1703 to 2004 0xB0 0x0158

Layout

The sizes in the preceding table and the offsets, names and types in the next are from type information in public symbol files for the kernel, starting from Windows 2000 SP3.

Offset (x86) Offset (x64) Definition Versions Remarks
0x00 0x00
OBJECT_DIRECTORY_ENTRY *HashBuckets [37];
all  
0x94 (3.10 to 5.0)  
OBJECT_DIRECTORY_ENTRY **LookupBucket;
3.10 to 5.0  
0x98 (3.10 to 5.0)  
BOOLEAN LookupFound;
3.10 to 5.0  
0x9A (4.0 to 5.0)  
USHORT SymbolicLinkUsageCount;
4.0 to 5.0 next at 0x9E
0x94 0x0128
EX_PUSH_LOCK Lock;
5.1 and higher  
0x9C (5.0);
0x98
0x0130
DEVICE_MAP *DeviceMap;
5.0 to 6.1  
union {
    DEVICE_MAP *DeviceMap;
    OBJECT_DIRECTORY *ShadowDirectory;
};
6.2 to 6.3  
DEVICE_MAP *DeviceMap;
10.0 and higher  
0x9C 0x0138
OBJECT_DIRECTORY *ShadowDirectory;
10.0 and higher  
0x9C (late 5.1 to 6.3);
0xA0 (10.0 to 1607)
0x0138 (late 5.2 to 6.3);
0x0140 (10.0 to 1607)
ULONG SessionId;
late 5.1 to 1607 next at 0xAC and 0x0154
0x9C (early 5.1);
0xA0 (late 5.1)
 
USHORT Reserved;
5.1 only  
0x9E (early 5.1);
0xA2 (late 5.1)
 
USHORT SymbolicLinkUsageCount;
5.1 only previously at 0x9A
0xA0 (6.0 to 6.3);
0xA4 (10.0 to 1607);
0xA0
0x0140 (6.0 to 6.3);
0x0148 (10.0 to 1607);
0x0140
PVOID NamespaceEntry;
6.0 and higher  
0xA4 0x0148
PVOID SessionObject;
1703 and higher  
0xA4 (6.0 to 6.3);
0xA8
0x0148 (6.0 to 6.3);
0x0150
ULONG Flags;
6.0 and higher  
0xAC 0x0154
ULONG SessionId;
1703 and higher previously at 0xA0 and 0x0140

Each of the HashBuckets is a single-linked list of potentially many OBJECT_DIRECTORY_ENTRY structures. Each of these points to an object that is in the directory. The gain is, of course, that instead of having one long list of all such objects, the directory has 37 shorter lists of directory entries for objects whose name within the directory have the same hash. The hashing algorithm is surely well known and just as surely doesn’t matter much. All that is required of it is that it (quickly) produces a more or less uniform distribution of (typical) names into hash buckets. Yet perhaps because the details are unimportant, the algorithm does happen to have been left alone. Its code for 32-bit Windows plausibly has been untouched from Windows NT 3.51 to at least the original Windows 10. The essence is that successive characters are converted to upper case for adding to the hash, which in turn has its bits spread in a particular way on each loop. Given Count Unicode characters at address Pointer, the computation for 32-bit Windows goes something very like

ULONG Hash (PWCHAR Pointer, ULONG Count)
{
    ULONG hash = 0;
    while (Count -- != 0) {
        WCHAR ch = *Pointer ++;
        hash = (hash << 1) + hash + (hash >> 1);
        if (ch < L'a') hash += ch;
        else if (ch <= L'z') hash += ch - (L'a' - L'A');
        else hash += RtlUpcaseUnicodeChar (ch);
    }
    return hash;
}

See that the ShadowDirectory litterally grew out of the DeviceMap.

Since the Flags are not in union with a formal definition of bit fields, Microsoft’s names for the flags are presumably defined by macro and don’t show in type information even in the private symbol files that Microsoft keeps to itself. That said, Microsoft’s names for some of the Flags bits are known from the possibly accidental publication of NTOSP.H in the original and Version 1511 editions of the WDK for Windows 10:

Value Name Versions
0x00000001 OBP_DIRECTORY_NAMESPACE 6.0 and higher
0x00000002 OBP_NAMESPACE_DELETED 6.0 and higher
0x00000004 OBP_SEARCH_SHADOW 6.2 and higher
0x00000008 OBP_INHERIT_SECURITY 6.2 and higher
0x00000010   1607 and higher
0x00000020   1703 and higher

Microsoft’s names for the last two flags above aren’t known to have been published.