Geoff Chappell - Software Analyst
The OBP_LOOKUP_CONTEXT structure (formally _OBP_LOOKUP_CONTEXT) dates from Windows XP. With one exception, it only ever exists temporarily on the stack while internal routines in the kernel look through the object namespace. The exception is that one internal routine’s need for this structure comes with so much other temporary data that no self-respecting programmer would put it all on the stack and so the space is instead obtained from non-paged pool (with the efficiency of a per-processor lookaside list, starting with Windows Vista).
As temporary data for looking through the object namespace, the structure is vital knowledge for Microsoft’s own programmers of the Object Manager. It plausibly has been too for some programmers outside Microsoft who are debugging the Object Manager, having been mystified by some apparent mistreatment of some object. Nowadays, the most interest outside Microsoft will be for security researchers who are examining the Object Manager for vulnerabilities. I don’t well know the methods of searching for vulnerabilities but I imagine that a thorough examination of the Object Manager is simply not possible without encountering the OBP_LOOKUP_CONTEXT.
The OBP_LOOKUP_CONTEXT is not documented. This is, of course, not the slightest surprise for a structure that exists only for internal routines. The surprise instead is that even the structure’s name is knowable from outside Microsoft.
How Microsoft’s name for this transient structure—and for its members—is known is the usual one: type information for the structure is available in public symbol files for the kernel, though in this case not until Windows Vista SP1.
But why any public symbol file has type information for this structure is far from obvious. Since the structure is not nested in another that is shown in the symobl files, nor even pointed to from one, type information for it is not needed to complete the type information for another. Instead, disclosure occurs only because a pointer to the structure is an argument to an inline routine that is in turn called from another that’s also defined in some header that is included when generating the public symbol file. Starting with Windows Vista SP1, the public symbol files record the type of the called routine. Starting with Windows 8, they record the name too:
FORCEINLINE VOID ObpUnlockDirectory ( OBJECT_DIRECTORY *, OBP_LOOKUP_CONTEXT *);
Disclosure of this inline routine’s name, which likely is unintended, has nothing directly to do with Windows 8 but with developments in the compiler and linker (and thus of what goes into PDB files) for Visual Studio 2012. Still, published it is—and were it not that these developments and the heavy use of inline routines, the OBP_LOOKUP_CONTEXT would be known only to the sort of “researcher” who’s compromised by learning from leaked source code.
From Windows XP through to the original Windows 10, much of the kernel’s use of the OBP_LOOKUP_CONTEXT looks to be through helper routines that are only ever inlined. One helper, named ObpReleaseLookupContextObject, is named in the public symbol files from Windows Server 2003 through to Windows Vista because although it is often inlined it is sometimes not. The inline expansions are many and large. Their repetition for each of some other routines’ different exits for different cases of failure plausibly gains no run-time efficiency and is just bloat. Programmers’ opinions on the merits of inlining vary widely, as does their interpretation of experimental observation, but some of these helpers surely are too large and execute too infrequently to be worth the trouble. Starting with Version 1511, at least some are sometimes not inlined—indeed, are inlined only rarely—and so their names show in the public symbol files. All can then be matched, with good confidence but some reservations, against the obvious inline expansions dating back to Windows XP: ObpLockDirectoryExclusive, ObpLockDirectoryShared and ObpReleaseLookupContext. The last of these is the one whose use of ObpUnlockDirectory got it into the public symbol files as long ago as 2012.
That I take the trouble to sketch an obscure structure’s use in inline routines is because I see a wider point for researchers and other commentators. Say what you will about Microsoft—the supposedly more open (and better-behaved) new Microsoft or just the Microsoft that always was—but however much the company may guard its private symbol files, it lets far more out in the public symbol files than tends to be recognised by either its detractors or supporters.
As a structure that is only ever passed between internal routines, the OBP_LOOKUP_CONTEXT is highly susceptible to changing between builds. Against this is that the structure is small and relatively straightforward, and so the variations have not been many. The following changes of size summarise which versions are affected:
Version | Size (x86) | Size (x64) |
---|---|---|
5.1 to early 5.2 (before SP1) | 0x10 | |
late 5.2 to 6.1 | 0x14 | 0x20 |
6.2 to 2004 | 0x18 | 0x28 |
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 with Windows Vista SP1.
Offset (x86) | Offset (x64) | Definition | Versions |
---|---|---|---|
0x00 | 0x00 |
OBJECT_DIRECTORY *Directory; |
5.1 and higher |
0x04 | 0x08 |
PVOID Object; |
5.1 and higher |
0x08 | 0x10 |
OBJECT_DIRECTORY_ENTRY **EntryLink; |
6.2 and higher |
0x08 (late 5.2 to 6.1); 0x0C |
0x10 (late 5.2 to 6.1); 0x18 |
ULONG HashValue; |
late 5.2 and higher |
0x08 (5.1 to early 5.2); 0x0C (late 5.2 to 6.1); 0x10 |
0x14 (late 5.2 to 6.1); 0x1C |
USHORT HashIndex; |
5.1 and higher |
0x0A (5.1 to early 5.2); 0x0E (late 5.2 to 6.1); 0x12 |
0x16 (late 5.2 to 6.1); 0x1E |
BOOLEAN DirectoryLocked; |
5.1 and higher |
0x0F (6.1); 0x13 |
0x17 (6.1); 0x1F |
BOOLEAN LockedExclusive; |
6.1 and higher |
0x0C (5.1 to early 5.2); 0x10 (late 5.2 to 6.1); 0x14 |
0x18 (late 5.2 to 6.1); 0x20 |
ULONG volatile LockStateSignature; |
5.1 to 6.1 |
ULONG LockStateSignature; |
6.2 and higher |
The intended scheme is that the LockStateSignature is initially 0xFFFF1234, but changes to 0xAAAA1234 or 0xBBBB1234 in anticipation of exclusive or shared locking of some directory object, and then to 0xCCCC1234 or 0xDDDD1234 after the lock is acquired, and to 0xEEEE1234 after the lock is released.