Geoff Chappell - Software Analyst
CURRENT WORK ITEM - PREVIEW ONLY
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.
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 |
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.