Windows内核中的Object由三部分组成:可选信息、对象头和对象体。 可选信息有四种: 0: kd> dt _object_header_name_info nt!_OBJECT_HEADER_NAME_INFO +0x000 Directory : Ptr32 _OBJECT_DIRECTORY +0x004 Name : _UNICODE_STRING +0x00c QueryReferences : Uint4B OBJECT_HEADER_HANDLE_INFO(无法在WinDbg中列出信息) OBJECT_HEADER_QUOTA_INFO(无法在WinDbg中列出信息) 0: kd> dt _object_header_creator_info nt!_OBJECT_HEADER_CREATOR_INFO +0x000 TypeList : _LIST_ENTRY +0x008 CreatorUniqueProcess : Ptr32 Void +0x00c CreatorBackTraceIndex : Uint2B +0x00e Reserved : Uint2B 一个Object可以包含以上四种可选信息中的0到4种。 对象头: 0: kd> dt _object_header nt!_OBJECT_HEADER +0x000 PointerCount : Int4B ; 被引用的次数,如ObReferenceObjectByPointer、ObReferenceObjectByHandle等 +0x004 HandleCount : Int4B ; 被打开句柄的次数 +0x004 NextToFree : Ptr32 Void ; 与HandleCount共用存储 +0x008 Type : Ptr32 _OBJECT_TYPE ; 一个Object属于一个OBJECT_TYPE +0x00c NameInfoOffset : UChar ; OBJECT_HEADER_NAME_INFO与OBJECT_HEADER的偏移量,通过OBJECT_HEADER - NameInfoOffset计算出OBJECT_HEADER_NAME_INFO +0x00d HandleInfoOffset : UChar ; 同上 +0x00e QuotaInfoOffset : UChar ; 同上 +0x00f Flags : UChar ; 标识是否有OBJECT_HEADER_CREATOR_INFO、OBJECT_CREATE_INFORMATION等 +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION ; 如果Flags标识有OBJECT_CREATE_INFORMATION则为其指针 +0x010 QuotaBlockCharged : Ptr32 Void ; 如果Flags标识无OBJECT_CREATE_INFORMATION则为QuotaBlockCharged +0x014 SecurityDescriptor : Ptr32 Void ; 由Flags标识 +0x018 Body : _QUAD 对象体: 不同的对象类型有不同的对象体。 0: kd> !object \ Object: e1000400 Type: (89bed488) Directory ObjectHeader: e10003e8 (old version) HandleCount: 0 PointerCount: 40 Directory Object: 00000000 Name: \ 153 symbolic links snapped through this directory "\"是NT命名空间的根目录,!object命令显示了其基本信息:对象头在0xe10003e8,对象体在0xe1000400,对象头的大小为24个字节;对象名为"\",类型为Directory,由于它是根目录因此它的父目录对象Directory Object为NULL。 0: kd> dt _object_header 0xe10003e8 nt!_OBJECT_HEADER +0x000 PointerCount : 0n40 +0x004 HandleCount : 0n0 +0x004 NextToFree : (null) +0x008 Type : 0x89bed488 _OBJECT_TYPE +0x00c NameInfoOffset : 0x10 '' +0x00d HandleInfoOffset : 0 '' +0x00e QuotaInfoOffset : 0 '' +0x00f Flags : 0x32 '2' +0x010 ObjectCreateInfo : 0x00000001 _OBJECT_CREATE_INFORMATION +0x010 QuotaBlockCharged : 0x00000001 Void +0x014 SecurityDescriptor : 0xe10012ed Void +0x018 Body : _QUAD 可以看到对象头之前还有个OBJECT_HEADER_NAME_INFO,!object命令显示的Directory Object和Name就取自这里,如下: 0: kd> dt _object_header_name_info 0xe10003e8-0x10 nt!_OBJECT_HEADER_NAME_INFO +0x000 Directory : (null) +0x004 Name : _UNICODE_STRING "\" +0x00c QueryReferences : 1 0: kd> dt _object_type 0x89bed488 ntdll!_OBJECT_TYPE +0x000 Mutex : _ERESOURCE +0x038 TypeList : _LIST_ENTRY [ 0x89bed4c0 - 0x89bed4c0 ] +0x040 Name : _UNICODE_STRING "Directory" +0x048 DefaultObject : 0x80569c60 Void +0x04c Index : 2 +0x050 TotalNumberOfObjects : 0x20 +0x054 TotalNumberOfHandles : 0x9b +0x058 HighWaterNumberOfObjects : 0x20 +0x05c HighWaterNumberOfHandles : 0xa5 +0x060 TypeInfo : _OBJECT_TYPE_INITIALIZER +0x0ac Key : 0x65726944 +0x0b0 ObjectLocks : [4] _ERESOURCE 尽管不同的对象类型有不同的对象体,但对象类型的结构是一致的,如上,对象类型结构包含了对象类型的名称,记录了这种类型活动对象的总数,以及这种类型的句柄和对象的尖峰数目。TypeInfo域保存了一个OBJECT_TYPE_INITIALIZER的指针,它里面保存了对于该对象类型的所有对象的公共属性,以及一组指向该对象类型的方法的指针: 0: kd> dt _object_type_initializer 0x89bed488+0x60 ntdll!_OBJECT_TYPE_INITIALIZER +0x000 Length : 0x4c +0x002 UseDefaultObject : 0x1 '' +0x003 CaseInsensitive : 0x1 '' +0x004 InvalidAttributes : 0x100 +0x008 GenericMapping : _GENERIC_MAPPING +0x018 ValidAccessMask : 0xf000f +0x01c SecurityRequired : 0 '' +0x01d MaintainHandleCount : 0 '' +0x01e MaintainTypeList : 0 '' +0x020 PoolType : 1 ( PagedPool ) +0x024 DefaultPagedPoolCharge : 0x30 +0x028 DefaultNonPagedPoolCharge : 0xa4 +0x02c DumpProcedure : (null) +0x030 OpenProcedure : (null) +0x034 CloseProcedure : (null) +0x038 DeleteProcedure : (null) +0x03c ParseProcedure : (null) +0x040 SecurityProcedure : 0x80570ea2 long nt!SeDefaultObjectMethod+0 +0x044 QueryNameProcedure : (null) +0x048 OkayToCloseProcedure : (null) 特别值得指出的是对象类型本身也是一个对象,如这里的Directory对象类型,其对象类型Type,如下Directory对象类型的对象其对象类型为Type。 0: kd> !object 0x89bed488 Object: 89bed488 Type: (89bed658) Type ObjectHeader: 89bed470 (old version) HandleCount: 0 PointerCount: 1 Directory Object: e1000268 Name: Directory 由此,Type对象类型的对象类型就是它自己! 下面看一看对象体,这里继续以"\"为例: 0: kd> dt _object_directory 0xe1000400 nt!_OBJECT_DIRECTORY +0x000 HashBuckets : [37] 0xe100c128 _OBJECT_DIRECTORY_ENTRY +0x094 Lock : _EX_PUSH_LOCK +0x098 DeviceMap : (null) +0x09c SessionId : 0xffffffff +0x0a0 Reserved : 0 +0x0a2 SymbolicLinkUsageCount : 0x99 重点注意这里的POBJECT_DIRECTORY_ENTRY HashBuckets[37],它里面保存了一个Directory下的所有对象,以此形成层次结构。下面看看OBJECT_DIRECTORY_ENTRY: 0: kd> dt _object_directory_entry nt!_OBJECT_DIRECTORY_ENTRY +0x000 ChainLink : Ptr32 _OBJECT_DIRECTORY_ENTRY +0x004 Object : Ptr32 Void 可以看到Directory下的所有对象通过一个Hash算法定位其在HashBuckets的哪条链表中,OBJECT_DIRECTORY_ENTRY通过ChainLink组成链表,如果为NULL则表示为该链表最后一个项。让我们看看"\"下的对象: 0: kd> dd 0xe1000400 e1000400 e100c128 e172e998 00000000 e10154b8 e1000410 00000000 e25aec90 e25079e8 00000000 e1000420 00000000 e161d138 e1003078 00000000 e1000430 e2466238 e14fba10 e1d58c58 00000000 e1000440 e14f61f8 00000000 e2ea92b0 e1008468 e1000450 e18c8920 e18d27e0 e18c6260 e1754418 e1000460 e14e5420 e1d52c08 e10020e0 e10084d0 e1000470 00000000 00000000 00000000 e1009430 0: kd> dt _object_directory_entry 0xe100c128 nt!_OBJECT_DIRECTORY_ENTRY +0x000 ChainLink : 0xe101d9b0 _OBJECT_DIRECTORY_ENTRY +0x004 Object : 0xe100b628 Void 0: kd> !object 0xe100b628 Object: e100b628 Type: (89bed488) Directory ObjectHeader: e100b610 (old version) HandleCount: 0 PointerCount: 14 Directory Object: e1000400 Name: ArcName 由此可以看出"\"目录下还有一个"ArcName"目录。 这条链表并没有结束,因为ChainLink为0xe101d9b0。我们再看一下: 0: kd> dt _object_directory_entry 0xe101d9b0 nt!_OBJECT_DIRECTORY_ENTRY +0x000 ChainLink : (null) +0x004 Object : 0x89b386f0 Void 0: kd> !object 0x89b386f0 Object: 89b386f0 Type: (89ba2340) Device ObjectHeader: 89b386d8 (old version) HandleCount: 0 PointerCount: 2 Directory Object: e1000400 Name: Ntfs 由此可以看出"\"目录下还有一个"Ntfs"设备对象。 可以看到Directory类型对象通过哈希表和链表保存其内部的对象,因此我们可以遍历一个Directory中的所有对象,尤其是如果能获得"\"目录对象的话。Windows内核中有个ObpRootDirectoryObject变量保存的正是它的地址! 0: kd> dd ObpRootDirectoryObject l 1 80569c78 e1000400 我们在WinObj中看不到Process、Thread等类型的对象,这是由于任何Directory中都没有它们的对象指针!当然,它们是存在,它们实际上是EPROCESS、ETHREAD等对象。也就是说EPROCESS、ETHREAD等之前有OBJECT_HEADER! 0: kd> !process 0 0 **** NT ACTIVE PROCESS DUMP **** PROCESS 89bb97c0 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000 DirBase: 0a140000 ObjectTable: e1001e38 HandleCount: 856. Image: System 0: kd> !object 89bb97c0 Object: 89bb97c0 Type: (89bb9e38) Process ObjectHeader: 89bb97a8 (old version) HandleCount: 2 PointerCount: 125 最后让我们再回过头来看看Type类型的对象,以此展示下同类对象是如何通过可选的OBJECT_HEADER_CREATOR_INFO链接起来的。如上"\"的类型对象(Directory类型对象)地址为0x89bed488,其对象头则为0x89bed488-0x18=0x89bed470: 0: kd> dt _object_header 0x89bed470 nt!_OBJECT_HEADER +0x000 PointerCount : 0n1 +0x004 HandleCount : 0n0 +0x004 NextToFree : (null) +0x008 Type : 0x89bed658 _OBJECT_TYPE +0x00c NameInfoOffset : 0x20 ' ' +0x00d HandleInfoOffset : 0 '' +0x00e QuotaInfoOffset : 0 '' +0x00f Flags : 0x17 '' +0x010 ObjectCreateInfo : (null) +0x010 QuotaBlockCharged : (null) +0x014 SecurityDescriptor : (null) +0x018 Body : _QUAD Flag标识了该对象含有一个OBJECT_HEADER_CREATOR_INFO可选信息,由于如果OBJECT_HEADER_CREATOR_INFO存在则它一定紧挨着OBJECT_HEADER,因此其地址为0x89bed470-0x10=0x89bed460。OBJECT_HEADER_CREATOR_INFO接口大小为0x10: typedef _OBJECT_HEADER_CREATOR_INFO { /*0x00*/LIST_ENTRY ObjectList; // 链接OBJECT_HEADER_CREATOR_INFO /*0x08*/HANDLE UniqueProcessId; /*0x0C*/WORD Reserved1; /*0x0E*/WORD Reserved2; } OBJECT_HEADER_CREATOR_INFO; 0: kd> dd 0x89bed460 l 4 89bed460 89bed290 89bed630 00000000 00000000 下一个对象的OBJECT_HEADER_CREATOR_INFO为89bed290。那么下一个对象的地址应该为0x89bed290+0x10(OBJECT_HEADER_CREATOR_INFO大小)+0x18(OBJECT_HEADER大小)=0x89bed2b8: 0: kd> !object 0x89bed2b8 Object: 89bed2b8 Type: (89bed658) Type ObjectHeader: 89bed2a0 (old version) HandleCount: 0 PointerCount: 1 Directory Object: e1000268 Name: SymbolicLink 可以看到同为对象类型的SymbolicLink对象在这个链表中,其它的依此类推。