win7对象头那些事儿

http://www.debugman.com/discussion/5239/%E5%8E%9F%E5%88%9Bwin7%E5%8F%AF%E5%8F%98%E5%AF%B9%E8%B1%A1%E5%A4%B4%E7%BB%93%E6%9E%84%E4%B9%8Binfomask%E8%A7%A3%E6%9E%90/

 

对Windows对象管理有一定了解的人都知道,在固定对象头(OBJECT_HEADER)前面是一块可变区域,称为可变对象头,它所包含的结构内容并不固定。在Win7之前,可变区域实际有哪些结构,通常是由OBJECT_HEADER中的几个偏移值指出。如下:
lkd> dt _OBJECT_HEADER(WinXP SP2)
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
其中的NameInfoOffset、HandleInfoOffset、QuotaInfoOffset就是用于指出该结构相对于固定对象头(OBJECT_HEADER)的偏移。如果NtGlobalFlags设置了MaintainTypeList标志,那么由于CreatorInfo的存在,这个部分还会更复杂一点,还得依据OBJECT_HEADER->Flag中的标志位来作一些判断才能具体确定某个结构的具体偏移。在Win7中,这一部分显然经过了精心设计,一个InfoMask域再加一个ObpInfoMaskToOffset表就搞定了,显得更加简捷快速。
顾名思义,InfoMask就是一个掩码,它的每一位表示可变对象头中某个指定的结构是否存在。在Win7中,对象的结构大体上没有太大变化,固定对象头的前面仍然是可变对象头。根据不同类型的对象及实际情况,可变对象头可能包含以下5个结构中的一个或几个:   
    (Size=0x08)ntkrpamp!_OBJECT_HEADER_PROCESS_INFO
    (Size=0x10)ntkrpamp!_OBJECT_HEADER_QUOTA_INFO
    (Size=0x08)ntkrpamp!_OBJECT_HEADER_HANDLE_INFO
    (Size=0x10)ntkrpamp!_OBJECT_HEADER_NAME_INFO
    (Size=0x10)ntkrpamp!_OBJECT_HEADER_CREATOR_INFO
这些结构对应的掩码分别为:
    #define OB_INFOMASK_PROCESS_INFO    0x10
    #define OB_INFOMASK_QUOTA          0x08
    #define OB_INFOMASK_HANDLE        0x04
    #define OB_INFOMASK_NAME          0x02
    #define OB_INFOMASK_CREATOR_INFO    0x01
因为可变对象头可能包含5种结构,每一种结构包括存在或不存在两种情况,那么一共是2^5=32种结果。所以,ObpInfoMaskToOffset表定义如下:
BYTE ObpInfoMaskToOffset[32];


而存在的结构就要占据一定的空间,那么对象头(OBJECT_HEADER)距PoolHeader的偏移就会因结构存在与否产生对应的变化,该偏移实际上也就是可变对象头的总大小。该表的初始化是在ObInitSystem()中进行的。初始化代码如下:
 ULONG i = 0;
 ULONG offset = 0;
    do
    {
      offset = 0;
      if ( i & OB_INFOMASK_CREATOR_INFO )
        offset = sizeof(_OBJECT_HEADER_CREATOR_INFO);
      if ( i & OB_INFOMASK_NAME )
        offset += sizeof(_OBJECT_HEADER_NAME_INFO);
      if ( i & OB_INFOMASK_HANDLE )
        offset += sizeof(_OBJECT_HEADER_HANDLE_INFO);
      if ( i & OB_INFOMASK_QUOTA )
        offset += sizeof(_OBJECT_HEADER_QUOTA_INFO);
      if ( i & OB_INFOMASK_PROCESS_INFO )
        offset += sizeof(_OBJECT_HEADER_PROCESS_INFO);
      ObpInfoMaskToOffset[i++] = offset;
    }while(i<32);

