Windows Object Headers
This document describes the changes to the object header structure that have been made in Windows 7 and the areas of functionality these changes affect. It starts with a brief description of the layout of objects and the related data structures, the difference between the legacy and the new object header structures, followed by a detailed description of the reason for the changes and the areas of functionality that are affected by these changes. This document refers to all versions of Windows before Windows 7 as "older" versions as Windows. All debugger examples used in the document are on Windows Vista SP1 or Windows 7 kernel running on the X86 platform.
A structure becomes an object when the object manager, a component in the Windows kernel, puts headers before the start of the object, to provide a range of services including object naming, object reference counting, checking for security, using information stored in these headers. The following figure shows the layout of objects and the associated headers:
As shown in the above figure, objects are allocated from either the paged or non-paged pool. The allocation always starts with a pool header, a structure of type nt!_POOL_HEADER. The pool header is then followed by optional object headers, which could be of four different types - details of which are described later.The optional object headers are followed by a mandatory object header which is a structure of type nt!_OBJECT_HEADER and finally the object itself. This could be any of the 37 different object types supported as of Windows Vista. Examples of objects include events (nt!_KEVENT), semaphores (nt!_KSEMAPHORE), file objects (nt!_FILE_OBJECT), device objects (nt!_DEVICE_OBJECT). The last few bytes of the object header, the field OBJECT_HEADER->Body, overlap with the actual object body. The OBJECT_HEADER->Type->TypeInfo.PoolType field determines which pool a particular type of objects would be allocated from this can be either PagedPool or NonPagedPool.
Running the "!object" command in the kernel debugger displays the following output on older version of Windows:
053c: Object: 87d67f80 GrantedAccess: 00000001 Entry: 8fe02a78
Object: 87d67f80 Type: (87515590) File
ObjectHeader: 87d67f68 (old version)
HandleCount: 1 PointerCount: 1
On Windows 7 and later version of Windows the "!object" command displays a slightly different output:
0328: Object: 84e8ee10 GrantedAccess: 00100001 Entry: 9b710650
Object: 84e8ee10 Type: (848cc3c0) File
ObjectHeader: 84e8edf8 (new version)
HandleCount: 1 PointerCount: 1
Examining the OBJECT_HEADER structure on the respective versions of Windows shows the reason for the debugger displaying "old version" and "new version". Following debugger output shows the OBJECT_HEADER on older versions of Windows. The fields below that are highlighted are ones that have been removed from the OBJECT_HEADER structure in Windows 7:
kd> dt nt!_OBJECT_HEADER +0x000 PointerCount : Int4B +0x004 HandleCount : Int4B +0x004 NextToFree : Ptr32 Void +0x008 Type : Ptr32 _OBJECT_TYPE +0x00c NameInfoOffset : UChar +0x00d HandleInfoOffset : UChar +0x00e QuotaInfoOffset : UChar +0x00f Flags : UChar +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : Ptr32 Void +0x014 SecurityDescriptor : Ptr32 Void +0x018 Body : _QUAD
The highlighted fields below are new to the OBJECT_HEADER structure in Windows 7:
kd> dt nt!_OBJECT_HEADER +0x000 PointerCount : Int4B +0x004 HandleCount : Int4B +0x004 NextToFree : Ptr32 Void +0x008 Lock : _EX_PUSH_LOCK +0x00c TypeIndex : UChar +0x00d TraceFlags : UChar +0x00e InfoMask : UChar +0x00f Flags : UChar +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : Ptr32 Void +0x014 SecurityDescriptor : Ptr32 Void +0x018 Body : _QUAD
Per Object Lock
In Windows 7, the new "Lock" field in the OBJECT_HEADER is used to reduce the locking granularity for objects. On older version of Windows, the object manager attempted to acquire an object type specific lock (OBJECT_TYPE->TypeLock) before performing an operation on an object. This implied that no other object of that type in the entire system could be manipulated for the duration the object type lock was held. With Windows 7, due to the availability of the per-object lock (OBJET_HEADER->Lock), manipulating one object does not prevent any other object from being manipulated simultaneously, including objects of the same type. The rest of the changes that have been made to the OBJECT_HEADER structure were required to make room for this "Lock" field without increasing the size of the structure.
Object Type Structure
In older versions of Windows, the OBJECT_HEADER->Type field contained a pointer to the OBJECT_TYPE data structure, which consumed 32/64 bits depending on the platform. In Windows 7, this field has been converted to a CHAR (8-bit) value stored at OBJECT_HEADER->TypeIndex. Instead of pointing directly to the OBJECT_TYPE data structure, the object header now contains an index into a new global data structure nt!ObTypeIndexTable, which is an array of pointers to the different OBJECT_TYPE structures, one of each supported object type. The following command shows the OBJECT_TYPE structure that a object header would point to if the OBJECT_HEADER->TypeIndex is set to 5 :
kd> dt nt!_OBJECT_TYPE poi(nt!ObTypeIndexTable + ( 5 * @$ptrsize )) +0x000 TypeList : _LIST_ENTRY [ 0x84833960 - 0x84833960 ] +0x008 Name : _UNICODE_STRING "Token" +0x010 DefaultObject : 0x82745ba0 Void +0x014 Index : 0x5 '' +0x018 TotalNumberOfObjects : 0x2f3 +0x01c TotalNumberOfHandles : 0xec +0x020 HighWaterNumberOfObjects : 0x2ff +0x024 HighWaterNumberOfHandles : 0xff +0x028 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x078 TypeLock : _EX_PUSH_LOCK +0x07c Key : 0x656b6f54 +0x080 CallbackList : _LIST_ENTRY [ 0x848339e0 - 0x848339e0 ]
Object tracing is a new feature added to windows Vista to collect stack traces during object referencing and dereferencing operations. This helps in debugging reference counting problems. The documentation of the "!objtrace" command, in the debugger help, describes how to enable various object reference tracing options and view them, in the debugger.
In older versions of Windows, the lower 2 bits (i.e. bits 0 and 1) of the OBJECT_HEADER->QuotaInfoOffset field were used to determine if object reference tracing was enabled on the object. Since the OBJECT_HEADER->QuotaInfoOffset is not longer available in Windows 7, as discussed later, the new field OBJECT_HEADER->TraceFlag was added to store the object tracing related flags.
Object Reference Tagging APIs
In Windows 7, the object reference tracing functionality has been enhanced to allow drivers to pass in their own custom tags, to the object referencing and dereferencing APIs. The new "WithTag" versions of all the object reference and dereference APIs have been added as shown below:
Developers can call these APIs instead of the old ones and pass in a 4 character tag that would be stored along with the stack trace when the operation is performed on the object.
Optional Object Headers
In older versions of Windows, the OBJECT_HEADER structure fields NameInfoOffset, HandleInfoOffset and QuotaInfoOffset were used to describe the presence and location of the optional object headers OBJECT_HEADER_NAME_INFO, OBJECT_HEADER_HANDLE_INFO and OBJECT_HEADER_QUOTA_INFO respectively. These xxxInfoOffset fields contain a number (between 0 and 265) that determines the offset of the corresponding OBJECT_HEADER_xxx_INFO structure from the start of the OBJECT_HEADER structure. If any of these fields are zero the object manager assumes that the corresponding header does not exist. There is another optional header, i.e. OBJECT_HEADER_CREATOR_INFO, whose presence is controlled by a bit in the OBJECT_HEADER->Flag field instead of the xxxInfoOffset fields. The following figure shows the layout of the optional object headers and their offsets:
Since all of the above optional headers OBJECT_HEADER_NAME_INFO, OBJECT_HEADER_HANDLE_INFO, OBJECT_HEADER_QUOTA_INFO and OBJECT_HEADER_CREATOR_INFO are fixed in size and they are always stored in a specific order, there is little need to store offsets to these structures. The object manager simply needs to know which ones are present and it can use this information to compute their respective offsets from the start of the OBJECT_HEADER.
So in Windows 7, the 3 xxInfoOffsets fields have been replaced with a single field OBJECT_HEADER->InfoMask, which contains one bit for each optional object header and indicates its presence. In addition to this a new optional header OBJECT_HEADER_PROCESS_INFO has been introduced in Windows 7. The following table shows the bits in the OBJECT_HEADER->InfoMask, the structures they represent, and the size of these structures that are used to compute the offsets:
|Size (on X86)
The order in which the optional object headers are stored in Windows 7 is shown in the following figure:
Depending on the number and position of the bits set in OBJECT_HEADER->InfoMask a number is calculated which serves as an index into the ObpInfoMaskToOffset array. The elements of this array contain the offset of the desired optional header taking into consideration presence of the other optional headers. This array is large enough to accommodate the offsets for all the 25possibilities based on the bits in OBJECT_HEADER->InfoMask.
The pseudo code for retrieving the offset of the desired optional object header for a particular object is Offset = ObpInfoMaskToOffset[OBJECT_HEADER->InfoMask & (DesiredHeaderBit | (DesiredHeaderBit-1))].
For example, if a certain object has the creator, handle and process info header structures, its OBJECT_HEADER->InfoMask value would be 0x15. If the object manager desires to compute the offset to the handle info header structure for this object, the DesiredHeaderBit would be 0x04. Based on the above pseudo code, the computed offset to the handle info header structure would be 0x18 as shown by the following command:
kd> ?? ((unsigned char *)@@masm(nt!ObpInfoMaskToOffset))[0x15 & (0x04 | (0x04-1))] unsigned char 0x18 ''
When changing the object header in Windows 7 Microsoft ensured that they maintained backward compatibility to the extent that the OBJECT_HEADER size did not change and the OBJECT_HEADER still contained all the information that the previous versions of the structure did. The document covered the areas of functionality that were affected by the changes to the object header and described the new functionality that was brought about by the changes in the header.