Geoff Chappell, Software Analyst
The RTL_PROCESS_LOCK_INFORMATION structure is a recurring element in the RTL_PROCESS_LOCKS structure that a successful call to ZwQuerySystemInformation or NtQuerySystemInformation produces at the start of its output buffer when given the information class SystemLocksInformation (0x0C). There is one RTL_PROCESS_LOCK_INFORMATION for each ERESOURCE that is initialised but not yet deleted.
The RTL_PROCESS_LOCK_INFORMATION structure is not documented.
Microsoft does publish the practical equivalent of a C-language definition as type information in public symbol files, though not for the kernel, where the structure is prepared, nor even for low-level user-mode DLLs that interpret the structure, but for various higher-level user-mode DLLs such as URLMON.DLL and only then starting with version 6.2.
Two earlier disclosures of type information are known, though not in symbol files but in statically linked libraries: GDISRVL.LIB from the Device Driver Kit (DDK) for Windows NT 3.51; and SHELL32.LIB from the DDK for Windows NT 4.0.
The RTL_PROCESS_LOCK_INFORMATION is 0x24 or 0x30 bytes in 32-bit and 64-bit Windows, respectively, in version 3.51 and higher. The original is 0x3C bytes.
Offset (x86) | Offset (x64) | Definition | Versions | Remarks |
---|---|---|---|---|
0x00 (3.10 to 3.50) | unknown dword | 3.10 to 3.50 | ||
0x04 (3.10 to 3.50); 0x00 |
0x00 |
PVOID Address; |
all | |
0x08 (3.10 to 3.50) |
ULONG CreatorBackTraceIndex; |
3.10 to 3.50 | next as USHORT at 0x06 | |
0x0C (3.10 to 3.50); 0x04 |
0x08 |
USHORT Type; |
all | |
0x06 | 0x0A |
USHORT CreatorBackTraceIndex; |
3.51 and higher | previously ULONG at 0x08 |
0x0E (3.10 to 3.50) |
USHORT Depth; |
3.10 to 3.50 | ||
0x10 (3.10 to 3.50) |
PVOID OwnerBackTrace [4]; |
3.10 to 3.50 | ||
0x20 (3.10 to 3.50); 0x08 |
0x10 |
PVOID OwningThread; |
all | |
0x24 (3.10 to 3.50); 0x0C |
0x18 |
LONG LockCount; |
all | |
0x28 (3.10 to 3.50); 0x10 |
0x1C |
ULONG ContentionCount; |
all | |
0x2C (3.10 to 3.50) | unaccounted four bytes | 3.10 to 3.50 | ||
0x14 | 0x20 |
ULONG EntryCount; |
3.51 and higher | |
0x18 | 0x24 |
LONG RecursionCount; |
3.51 and higher | |
0x34 (3.10 to 3.50); 0x1C |
0x28 |
ULONG NumberOfWaitingShared; |
all | |
0x38 (3.10 to 3.50); 0x20 |
0x2C |
ULONG NumberOfWaitingExclusive; |
all |
The unknown dword that begins the structure in its original layout is always zero.
The Address is that of an ERESOURCE from the kernel’s list. This is a kernel-mode address, which is why the SystemLocksInformation case of NtQuerySystemInformation is nowadays failed for restricted callers.
The Type is always 1. Perhaps it was imagined that the SystemLocksInformation might be broadened to other types of lock than the ERESOURCE.
From version 3.50 up to but not including the version 5.2 from Windows Server 2003 SP1, the CreatorBackTraceIndex is always 0. Other versions, both earlier and later, take it from the same-named member of the ERESOURCE. What it is an index into is the kernel’s Stack Trace Database. The indexed entry in this database will have been created when the resource was initialised, given that a particular bit is set in the NtGlobalFlag and that the kernel actually did initialise the database. The required bit is 0x00200000 in versions 3.10 to 3.50 but FLG_KERNEL_STACK_TRACE_DB (0x00002000) in version 3.51 and higher. Microsoft has long documented that this flag “works only when using the checked build of Windows”, and indeed the free builds of versions 3.51 to 5.0 ignore this flag while initialising and therefore never can have a stack trace database for CreatorBackTraceIndex to be non-zero in an ERESOURCE.
The Depth in the original layout is the count of dwords that version 3.10 copies to the OwnerBackTrace array. These names are supposed because the count and dwords come from the Depth and OwnerBackTrace members of the original ERESOURCE. There is no copying in version 3.50: the count is set to zero. Even in version 3.10, no way is yet known that the count can be non-zero in the ERESOURCE.
Though the OwningThread is formally a PVOID, it is not the address of anything but is instead the thread ID. That the RTL_PROCESS_LOCK_INFORMATION has only one OwningThread is because this information structure tells only which thread, if any, has exclusive access to the resource—or, more precisely, had it when the kernel collected the information.
The LockCount is what the ERESOURCE has as its ActiveCount (before version 6.0) or ActiveEntries. It is the number of threads that have access to the resource, i.e., as the exclusive owner or as shared owners.
The unaccounted member at offset 0x2C in the original layout is presumably one of what the much later symbol files present as EntryCount and RecursionCount. No way is yet known that any of these get set in any version.