初始化完成后表的内容如下:
kd> db ObpInfoMaskToOffset
83b97e60  00 10 10 20 08 18 18 28-10 20 20 30 18 28 28 38  ... ...(.  0.((8
83b97e70  08 18 18 28 10 20 20 30-18 28 28 38 20 30 30 40  ...(.  0.((8 00@

可以看到,根据掩码不同,那么实际存在的可变头结构就不同,其大小是掩码所代表的有效结构的大小之和。可变头为空,那么偏移自然为0,若5个结构都包含了,那么偏移就是所有结构之和,也就是0x40。所以,根据对象头中的掩码InfoMask,就可以确定可变头部分的大小和具体包含的结构,及每一个结构相对于固定对象头(OBJECT_HEADER)的实际偏移。因为这个偏移是相对于固定对象头(OBJECT_HEADER)往前的偏移,而在初始化过程中,这个偏移的值却又是依次累加的,所以后来加上的结构大小,在整个可变对象头中反而比较靠上,掩码小的结构离固定对象头越近。我以一个明确的图来说明这些信息:
image
举个例子,比如某对象的ObjectHeader->InfoMask值为9,那么就说明它包含了_OBJECT_HEADER_CREATOR_INFO(对应掩码为1)和_OBJECT_HEADER_QUOTA_INFO(对应掩码为8)两个结构,ObpInfoMaskToOffset[9]的值为0x20,正是这两个结构的大小之和。并且,由于掩码小的结构离固定对象头越近,所以可以明确知道ObjectHeader-0x10是_OBJECT_HEADER_CREATOR_INFO结构,ObjectHeader->0x20是_OBJECT_HEADER_QUOTA_INFO结构。
下面我以一个实际的例子来详细说明。
kd> dt _OBJECT_TYPE 84e4aa38 //查看对象类型
nt!_OBJECT_TYPE
   +0x000 TypeList         : _LIST_ENTRY [ [color=#FF0000]0x8603a548 [/color]- 0x863593e8 ] //为了便观察到同类型的所有对象,我设置了MaintainTypeList标志
   +0x008 Name             : _UNICODE_STRING "WindowStation"
   +0x010 DefaultObject    : (null) 
   +0x014 Index            : 0x14 ''
   +0x018 TotalNumberOfObjects : 6
   +0x01c TotalNumberOfHandles : 0x35
   +0x020 HighWaterNumberOfObjects : 6
   +0x024 HighWaterNumberOfHandles : 0x3f
   +0x028 TypeInfo         : _OBJECT_TYPE_INITIALIZER
   +0x078 TypeLock         : _EX_PUSH_LOCK
   +0x07c Key              : 0x646e6957
   +0x080 CallbackList     : _LIST_ENTRY [ 0x84e4aab8 - 0x84e4aab8 ]
kd> !pool 0x8603a548 //观察第一个对象的内存池分配
Pool page 8603a548 region is Nonpaged pool
 8603a500 size:   18 previous size:   48  (Allocated)  MmSi
*[color=#FF0000]8603a518 [/color]size:   b0 previous size:   18  (Allocated) *Wind (Protected)
        Owning component : Unknown (update pooltag.txt)
 8603a5c8 size:   78 previous size:   b0  (Allocated)  EtwR (Protected)
...

可以看到,内存池的分配从8603a518开始。
kd> dd 8603a518 
8603a518  04160003 e46e6957 00000000 00000068
8603a528  00000094 83b44c40 8a42edb8 00000002
8603a538  90ca2cd8 000e000e 90d95cd0 00000000
8603a548  86062f80 84e4aa38 0000016c 00000000
8603a558  0000000c 00000005 00000000 000f00[color=#FF0000]14[/color]
8603a568  83b44c40 90d9c23e 00000000 86022848
8603a578  8604d8c8 91badaa0 00000000 ffabb8e8
8603a588  00000000 00000000 00000000 00000000

根据对象的TypeIndex为0x14,及TypeIndex在OBJECT_HEADER中的偏移,不难看出8603a558就是对象头。
kd> dt _OBJECT_HEADER 8603a558  
nt!_OBJECT_HEADER
   +0x000 PointerCount     : 12
   +0x004 HandleCount      : 5
   +0x004 NextToFree       : 0x00000005 
   +0x008 Lock             : _EX_PUSH_LOCK
   +0x00c TypeIndex        : 0x14 ''
   +0x00d TraceFlags       : 0 ''
   +0x00e InfoMask         : 0xf ''
   +0x00f Flags            : 0 ''
   +0x010 ObjectCreateInfo : 0x83b44c40 _OBJECT_CREATE_INFORMATION
   +0x010 QuotaBlockCharged : 0x83b44c40 
   +0x014 SecurityDescriptor : 0x90d9c23e 
   +0x018 Body             : _QUAD

可以看到,InfoMask为0xF=8+4+2+1,也就是说包含了_OBJECT_HEADER_QUOTA_INFO(掩码为8)、_OBJECT_HEADER_HANDLE_INFO(掩码为4)、_OBJECT_HEADER_NAME_INFO(掩码为2)和_OBJECT_HEADER_CREATOR_INFO(掩码为1)这四个结构。掩码最大的结构_OBJECT_HEADER_QUOTA_INFO在最上面:
kd> dt _OBJECT_HEADER_QUOTA_INFO 8603a520
nt!_OBJECT_HEADER_QUOTA_INFO
   +0x000 PagedPoolCharge  : 0
   +0x004 NonPagedPoolCharge : 0x68
   +0x008 SecurityDescriptorCharge : 0x94
   +0x00c SecurityDescriptorQuotaBlock : 0x83b44c40 
//加上该结构的大小,就是下一个结构_OBJECT_HEADER_HANDLE_INFO
kd> dt _OBJECT_HEADER_HANDLE_INFO 8603a520+10
nt!_OBJECT_HEADER_HANDLE_INFO
   +0x000 HandleCountDataBase : 0x8a42edb8 _OBJECT_HANDLE_COUNT_DATABASE
   +0x000 SingleEntry      : _OBJECT_HANDLE_COUNT_ENTRY
//同理,下一个结构是_OBJECT_HEADER_NAME_INFO
kd> dt _OBJECT_HEADER_NAME_INFO 8603a520+10+8
nt!_OBJECT_HEADER_NAME_INFO
   +0x000 Directory        : 0x90ca2cd8 _OBJECT_DIRECTORY
   +0x004 Name             : _UNICODE_STRING "WinSta0"
   +0x00c ReferenceCount   : 0
//然后是_OBJECT_HEADER_CREATOR_INFO
kd> dt _OBJECT_HEADER_CREATOR_INFO 8603a520+10+8+10
nt!_OBJECT_HEADER_CREATOR_INFO
   +0x000 TypeList         : _LIST_ENTRY [ 0x86062f80 - 0x84e4aa38 ]
   +0x008 CreatorUniqueProcess : 0x0000016c 
   +0x00c CreatorBackTraceIndex : 0
   +0x00e Reserved         : 0
kd> !process 16c 0
Searching for Process with Cid == 16c
Cid Handle table at 93365000 with 553 Entries in use
PROCESS 84e39d40  SessionId: 0  Cid: 016c    Peb: 7ffd4000  ParentCid: 0140
    DirBase: 1ee8a0a0  ObjectTable: 90cae1f8  HandleCount:  75.
    Image: wininit.exe

查看WinSta0的创建者,发现是wininit.exe~~
理解上以上结构,相信对于获取可变对象头中某个结构的位置已经不在话下。实际上,Win7的内核导出了一个函数专门用于获取对象头中的_OBJECT_HEADER_NAME_INFO结构。该函数就是ObQueryNameInfo(),还原成源码如下:
PVOID ObQueryNameInfo(IN PVOID Object)
{
    POBJECT_HEADER ObjectHeader=OBJECT_TO_OBJECT_HEADER(Object);
    BYTE InfoMask=ObjectHeader->InfoMask;
    ULONG NameInfo=0;
    if(InfoMask & OB_INFOMASK_NAME)
    {
        NameInfo=(ULONG)ObjectHeader - ObpInfoMaskToOffset[InfoMask & (OB_INFOMASK_NAME+OB_INFOMASK_CREATOR_INFO)];
    }
    else
    {
        NameInfo=0;
    }
    return (PVOID)NameInfo;
}

源码很好理解,取ObjectHeader->InfoMask,判断OB_INFOMASK_NAME标志位是否有效,若无效说明可变头中并没有这个结构。若有效,就根据掩码取_OBJECT_HEADER_NAME_INFO靠下的两个结构的大小(包括_OBJECT_HEADER_NAME_INFO和_OBJECT_HEADER_CREATOR_INFO两个结构),然后对象头减去这个偏移量,就是_OBJECT_HEADER_NAME_INFO结构的位置了,结合前面的结构图,相信不难理解。
实际上,根据掩码与结构大小的对应关系,完全可以用一个更一般的方式来获取相应的可变对象头结构信息。如下:
PVOID GetSpecificObjectHeaderInfo(PVOID Object,BYTE HeaderMask)
{
    POBJECT_HEADER ObjectHeader=(POBJECT_HEADER)OBJECT_TO_OBJECT_HEADER(Object);
    BYTE InfoMask=ObjectHeader->InfoMask;
    BYTE MaxMask=0;
    ULONG HeaderInfo=0;
    if(InfoMask & HeaderMask)
    {
        MaxMask=2*HeaderMask-1;
        HeaderInfo=(ULONG)ObjectHeader - ObpInfoMaskToOffset[InfoMask & MaxMask];
    }
    else
    {
        HeaderInfo=0;
    }
    return (PVOID)HeaderInfo;
}

参数解释:
    Object : 欲操作的对象
    HeaderMask : 要获取的对象结构对应的掩码
    函数中的ObpInfoMaskToOffset可以自己定义,反正内容和初始化方式已经清楚了,内容一样用起来没什么影响

这样,直接用GetSpecificObjectHeaderInfo(Object,OB_INFOMASK_NAME)就可以代替ObQueryNameInfo(Object)了。
如果要获取_OBJECT_HEADER_HANDLE_INFO结构,那么GetSpecificObjectHeaderInfo(Object,OB_INFOMASK_HANDLE)就可以了,获取其它结构信息也是如此,非常方便~~

关于Win7的可变对象头,就说到这里~~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值