JIURL玩玩Win2k 对象
作者: JIURL
日期: 2003-7-30
Windows 2000 中有如下27种对象 Directory Thread Mutant Controller Type Profile Event SymbolicLink Section EventPair Desktop Timer File WindowStation Driver WmiGuid Token Device IoCompletion Process Adapter Key Job WaitablePort Port Callback Semaphore 。这27个名字就充分说明了对象对于Win2k有多么重要。
图1
先来看看对象的样子,如图1所示。对象至少由对象头和对象体组成,而在对象头之前(比对象头地址更低的地方)还可能有些相关的信息。对于不同类型的对象来说,对象头的定义是一样的,而对象体的大小,内容是完全不一样的。可选信息,对象头,对象体是紧紧的挨在一起的。
也可以使用以下工具,增加对对象的认识。http://www.sysinternals.com 中的 Winobj ,可以观察命名空间中的对象。《 Programming the Microsoft Windows Driver Model 》一书附书CD中的工具 DevView 。这两个工具功能差不多。
对象头及可选结构
OBJECT_HEADER 之前有四种可选结构,OBJECT_NAME,OBJECT_HANDLE_DB,OBJECT_QUOTA_CHARGES ,OBJECT_CREATOR_INFO。他们是否存在,都由 OBJECT_HEADER 中的相关域进行说明。前三种结构如果存在他们的相对于OBJECT_HEADER 的位置由 OBJECT_HEADER 中的相关域进行说明。只有 OBJECT_CREATOR_INFO 结构,如果存在,就位于 OBJECT_HEADER 之前,并紧挨着 OBJECT_HEADER。
OBJECT_HEADER 的定义如下,大小24(十进制)个字节,也就是6个DWORD。
(没有特别注明的话,本文所有的类型定义来自《Undocumented Windows 2000 Secrets: A Programmer's Cookbook》一书的部分章节及其附书源码。由于这部分没有什么官方文档,所以错误不可避免,大家对这些类型定义还是要有一点怀疑)
typedef struct _OBJECT_HEADER
{
/*000*/ DWORD PointerCount; // number of references
/*004*/ DWORD HandleCount; // number of open handles
/*008*/ POBJECT_TYPE ObjectType;
/*00C*/ BYTE NameOffset; // -> OBJECT_NAME
/*00D*/ BYTE HandleDBOffset; // -> OBJECT_HANDLE_DB
/*00E*/ BYTE QuotaChargesOffset; // -> OBJECT_QUOTA_CHARGES
/*00F*/ BYTE ObjectFlags; // OB_FLAG_*
/*010*/ union
{ // OB_FLAG_CREATE_INFO ? ObjectCreateInfo : QuotaBlock
/*010*/ PQUOTA_BLOCK QuotaBlock;
/*010*/ POBJECT_CREATE_INFO ObjectCreateInfo;
/*014*/ };
/*014*/ PSECURITY_DESCRIPTOR SecurityDescriptor;
/*018*/ }
OBJECT_HEADER;
.PointerCount
ntoskrnl.exe 中的 API ObfReferenceObject(), ObReferenceObjectByHandle(), ObReferenceObjectByName(), 和 ObReferenceObjectByPointer() 增加的就是这个PointerCount, ObfDereferenceObject() 和 ObDereferenceObject() 用来减少这个值。
.HandleCount
指明有多少个这个对象的打开句柄
.ObjectType
指向 OBJECT_TYPE 结构体的指针。
.NameOffset
用 OBJECT_HEADER 的地址减去 NameOffset 的值,来定位 OBJECT_NAME 部分。
如果 NameOffset 值为0,表示这个 OBJECT_HEADER 没有 OBJECT_NAME。
用来指出对象的名字,可选信息之一。
.HandleDBOffset
用 OBJECT_HEADER 的地址减去 HandleDBOffset 的值,来定位 OBJECT_HANDLE_DB 部分。
如果 HandleDBOffset 值为0,表示这个 OBJECT_HEADER 没有 OBJECT_HANDLE_DB。
可选信息之一。
.QuotaChargesOffset
用 OBJECT_HEADER 的地址减去 QuotaChargesOffset 的值,来定位 OBJECT_QUOTA_CHARGES 部分。
如果 QuotaChargesOffset 值为0,表示这个 OBJECT_HEADER 没有 OBJECT_QUOTA_CHARGES。
可选信息之一。
.ObjectFlags
#define OB_FLAG_CREATE_INFO 0x01 // has OBJECT_CREATE_INFO
#define OB_FLAG_KERNEL_MODE 0x02 // created by kernel
#define OB_FLAG_CREATOR_INFO 0x04 // has OBJECT_CREATOR_INFO
#define OB_FLAG_EXCLUSIVE 0x08 // OBJ_EXCLUSIVE
#define OB_FLAG_PERMANENT 0x10 // OBJ_PERMANENT
#define OB_FLAG_SECURITY 0x20 // has security descriptor
#define OB_FLAG_SINGLE_PROCESS 0x40 // no HandleDBList
也就是说,第0位,OB_FLAG_CREATE_INFO 位。第1位,OB_FLAG_KERNEL_MODE 位。第2位,OB_FLAG_CREATOR_INFO位。第3位,OB_FLAG_EXCLUSIVE 位。第4位,OB_FLAG_PERMANENT 位。第5位,OB_FLAG_SECURITY 位。第6位,OB_FLAG_SINGLE_PROCESS 位。
如果 OB_FLAG_CREATOR_INFO 位为1,则在 OBJECT_HEADER 之前,紧挨着一个 OBJECT_CREATOR_INFO 结构。
注意 OBJECT_CREATE_INFO 与 OBJECT_CREATOR_INFO 是不同的。
.QuotaBlock 或者 .ObjectCreateInfo
如果 ObjectFlags 的 OB_FLAG_CREATE_INFO 位为1,
则这里为 ObjectCreateInfo ,一个指针,指向一个 OBJECT_CREATE_INFO 结构。OBJECT_CREATE_INFO 为可选信息之一。
否则,这里为 QuotaBlock ,一个指针,指向一个 QUOTA_BLOCK 结构,
提供关于paged 和 non-paged 内存池使用情况的信息。PspDefaultQuotaBlock 一个全局变量,保存着 DefaultQuotaBlock。很多对象的 QuotaBlock 中的地址和 PspDefaultQuotaBlock 一样,指向DefaultQuotaBlock。这个 union 的值可以为 NULL。
.SecurityDescriptor
如果 ObjectFlags 的 OB_FLAG_SECURITY 位为1,
则 SecurityDescriptor 为一个指针,指向一个 SECURITY_DESCRIPTOR 结构。
如果 ObjectFlags 的 OB_FLAG_SECURITY 位为0,
则 SecurityDescriptor 值为 NULL。
OBJECT_NAME 定义如下
typedef struct _OBJECT_NAME
{
/*000*/ POBJECT_DIRECTORY Directory;
/*004*/ UNICODE_STRING Name;
/*00C*/ DWORD Reserved;
/*010*/ }
OBJECT_NAME;
.Directory
指向所在 Directory 对应的 Directory Object。可以为空。
.Name
对象的名字,是一个UNICODE_STRING结构,
UNICODE_STRING结构在ddk中定义如下。
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING *PUNICODE_STRING;
.Reserved
保留
其他的可选结构我们将在后面介绍。
下面我们看一个实际的例子,一个名字叫"\"的Directory 类型对象,它其实就是Directory的根对象。
kd> !object !object Object: 8141ecd0 Type: (81452820) Directory
ObjectHeader: 8141ecb8
HandleCount: 0 PointerCount: 34
Directory Object: 00000000 Name: 120 symbolic links snapped through this directory
HashBucket[ 00 ]: 8141b930 Directory 'ArcName'
HashBucket[ 01 ]: e2f7b600 Port 'SeLsaCommandPort'
HashBucket[ 03 ]: e1007390 Key '\REGISTRY'
HashBucket[ 07 ]: e2bb16e0 Port 'DbgUiApiPort'
HashBucket[ 09 ]: 810e7e00 Directory 'NLS'
HashBucket[ 10 ]: 8141ea50 SymbolicLink 'DosDevices'
HashBucket[ 13 ]: e14088a0 Port 'SeRmCommandPort'
HashBucket[ 14 ]: 810e8540 Mutant 'NlsCacheMutant'
e2fdb4c0 Port 'LsaAuthenticationPort'
81421450 Device 'Dfs'
810fc870 Event 'LanmanServerAnnounceEvent'
HashBucket[ 16 ]: 81416530 Directory 'Driver'
HashBucket[ 17 ]: e17c79c0 Port 'DbgSsApiPort'
HashBucket[ 18 ]: 81437d30 Directory 'WmiGuid'
HashBucket[ 19 ]: 8141b850 Directory 'Device'
HashBucket[ 20 ]: 810f68d0 Directory 'Windows'
HashBucket[ 21 ]: 810a7e70 Event 'SAM_SERVICE_STARTED'
HashBucket[ 22 ]: 810f67f0 Directory 'RPC Control'
e1408aa0 Port 'SmApiPort'
81422af0 Device 'Fat'
HashBucket[ 23 ]: 810e8730 Directory 'BaseNamedObjects'
HashBucket[ 24 ]: 8141eb10 Directory '??'
81416450 Directory 'FileSystem'
HashBucket[ 26 ]: 8141ebf0 Directory 'ObjectTypes'
HashBucket[ 27 ]: 8141ba10 Directory 'Security'
e302e6e0 Port 'ErrorLogPort'
HashBucket[ 31 ]: 8141bbb0 SymbolicLink 'SystemRoot'
HashBucket[ 32 ]: 8141d2d0 Directory 'Callback'
HashBucket[ 33 ]: 810ab330 Event 'EFSInitEvent'
810f7df0 Event 'SeLsaInitEvent'
810ec9d0 Event 'UniqueSessionIdEvent'
HashBucket[ 35 ]: 810f5f50 Directory 'KnownDlls'
\\ 从输出中我们可以看到 ObjectHeader: 8141ecb8 ,ObjectHeader结构大小为24个字节,也就是6个DWORD。
kd> dd 8141ecb8 l 6
dd 8141ecb8 l 6
8141ecb8 00000023 00000000 81452820 32000010
8141ecc8 00000001 e10010f8
\\ 分析一下这个 ObjectHeader 结构的每个域的值
/*000*/ DWORD PointerCount; // number of references
00000023
/*004*/ DWORD HandleCount; // number of open handles
00000000
/*008*/ POBJECT_TYPE ObjectType;
81452820
/*00C*/ BYTE NameOffset; // -> OBJECT_NAME
10
\\ 有 OBJECT_NAME
/*00D*/ BYTE HandleDBOffset; // -> OBJECT_HANDLE_DB
00
\\ 没有 OBJECT_HANDLE_DB
/*00E*/ BYTE QuotaChargesOffset; // -> OBJECT_QUOTA_CHARGES
00
\\ 没有 OBJECT_QUOTA_CHARGES
/*00F*/ BYTE ObjectFlags; // OB_FLAG_*
32
\\ 32(hex)=00110010(bin) ,即 OB_FLAG_KERNEL_MODE,OB_FLAG_PERMANENT,OB_FLAG_SECURITY 这3位被设立。
/*010*/ union
{ // OB_FLAG_CREATE_INFO ? ObjectCreateInfo : QuotaBlock
/*010*/ PQUOTA_BLOCK QuotaBlock;
/*010*/ POBJECT_CREATE_INFO ObjectCreateInfo;
/*014*/ };
00000001
\\ OB_FLAG_CREATE_INFO位没有被设立,但从值来看也不是一个 PQUOTA_BLOCK QuotaBlock。
/*014*/ PSECURITY_DESCRIPTOR SecurityDescriptor;
e10010f8
\\ OB_FLAG_SECURITY 标志位被设立说明了,SecurityDescriptor 不为空。这里是一个PSECURITY_DESCRIPTOR指针,我们将在后面进行讨论
ObjectHeader四个可选信息中 HandleDBOffset,QuotaChargesOffset 值都为0,表示没有相应的结构,NameOffset 值为 10,表示有相应的OBJECT_NAME结构,并且位于 ObjectHeader地址-0x10处。注意 OBJECT_NAME 结构长为0x10。可以看到 OBJECT_NAME 和 ObjectHeader 是紧挨着的。下面我们就来看看 OBJECT_NAME 中的内容。
kd> dd 8141ecb8-10 l 4
dd 8141ecb8-10 l 4
8141eca8 00000000 00040002 81452148 00000000
/*000*/ POBJECT_DIRECTORY Directory;
00000000
// 所在Directory的指针为0,是由于对象"\"是根,不在任何目录下。比如 directory对象\KnownDlls,它的这个值就不为0。我得到的值为 8141ecd0 ,正是 对象"\" 的 ObjectBody 的地址。
/*004*/ UNICODE_STRING Name;
0002 0004 81452148
// UNICODE_STRING 的 PWSTR Buffer 值为 81452148
kd> du 81452148
du 81452148
81452148 "\"
// 可以看到对象的名字叫"\"
/*00C*/ DWORD Reserved;
00000000
对象头的简单分析就到这里,对于不同类型的对象,对象头的格式是一样的。
Directory 类型的对象
现在我们继续分析Directory类型的对象体。不同类型的对象由于用途不同,对象体的结构是完全不一样的,就象图1中的情况那样。某种类型对象体的内容是由该类型的需要所决定的。对于 Directory 类型对象的用途是什么呢?这里的 Directory 指的不是文件系统目录,而是一个对象的目录,从 WinObj 这样的工具我们就能看到这一点。对象目录的组织使用和文件系统目录的组织相似的层次结构。从前面使用kd得到的输出,我们可以看到kd至少可以从Directory类型的对象体中得到下面的信息。
kd> !object !object Object: 8141ecd0 Type: (81452820) Directory
...
HashBucket[ 13 ]: e14088a0 Port 'SeRmCommandPort'
HashBucket[ 14 ]: 810e8540 Mutant 'NlsCacheMutant'
e2fdb4c0 Port 'LsaAuthenticationPort'
81421450 Device 'Dfs'
810fc870 Event 'LanmanServerAnnounceEvent'
HashBucket[ 16 ]: 81416530 Directory 'Driver'
HashBucket[ 17 ]: e17c79c0 Port 'DbgSsApiPort'
...
我们来看 Directory类型的对象体 OBJECT_DIRECTORY 的定义
#define OBJECT_HASH_TABLE_SIZE 37
typedef struct _OBJECT_DIRECTORY
{
/*000*/ POBJECT_DIRECTORY_ENTRY HashTable [OBJECT_HASH_TABLE_SIZE];
/*094*/ POBJECT_DIRECTORY_ENTRY CurrentEntry;
/*098*/ BOOLEAN CurrentEntryValid;
/*099*/ BYTE Reserved1;
/*09A*/ WORD Reserved2;
/*09C*/ DWORD Reserved3;
/*0A0*/ }
OBJECT_DIRECTORY;
typedef struct _OBJECT_DIRECTORY_ENTRY
{
/*000*/ struct _OBJECT_DIRECTORY_ENTRY *NextEntry;
/*004*/ POBJECT Object;
/*008*/ }
OBJECT_DIRECTORY_ENTRY;
从定义中我们看到 OBJECT_DIRECTORY 中主要是一个37项的哈希表。每项是一个指向 OBJECT_DIRECTORY_ENTRY 的指针。指向一个 OBJECT_DIRECTORY_ENTRY 组成的链。OBJECT_DIRECTORY_ENTRY 结构有两个域,第一个域是指向链的下一个OBJECT_DIRECTORY_ENTRY的指针,如果值为空的话,表明链已经结束了。第二域是指向一个某种类型对象的对象体的指针。比如上面例子中 HashBucket[ 16 ]: 81416530 Directory 'Driver' 的链上只有一项,该项的第二个域指向一个Directory类型对象。当有一个新的对象想要放入某个目录下,或者需要根据对象的名字查找对象时,通过哈希函数来确定对象该在哈希表的37个表目中的哪一个链上。这里所做的就是通过对对象名的每一个字符做一定的计算,最后得到一个数,用这个数除以37,取余数,得到了0-36 中的某个数,来确定所在的哈希表目。也就是用哈希函数对对象名计算来确定该对象应该在哈希表中的什么地方。如果想了解那个哈希函数具体算法,可以跟一下 ObpLookupDirectoryEntry 函数。使用 kd? ObpLookupDirectoryEntry 获得该函数的入口地址,然后用SoftICE断该地址。我用SoftICE大概跟了一下,这个函数有3个参数,第一个参数是一个指向OBJECT_DIRECTORY的指针,指定查找的目录。第二个参数是一个指向UNICODE_STRING的指针,指定要查找的名字。我就不详细叙述了。如果只是想找到某个名字的对象的话,可以遍历整个目录的每一个OBJECT_DIRECTORY_ENTRY,这样虽然慢一些,不过如果将来哈希函数有所变化的话,不会受到影响。 OBJECT_DIRECTORY 中使用哈希表的原因是为了加快查找速度。如果不使用哈希表的话,可以每个目录只维护一个对象链表,不过那样的话,根据对象名在某个目录下查找该对象的指针,有可能得遍历该目录下的每个对象。而使用了哈希表,利用哈希函数的计算,将很快确定只在该目录下的一部分对象中遍历。目前目录对象哈希表的项数为37。影响项数的一个因素是通常一个目录下可能会有多少对象,应该尽可能保证链上只有一项,或者链很短,来保证查找的速度。在 kd 中使用 !object 命令观察几个对象目录,可以看到Win2k使用的哈希函数,和37作为哈希表项数,效果还是不错的,对象比较均匀的分布在哈希表中,而且很多哈希表项对应的链上只有一个对象,有多个对象的链也都不是很长。
下面我们来看实际的例子,从前面 kd 的输出
kd> !object !object Object: 8141ecd0 Type: (81452820) Directory
ObjectHeader: 8141ecb8
...
我们看到对象"\"的对象头在 8141ecb8 ,对象体在 8141ecd0,对象头结构大小为0x18,也可以看出对象头和对象体是紧挨在一起的。下面我们来分析目录类型对象"\"的对象体。
kd> dd 8141ecd0 l a0/4
dd 8141ecd0 l a0/4
8141ecd0 e1008c68 e2f7c008 00000000 e10073c8
8141ece0 00000000 00000000 00000000 e2bac088
8141ecf0 00000000 e2bea1a8 e10001e8 00000000
8141ed00 00000000 e13891a8 e2bea328 00000000
8141ed10 e131eb08 e2baf188 e132f208 e1008ba8
8141ed20 e17b6708 e13d4268 e17b65e8 e135a768
8141ed30 e1007f28 00000000 e10004a8 e1007708
8141ed40 00000000 00000000 00000000 e1008b08
8141ed50 e10003a8 e2ffe508 00000000 e17bca68
8141ed60 00000000 8141ecf4 00780001 00000000
前面使用kd得到的输出是
HashBucket[ 00 ]: 8141b930 Directory 'ArcName'
...
HashBucket[ 14 ]: 810e8540 Mutant 'NlsCacheMutant'
e2fdb4c0 Port 'LsaAuthenticationPort'
81421450 Device 'Dfs'
810fc870 Event 'LanmanServerAnnounceEvent'
...
HashBucket[ 35 ]: 810f5f50 Directory 'KnownDlls'
哈希表的第0项值为 e1008c68,是个POBJECT_DIRECTORY_ENTRY
kd> dd e1008c68 l 2
dd e1008c68 l 2
e1008c68 00000000 8141b930
可以看到指向的OBJECT_DIRECTORY_ENTRY的.NextEntry为空,.Object 为 8141b930
用kd看到8141b930果然是一个对象,而且这个对象是一个目录类型的对象,通过它,又可以找到一些对象。
kd> !object 8141b930
!object 8141b930
Object: 8141b930 Type: (81452820) Directory
ObjectHeader: 8141b918
HandleCount: 0 PointerCount: 12
Directory Object: 8141ecd0 Name: ArcName
HashBucket[ 00 ]: 813c4c90 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(3)'
814070d0 SymbolicLink 'multi(0)disk(0)rdisk(0)'
HashBucket[ 03 ]: 813c4c30 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(4)'
HashBucket[ 07 ]: 813c4bd0 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(5)'
HashBucket[ 10 ]: 813c4b70 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(6)'
HashBucket[ 14 ]: 813c4b10 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(7)'
HashBucket[ 17 ]: 813c4ab0 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(8)'
HashBucket[ 21 ]: 813c4a50 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(9)'
HashBucket[ 30 ]: 81420370 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(1)'
HashBucket[ 33 ]: 813e7230 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(2)'
813f9450 SymbolicLink 'multi(0)disk(0)fdisk(0)'
哈希表的第14项值为 e2bea328,是个POBJECT_DIRECTORY_ENTRY
kd> dd e2bea328 l 2
dd e2bea328 l 2
e2bea328 e13fee68 810e8540
kd> !object 810e8540
!object 810e8540
Object: 810e8540 Type: (8141ccc0) Mutant
ObjectHeader: 810e8528
HandleCount: 14 PointerCount: 15
Directory Object: 8141ecd0 Name: NlsCacheMutant
// 链表的第一项中,下一项地址为 e13fee68 ,对象地址为 810e8540
kd> dd e13fee68 l 2
dd e13fee68 l 2
e13fee68 e13c02e8 e2fdb4c0
kd> !object e2fdb4c0
!object e2fdb4c0
Object: e2fdb4c0 Type: (81416e80) Port
ObjectHeader: e2fdb4a8
HandleCount: 1 PointerCount: 29
Directory Object: 8141ecd0 Name: LsaAuthenticationPort
// 链表的第二项中,下一项地址为 e13c02e8 ,对象地址为 e2fdb4c0
kd> dd e13c02e8 l 2
dd e13c02e8 l 2
e13c02e8 e178c908 81421450
kd> !object 81421450
!object 81421450
Object: 81421450 Type: (81416920) Device
ObjectHeader: 81421438
HandleCount: 0 PointerCount: 2
Directory Object: 8141ecd0 Name: Dfs
// 链表的第三项中,下一项地址为 e178c908 ,对象地址为 81421450
kd> dd e178c908 l 2
dd e178c908 l 2
e178c908 00000000 810fc870
kd> !object 810fc870
!object 810fc870
Object: 810fc870 Type: (8141e460) Event
ObjectHeader: 810fc858
HandleCount: 1 PointerCount: 3
Directory Object: 8141ecd0 Name: LanmanServerAnnounceEvent
// 链表的第四项中,下一项地址为空,表示链表结束 ,对象地址为 810fc870
我们看到了目录类型对象中通过哈希表和链表来保存另一些对象的指针,包括目录对象。现在我们只要知道了目录类型对象"\"的地址("\"是根目录,它保存有下一级目录对象的指针)我们就可以找到所有目录下的对象。Win2k中全局变量 ObpRootDirectoryObject 中保存着目录对象"\"的地址。在 Win2k Build 2195 上,这个全局变量的地址为 8046ac24 。可以通过kd得到
kd> ? ObpRootDirectoryObject
? ObpRootDirectoryObject
Evaluate expression: -2142852060 = 8046ac24
dd 8046ac24 l 1
8046ac24 8141ecd0
我们前面已经看到了 8141ecd0 是目录对象"\"的对象体的地址。
我们在WinObj中,看不到任何的 Process, Thread 类型的对象。这是由于任何的 Directory Object 中都没有他们的对象指针。他们当然是存在的,否则就没必要弄个 Process, Thread 类型。他们其实就是 EPROCESS 和 ETHREAD。也就是说,EPROCESS,ETHREAD之前就有OBJECT_HEADER。
kd> !process 8 0
!process 8 0
Searching for Process with Cid == 8
PROCESS 8141e020 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000
DirBase: 00030000 ObjectTable: 81452a68 TableSize: 108.
Image: System
\\ System 进程的 EPROCESS 地址为 8141e020
kd> !object 8141e020
!object 8141e020
Object: 8141e020 Type: (814524e0) Process
ObjectHeader: 8141e008
HandleCount: 2 PointerCount: 36
kd> dd 8141e008 l 6
dd 8141e008 l 6
8141e008 00000024 00000002 814524e0 22000000
8141e018 804699c0 e1000618
很多的对象都不放在任何目录下。
关于 Directory Object 就介绍到这里。
Type 类型的对象
在前面介绍 OBJECT_HEADER 的时候,我们没有分析其中的 /*008*/ POBJECT_TYPE ObjectType; 这个域。这是一个非常重要的域,它是一个指针,指向包含对象类型的信息。前面例子中
kd> !object !object Object: 8141ecd0 Type: (81452820) Directory
...
kd> dd 8141ecb8 l 6
dd 8141ecb8 l 6
8141ecb8 00000023 00000000 81452820 32000010
8141ecc8 00000001 e10010f8
kd 可以知道"\"是 Directory 类型的对象,就是通过 POBJECT_TYPE ObjectType找到相关结构,获得的信息。
实际上 OBJECT_TYPE 也是一个对象,它就是 Type 类型的对象。它也象图1中显示的那样,有对象头,对象体和可选信息。
我们看到了"\"的 Type Object 地址为 81452820 ,我们现在先分析一下这个 Type Object 的头。
kd> !object 81452820
!object 81452820
Object: 81452820 Type: (81452920) Type
ObjectHeader: 81452808
HandleCount: 0 PointerCount: 1
Directory Object: 8141ebf0 Name: Directory
头部应该在 0x81452820-0x18=0x81452808 处(原因是头部紧挨着对象体,头部在前,头部大小0x18个字节)从 kd 的输出中,看到 kd 也是这么认为的。
kd> dd 81452808 l 6
dd 81452808 l 6
81452808 00000001 00000000 81452920 17000020
81452818 00000000 00000000
/*000*/ DWORD PointerCount; // number of references
00000001
/*004*/ DWORD HandleCount; // number of open handles
00000000
/*008*/ POBJECT_TYPE ObjectType;
81452920
/*00C*/ BYTE NameOffset; // -> OBJECT_NAME
20
/*00D*/ BYTE HandleDBOffset; // -> OBJECT_HANDLE_DB
00
/*00E*/ BYTE QuotaChargesOffset; // -> OBJECT_QUOTA_CHARGES
00
/*00F*/ BYTE ObjectFlags; // OB_FLAG_*
17
17(hex)=00010111(bin) OB_FLAG_CREATE_INFO OB_FLAG_KERNEL_MODE OB_FLAG_CREATOR_INFO OB_FLAG_PERMANENT
/*010*/ union
{ // OB_FLAG_CREATE_INFO ? ObjectCreateInfo : QuotaBlock
/*010*/ PQUOTA_BLOCK QuotaBlock;
/*010*/ POBJECT_CREATE_INFO ObjectCreateInfo;
/*014*/ };
00000000
/*014*/ PSECURITY_DESCRIPTOR SecurityDescriptor;
00000000
看到存在 OBJECT_NAME,并且在偏移20处,也就是0x81452808-0x20处。
kd> dd 81452808-20 l 4
dd 81452808-20 l 4
814527e8 8141ebf0 00140012 e1001948 00000000
// 所在目录的对象地址为 8141ebf0
kd> !object 8141ebf0
!object 8141ebf0
Object: 8141ebf0 Type: (81452820) Directory
...
// 和从WinObj 中看到的一样是Directory类型对象"Type"
kd> du e1001948
du e1001948
e1001948 "Directory"
// 类型对象的名字叫 "Directory"
OBJECT_HEADER中的标志OB_FLAG_CREATOR_INFO被设立,表明 OBJECT_CREATOR_INFO 结构存在,这个可选结构不像其他三个需要指明偏移,这个结构如果存在就一定位于 OBJECT_HEADER 之前,并紧挨着 OBJECT_HEADER。
OBJECT_CREATOR_INFO 结构定义如下
typedef struct _OBJECT_CREATOR_INFO
{
/*000*/ LIST_ENTRY ObjectList; // OBJECT_CREATOR_INFO
/*008*/ HANDLE UniqueProcessId;
/*00C*/ WORD Reserved1;
/*00E*/ WORD Reserved2;
/*010*/ }
OBJECT_CREATOR_INFO;
typedef struct _LIST_ENTRY {//from ntdef.h
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
.ObjectList 同一类型的对象被这个双向链表链在一起。
.UniqueProcessId 为创建这个对象的进程ID。
// 由于在对象头之前,并紧挨着对象头,大小为0x10字节,所以
kd> dd 81452808-10 l 4
dd 81452808-10 l 4
814527f8 814526f8 814528f8 00000000 00000000
/*000*/ LIST_ENTRY ObjectList; // OBJECT_CREATOR_INFO
814526f8 814528f8
/*008*/ HANDLE UniqueProcessId;
00000000
我们用 kd 遍历一下 ObjectList
kd> dd 814527f8 l 4
dd 814527f8 l 4
814527f8 814526f8 814528f8 00000000 00000000
// 814527f8 就是 81452808-10
kd> ? 814526f8+28
? 814526f8+28
Evaluate expression: -2126174432 = 81452720
// 由于 OBJECT_CREATOR_INFO 大小 0x10,紧跟着 OBJECT_HEADER 大小 0x18,紧跟着的才是对象体
而 !object 的参数是对象体的指针
kd> !object 81452720
!object 81452720
Object: 81452720 Type: (81452920) Type
ObjectHeader: 81452708
HandleCount: 0 PointerCount: 1
Directory Object: 8141ebf0 Name: SymbolicLink
// 可以看到链上的下一项是Type Object "SymbolicLink",可见ObjectList是同一类型的对象的链
kd> dd 814526f8 l 4
dd 814526f8 l 4
814526f8 814525f8 814527f8 00000000 00000000
// 我们顺着LIST_ENTRY 的Flink 一直走下去,直到遇到814527f8,表明已经循环了。
kd> dd 814525f8 l 4
dd 814525f8 l 4
814525f8 814524b8 814526f8 00000000 00000000
kd> dd 814524b8 l 4
dd 814524b8 l 4
814524b8 814523b8 814525f8 00000000 00000000
kd> dd 814523b8 l 4
dd 814523b8 l 4
814523b8 814522b8 814524b8 00000000 00000000
kd> dd 814522b8 l 4
dd 814522b8 l 4
814522b8 8141e438 814523b8 00000000 00000000
kd> dd 8141e438 l 4
dd 8141e438 l 4
8141e438 8141e338 814522b8 00000008 00000000
kd> dd 8141e338 l 4
dd 8141e338 l 4
8141e338 8141cc98 8141e438 00000008 00000000
kd> dd 8141cc98 l 4
dd 8141cc98 l 4
8141cc98 8141cb98 8141e338 00000008 00000000
kd> dd 8141cb98 l 4
dd 8141cb98 l 4
8141cb98 8141c878 8141cc98 00000008 00000000
kd> dd 8141c878 l 4
dd 8141c878 l 4
8141c878 8141c778 8141cb98 00000008 00000000
kd> dd 8141c778 l 4
dd 8141c778 l 4
8141c778 8141c678 8141c878 00000008 00000000
kd> dd 8141c678 l 4
dd 8141c678 l 4
8141c678 8141c578 8141c778 00000008 00000000
kd> dd 8141c578 l 4
dd 8141c578 l 4
8141c578 8141c478 8141c678 00000008 00000000
kd> dd 8141c478 l 4
dd 8141c478 l 4
8141c478 8141b738 8141c578 00000008 00000000
kd> dd 8141b738 l 4
dd 8141b738 l 4
8141b738 8141b098 8141c478 00000008 00000000
kd> dd 8141b098 l 4
dd 8141b098 l 4
8141b098 81416e58 8141b738 00000008 00000000
kd> dd 81416e58 l 4
dd 81416e58 l 4
81416e58 81416d58 8141b098 00000008 00000000
kd> dd 81416d58 l 4
dd 81416d58 l 4
81416d58 81416af8 81416e58 00000008 00000000
kd> dd 81416af8 l 4
dd 81416af8 l 4
81416af8 814169f8 81416d58 00000008 00000000
kd> dd 814169f8 l 4
dd 814169f8 l 4
814169f8 814168f8 81416af8 00000008 00000000
kd> dd 814168f8 l 4
dd 814168f8 l 4
814168f8 814167f8 814169f8 00000008 00000000
kd> dd 814167f8 l 4
dd 814167f8 l 4
814167f8 814166f8 814168f8 00000008 00000000
kd> dd 814166f8 l 4
dd 814166f8 l 4
814166f8 814165f8 814167f8 00000008 00000000
kd> dd 814165f8 l 4
dd 814165f8 l 4
814165f8 814379b8 814166f8 00000008 00000000
kd> dd 814379b8 l 4
dd 814379b8 l 4
814379b8 81452958 814165f8 00000008 003f0000
kd> dd 81452958 l 4
dd 81452958 l 4
81452958 814528f8 814379b8 000a0008 e1000ce8
kd> dd 814528f8 l 4
dd 814528f8 l 4
814528f8 814527f8 81452958 00000000 00000000
kd> dd 814527f8 l 4
dd 814527f8 l 4
814527f8 814526f8 814528f8 00000000 00000000
// 遇到814527f8,表明已经循环了。
下面我们看一下 Type Object 的对象体
typedef struct _OBJECT_TYPE
{
/*000*/ ERESOURCE Lock;
/*038*/ LIST_ENTRY ObjectListHead; // OBJECT_CREATOR_INFO
/*040*/ UNICODE_STRING ObjectTypeName; // see above
/*048*/ union
{
/*048*/ PVOID DefaultObject; // ObpDefaultObject
/*048*/ DWORD Code; // File: 5C, WaitablePort: A0
};
/*04C*/ DWORD ObjectTypeIndex; // OB_TYPE_INDEX_*
/*050*/ DWORD ObjectCount;
/*054*/ DWORD HandleCount;
/*058*/ DWORD PeakObjectCount;
/*05C*/ DWORD PeakHandleCount;
/*060*/ OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
/*0AC*/ DWORD ObjectTypeTag; // OB_TYPE_TAG_*
/*0B0*/ }
OBJECT_TYPE;
// 地址 81452820,OBJECT_TYPE 结构长0xB0个字节
kd> dd 81452820 l b0/4
dd 81452820 l b0/4
81452820 81452720 81452920 00000000 00000000
81452830 00000000 00000000 00000000 00000000
81452840 00000000 00000000 00000000 00000000
81452850 00000000 00000000 81452858 81452858
81452860 00140012 e1001948 00000000 00000002
81452870 00000018 0000002d 00000018 00000032
81452880 0000004c 00000100 00020003 0002000c
81452890 00020003 000f000f 000f000f 00000000
814528a0 00000000 00000000 000000d0 00000000
814528b0 00000000 00000000 00000000 00000000
814528c0 804bfb34 00000000 00000000 65726944
/*000*/ ERESOURCE Lock;
81452820 81452720 81452920 00000000 00000000
81452830 00000000 00000000 00000000 00000000
81452840 00000000 00000000 00000000 00000000
81452850 00000000 00000000
/*038*/ LIST_ENTRY ObjectListHead; // OBJECT_CREATOR_INFO
81452858 81452858
/*040*/ UNICODE_STRING ObjectTypeName;
0012 0014 e1001948
// 看看 ObjectTypeName 中所指的字符串吧,
kd> du e1001948
du e1001948
e1001948 "Directory"
// 它是 "Directory"
/*048*/ PVOID DefaultObject; // ObpDefaultObject
/*048*/ DWORD Code; // File: 5C, WaitablePort: A0
00000000
/*04C*/ DWORD ObjectTypeIndex; // OB_TYPE_INDEX_*
00000002
// #define OB_TYPE_INDEX_DIRECTORY 2 // [Dire] "Directory"
系统应该是由这里来判断类型的,类型名只是给人看的。其他类型的定义将在后面列出。
/*050*/ DWORD ObjectCount;
00000018
/*054*/ DWORD HandleCount;
00000038
/*058*/ DWORD PeakObjectCount;
00000018
/*05C*/ DWORD PeakHandleCount;
00000044
/*060*/ OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
81452880 0000004c 00000100 00020003 0002000c
81452890 00020003 000f000f 000f000f 00000000
814528a0 00000000 00000000 000000d0 00000000
814528b0 00000000 00000000 00000000 00000000
814528c0 804bfb34 00000000 00000000
/*0AC*/ DWORD ObjectTypeTag; // OB_TYPE_TAG_*
65726944
// 就是ascii的"eriD" ,#define OB_TYPE_TAG_DIRECTORY 'eriD' // [Dire] "Directory"
其他类型的定义将在后面列出。
OBJECT_TYPE_INITIALIZER 结构定义如下
typedef struct _OBJECT_TYPE_INITIALIZER
{
/*000*/ WORD Length; //0x004C
/*002*/ BOOLEAN UseDefaultObject;//OBJECT_TYPE.DefaultObject
/*003*/ BOOLEAN Reserved1;
/*004*/ DWORD InvalidAttributes;
/*008*/ GENERIC_MAPPING GenericMapping;
/*018*/ ACCESS_MASK ValidAccessMask;
/*01C*/ BOOLEAN SecurityRequired;
/*01D*/ BOOLEAN MaintainHandleCount; // OBJECT_HANDLE_DB
/*01E*/ BOOLEAN MaintainTypeList; // OBJECT_CREATOR_INFO
/*01F*/ BYTE Reserved2;
/*020*/ BOOL PagedPool;
/*024*/ DWORD DefaultPagedPoolCharge;
/*028*/ DWORD DefaultNonPagedPoolCharge;
/*02C*/ NTPROC DumpProcedure;
/*030*/ NTPROC OpenProcedure;
/*034*/ NTPROC CloseProcedure;
/*038*/ NTPROC DeleteProcedure;
/*03C*/ NTPROC_VOID ParseProcedure;
/*040*/ NTPROC_VOID SecurityProcedure; // SeDefaultObjectMethod
/*044*/ NTPROC_VOID QueryNameProcedure;
/*048*/ NTPROC_BOOLEAN OkayToCloseProcedure;
/*04C*/ }
OBJECT_TYPE_INITIALIZER;
/*000*/ WORD Length; //0x004C
004c
/*002*/ BOOLEAN UseDefaultObject;//OBJECT_TYPE.DefaultObject
00
/*003*/ BOOLEAN Reserved1;
00
/*004*/ DWORD InvalidAttributes;
00000100
/*008*/ GENERIC_MAPPING GenericMapping;
struct _GENERIC_MAPPING (sizeof=16)
+00 uint32 GenericRead
+04 uint32 GenericWrite
+08 uint32 GenericExecute
+0c uint32 GenericAll
00020003 0002000c
/*018*/ ACCESS_MASK ValidAccessMask;
typedef ULONG ACCESS_MASK;
00020003
/*01C*/ BOOLEAN SecurityRequired;
0f
/*01D*/ BOOLEAN MaintainHandleCount; // OBJECT_HANDLE_DB
00
/*01E*/ BOOLEAN MaintainTypeList; // OBJECT_CREATOR_INFO
0f
/*01F*/ BYTE Reserved2;
00
/*020*/ BOOL PagedPool;
00000000
/*024*/ DWORD DefaultPagedPoolCharge;
00000000
/*028*/ DWORD DefaultNonPagedPoolCharge;
000000d0
/*02C*/ NTPROC DumpProcedure;
00000000
/*030*/ NTPROC OpenProcedure;
00000000
/*034*/ NTPROC CloseProcedure;
00000000
/*038*/ NTPROC DeleteProcedure;
00000000
/*03C*/ NTPROC_VOID ParseProcedure;
00000000
/*040*/ NTPROC_VOID SecurityProcedure; // SeDefaultObjectMethod
804bfb34
kd> u 804bfb34
u 804bfb34
ntoskrnl!SeDefaultObjectMethod:
/*044*/ NTPROC_VOID QueryNameProcedure;
00000000
/*048*/ NTPROC_BOOLEAN OkayToCloseProcedure;
00000000
可以看到 Type Object 中的 OBJECT_TYPE_INITIALIZER 中有用于保存了函数例程的地方。
而关于这些函数概括性的介绍可以参考 《Inside Windows 2000 Third Edition》"Chapter 3 System Mechanisms" 中的 "Object Manager" 中的 Object Methods 部分。
Type Object 的对象体的描述就到这里。
对于 Type Object 还有几个问题需要注意
对于同一个类型的对象,他们的对象头中指向类型的指针,指到的是同一个地址。
比如,对于 Directory 类型的对象,他们指向类型的指针都指向 Type Object "Directory"
kd> !object \Device\Harddiskdmvolumes
!object \Device\Harddiskdmvolumes
Object: 813d2890 Type: (81452820) Directory
\\ 注意对象 \Device\Harddiskdmvolumes 的类型指针指向 81452820
kd> !object \Device\Harddiskdmvolumes\physicaldmvolumes
!object \Device\Harddiskdmvolumes\physicaldmvolumes
Object: 813d26d0 Type: (81452820) Directory
\\ 注意对象 \Device\Harddiskdmvolumes\physicaldmvolumes 的类型指针指向 81452820
所以,如果没有新的类型加入的话,类型对象一共就27个。
所有的 Type Object 的 OBJECT_HEADER 中的 ObjectType 都指向名字叫"Type"的 Type Object。
名字叫"Type"的 Type Object,也是Type Object,所以它的OBJECT_HEADER 中的 ObjectType 指向自己。
kd> !object 81452920
!object 81452920
Object: 81452920 Type: (81452920) Type
ObjectHeader: 81452908
HandleCount: 0 PointerCount: 1
Directory Object: 8141ebf0 Name: Type
就是说所有的类型对象的对象头中的 ObjectType 指针,指向27个类型对象中的叫"Type"的那个。而叫"Type"的那个类型对象的对象头中的 ObjectType 指针,指向自己。
所有的类型对象被放在了 \ObjectTypes\ 下,我们可以在"\"目录对象的结构中找"ObjectTypes"目录对象,来得到所有的类型对象的指针。也可以通过一个叫 ObpTypeDirectoryObject 的全局变量,这个全局变量中放着"ObjectTypes"目录对象的地址。使用第一种方法保险一些。
下面列出 ObjectTypeIndex 和 ObjectTypeTag
#define OB_TYPE_INDEX_TYPE 1 // [ObjT] "Type"
#define OB_TYPE_INDEX_DIRECTORY 2 // [Dire] "Directory"
#define OB_TYPE_INDEX_SYMBOLIC_LINK 3 // [Symb] "SymbolicLink"
#define OB_TYPE_INDEX_TOKEN 4 // [Toke] "Token"
#define OB_TYPE_INDEX_PROCESS 5 // [Proc] "Process"
#define OB_TYPE_INDEX_THREAD 6 // [Thre] "Thread"
#define OB_TYPE_INDEX_JOB 7 // [Job ] "Job"
#define OB_TYPE_INDEX_EVENT 8 // [Even] "Event"
#define OB_TYPE_INDEX_EVENT_PAIR 9 // [Even] "EventPair"
#define OB_TYPE_INDEX_MUTANT 10 // [Muta] "Mutant"
#define OB_TYPE_INDEX_CALLBACK 11 // [Call] "Callback"
#define OB_TYPE_INDEX_SEMAPHORE 12 // [Sema] "Semaphore"
#define OB_TYPE_INDEX_TIMER 13 // [Time] "Timer"
#define OB_TYPE_INDEX_PROFILE 14 // [Prof] "Profile"
#define OB_TYPE_INDEX_WINDOW_STATION 15 // [Wind] "WindowStation"
#define OB_TYPE_INDEX_DESKTOP 16 // [Desk] "Desktop"
#define OB_TYPE_INDEX_SECTION 17 // [Sect] "Section"
#define OB_TYPE_INDEX_KEY 18 // [Key ] "Key"
#define OB_TYPE_INDEX_PORT 19 // [Port] "Port"
#define OB_TYPE_INDEX_WAITABLE_PORT 20 // [Wait] "WaitablePort"
#define OB_TYPE_INDEX_ADAPTER 21 // [Adap] "Adapter"
#define OB_TYPE_INDEX_CONTROLLER 22 // [Cont] "Controller"
#define OB_TYPE_INDEX_DEVICE 23 // [Devi] "Device"
#define OB_TYPE_INDEX_DRIVER 24 // [Driv] "Driver"
#define OB_TYPE_INDEX_IO_COMPLETION 25 // [IoCo] "IoCompletion"
#define OB_TYPE_INDEX_FILE 26 // [File] "File"
#define OB_TYPE_INDEX_WMI_GUID 27 // [WmiG] "WmiGuid"
#define OB_TYPE_TAG_TYPE 'TjbO' // [ObjT] "Type"
#define OB_TYPE_TAG_DIRECTORY 'eriD' // [Dire] "Directory"
#define OB_TYPE_TAG_SYMBOLIC_LINK 'bmyS' // [Symb] "SymbolicLink"
#define OB_TYPE_TAG_TOKEN 'ekoT' // [Toke] "Token"
#define OB_TYPE_TAG_PROCESS 'corP' // [Proc] "Process"
#define OB_TYPE_TAG_THREAD 'erhT' // [Thre] "Thread"
#define OB_TYPE_TAG_JOB ' boJ' // [Job ] "Job"
#define OB_TYPE_TAG_EVENT 'nevE' // [Even] "Event"
#define OB_TYPE_TAG_EVENT_PAIR 'nevE' // [Even] "EventPair"
#define OB_TYPE_TAG_MUTANT 'atuM' // [Muta] "Mutant"
#define OB_TYPE_TAG_CALLBACK 'llaC' // [Call] "Callback"
#define OB_TYPE_TAG_SEMAPHORE 'ameS' // [Sema] "Semaphore"
#define OB_TYPE_TAG_TIMER 'emiT' // [Time] "Timer"
#define OB_TYPE_TAG_PROFILE 'forP' // [Prof] "Profile"
#define OB_TYPE_TAG_WINDOW_STATION 'dniW' // [Wind] "WindowStation"
#define OB_TYPE_TAG_DESKTOP 'kseD' // [Desk] "Desktop"
#define OB_TYPE_TAG_SECTION 'tceS' // [Sect] "Section"
#define OB_TYPE_TAG_KEY ' yeK' // [Key ] "Key"
#define OB_TYPE_TAG_PORT 'troP' // [Port] "Port"
#define OB_TYPE_TAG_WAITABLE_PORT 'tiaW' // [Wait] "WaitablePort"
#define OB_TYPE_TAG_ADAPTER 'padA' // [Adap] "Adapter"
#define OB_TYPE_TAG_CONTROLLER 'tnoC' // [Cont] "Controller"
#define OB_TYPE_TAG_DEVICE 'iveD' // [Devi] "Device"
#define OB_TYPE_TAG_DRIVER 'virD' // [Driv] "Driver"
#define OB_TYPE_TAG_IO_COMPLETION 'oCoI' // [IoCo] "IoCompletion"
#define OB_TYPE_TAG_FILE 'eliF' // [File] "File"
#define OB_TYPE_TAG_WMI_GUID 'GimW' // [WmiG] "WmiGuid"
对 Type 类型的对象就介绍到这里。
其他
可选结构之一的 OBJECT_HANDLE_DB
OBJECT_HANDLE_DB 结构定义如下
typedef struct _OBJECT_HANDLE_DB
{
/*000*/ union
{
/*000*/ struct _EPROCESS *Process;
/*000*/ struct _OBJECT_HANDLE_DB_LIST *HandleDBList;
/*004*/ };
/*004*/ DWORD HandleCount;
/*008*/ }
OBJECT_HANDLE_DB;
typedef struct _OBJECT_HANDLE_DB_LIST
{
/*000*/ DWORD Count;
/*004*/ OBJECT_HANDLE_DB Entries [];
/*???*/ };
如果头中的 OB_FLAG_SINGLE_PROCESS 标志位被设立,则 union 中的 .Process 有效,指向一个EPROCESS。如果有多个进程都拥有本对象的句柄(handle),那么OB_FLAG_SINGLE_PROCESS 标志位不会被设立,则 union 中的 .HandleDBList 有效。
kd> !object \Windows\WindowStations\WinSta0
!object \Windows\WindowStations\WinSta0
Object: 810c9398 Type: (8141c5a0) WindowStation
ObjectHeader: 810c9380
HandleCount: 29 PointerCount: 46
Directory Object: 810e5930 Name: WinSta0
\\ 对象头地址为 810c9380 ,对象头中 +c 开始的4个字节分别是 NameOffset HandleDBOffset \\ QuotaChargesOffset ObjectFlags
kd> db 810c9380+c l 4
db 810c9380+c l 4
810c938c 10 18 00 20
\\ HandleDBOffset 不为0,表明存在 OBJECT_HANDLE_DB ,并且偏移为 18,即810c9380-18=810c9368,大小为8个字节。NameOffset 不为0,偏移为10。ObjectFlags为 20(hex)=00100000(bin) 即 OB_FLAG_SECURITY, 标志位 OB_FLAG_SINGLE_PROCESS 没有被设立,表明 OBJECT_HANDLE_DB 的union 中的 .HandleDBList 有效。
kd> dd 810c9380-18 l 2
dd 810c9380-18 l 2
810c9368 e308ac48 00000002
/*000*/ struct _OBJECT_HANDLE_DB_LIST *HandleDBList;
e308ac48
/*004*/ DWORD HandleCount;
00000002
\\ OBJECT_HANDLE_DB_LIST 的 Count
kd> dd e308ac48 l 1
dd e308ac48 l 1
e308ac48 00000016
\\ 有0x16 项
kd> dd e308ac48+4 l 16*2
dd e308ac48+4 l 16*2
e308ac4c 810cc740 00000002 00000000 00000000
e308ac5c 81054020 00000002 833eb1c0 00000002
e308ac6c 81f9f020 00000002 824c65e0 00000002
e308ac7c 80e68100 00000002 00000000 00000000
e308ac8c 84f7a7e0 00000002 810e8020 00000002
e308ac9c 00000000 00000000 00000000 00000000
e308acac 00000000 00000000 00000000 00000000
e308acbc 8352a620 00000002 00000000 00000000
e308accc 8475f020 00000002 830f0a00 00000002
e308acdc 83084b00 00000002 817cb3c0 00000001
e308acec 83589020 00000002 8345a440 00000002
\\ 试一下第一项中的指向EPROCESS的指针。
kd> !process 810cc740 0
!process 810cc740 0
PROCESS 810cc740 SessionId: 0 Cid: 00bc Peb: 7ffdf000 ParentCid: 008c
DirBase: 075a7000 ObjectTable: 810cc6a8 TableSize: 357.
Image: winlogon.exe
\\ 确实是指向一个EPROCESS结构
可选结构之一的 OBJECT_QUOTA_CHARGES
OBJECT_QUOTA_CHARGES 结构定义如下
#define OB_SECURITY_CHARGE 0x00000800
typedef struct _OBJECT_QUOTA_CHARGES
{
/*000*/ DWORD PagedPoolCharge;
/*004*/ DWORD NonPagedPoolCharge;
/*008*/ DWORD SecurityCharge;
/*00C*/ DWORD Reserved;
/*010*/ }
OBJECT_QUOTA_CHARGES;
当对象头中的标志位 OB_FLAG_CREATE_INFO 为0时,头中的 PQUOTA_BLOCK QuotaBlock 有效。
struct _EPROCESS_QUOTA_BLOCK (sizeof=44)
+00 uint32 QuotaLock
+04 uint32 ReferenceCount
+08 uint32 QuotaPeakPoolUsage[2]
+10 uint32 QuotaPoolUsage[2]
+18 uint32 QuotaPoolLimit[2]
+20 uint32 PeakPagefileUsage
+24 uint32 PagefileUsage
+28 uint32 PagefileLimit
kd> !object \KnownDlls\user32.dll
!object \KnownDlls\user32.dll
Object: e17c29e0 Type: (8141b760) Section
ObjectHeader: e17c29c8
HandleCount: 0 PointerCount: 1
Directory Object: 810f63d0 Name: user32.dll
\\ 对象头地址为 e17c29c8 ,对象头中 +c 开始的4个字节分别是 NameOffset HandleDBOffset
\\ QuotaChargesOffset ObjectFlags
kd> db e17c29c8+c l 4
db e17c29c8+c l 4
e17c29d4 10 00 20 10
\\ QuotaChargesOffset 不为0,表明存在 OBJECT_QUOTA_CHARGES ,并且偏移为 20,即e17c29c8-20=e17c29a8,大小为8个字节。NameOffset 不为0,偏移为10。ObjectFlags为 10(hex)=00010000(bin) 即 OB_FLAG_PERMANENT,OB_FLAG_CREATE_INFO 位为0。
\\ OBJECT_QUOTA_CHARGES
kd> dd e17c29c8-20 l 4
dd e17c29c8-20 l 4
e17c29a8 000001b8 000000d8 00000800 00000000
/*000*/ DWORD PagedPoolCharge;
000001b8
/*004*/ DWORD NonPagedPoolCharge;
000000d8
/*008*/ DWORD SecurityCharge;
00000800
/*00C*/ DWORD Reserved;
00000000
\\ 头地址在 e17c29c8,头中 QuotaBlock 值为 804699c0
kd> dd e17c29c8 l 6
dd e17c29c8 l 6
e17c29c8 00000001 00000000 8141b760 10200010
e17c29d8 804699c0 e17bb8d8
\\EPROCESS_QUOTA_BLOCK
kd> dd 804699c0 l 2c/4
dd 804699c0 l 2c/4
804699c0 00000000 000006b9 0008395c 001499e0
804699d0 0007eebc 00139f6c ffffffff ffffffff
804699e0 0000106e 00000f16 ffffffff
+00 uint32 QuotaLock
00000000
+04 uint32 ReferenceCount
000006b9
+08 uint32 QuotaPeakPoolUsage[2]
0008395c 001499e0
+10 uint32 QuotaPoolUsage[2]
0007eebc 00139f6c
+18 uint32 QuotaPoolLimit[2]
ffffffff ffffffff
+20 uint32 PeakPagefileUsage
0000106e
+24 uint32 PagefileUsage
00000f16
+28 uint32 PagefileLimit
ffffffff
OBJECT_HEADER中的 /*014*/ PSECURITY_DESCRIPTOR ObjectCreateInfo
如果OBJECT_HEADER中的 OB_FLAG_SECURITY 标志被设立则 SecurityDescriptor 为指向 SECURITY_DESCRIPTOR 的指针。如果 OB_FLAG_SECURITY 标志没有被设立,则 SecurityDescriptor 为空。
SECURITY_DESCRIPTOR 结构定义如下
// kd 中使用 !strct 得到。
struct _SECURITY_DESCRIPTOR (sizeof=20)
+00 byte Revision
+01 byte Sbz1
+02 uint16 Control
+04 void *Owner
+08 void *Group
+0c struct _ACL *Sacl
+10 struct _ACL *Dacl
struct _SID (sizeof=12)
+0 byte Revision
+1 byte SubAuthorityCount
+2 struct _SID_IDENTIFIER_AUTHORITY IdentifierAuthority
+2 byte Value[6]
+8 uint32 SubAuthority[1]
struct _ACL (sizeof=8)
+0 byte AclRevision
+1 byte Sbz1
+2 uint16 AclSize
+4 uint16 AceCount
+6 uint16 Sbz2
\\ 以"\"为例
kd> !object !object Object: 8141ecd0 Type: (81452820) Directory
ObjectHeader: 8141ecb8
kd> dd 8141ecb8 l 6
dd 8141ecb8 l 6
8141ecb8 00000023 00000000 81452820 32000010
8141ecc8 00000001 e10010f8
\\ 32(hex)=00110010(bin) ,即 OB_FLAG_KERNEL_MODE,OB_FLAG_PERMANENT,OB_FLAG_SECURITY 这3位被设立。所以 e10010f8 为 SecurityDescriptor 指向 SECURITY_DESCRIPTOR。SECURITY_DESCRIPTOR 结构大小为20(dec)个字节。
kd> dd e10010f8 l 5
dd e10010f8 l 5
e10010f8 80040001 00000070 00000080 00000000
e1001108 00000014
+00 byte Revision
01
+01 byte Sbz1
00
+02 uint16 Control
8004
+04 void *Owner
00000070
+08 void *Group
00000080
+0c struct _ACL *Sacl
00000000
// 为空,表示不存在 Sacl
+10 struct _ACL *Dacl
00000014
// 不为空,表示存在 Dacl ,并且在从 SECURITY_DESCRIPTOR 开始处偏移为 14 的地方,
即 e10010f8+14
kd> db e10010f8+14 l 8
db e10010f8+14 l 8
e100110c 02 00 5c 00 04 00 00 00
+0 byte AclRevision
02
+1 byte Sbz1
00
+2 uint16 AclSize
005c
+4 uint16 AceCount
0004
+6 uint16 Sbz2
0000
// 使用 kd 的 !sd 命令来显示安全描述符
kd> !sd e10010f8
!sd e10010f8
->Revision: 0x1
->Sbz1 : 0x0
->Control : 0x8004
SE_DACL_PRESENT
SE_SELF_RELATIVE
->Owner : S-1-5-32-544
->Group : S-1-5-18
->Dacl :
->Dacl : ->AclRevision: 0x2
->Dacl : ->Sbz1 : 0x0
->Dacl : ->AclSize : 0x5c
->Dacl : ->AceCount : 0x4
->Dacl : ->Sbz2 : 0x0
->Dacl : ->Ace[0]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[0]: ->AceFlags: 0x0
->Dacl : ->Ace[0]: ->AceSize: 0x14
->Dacl : ->Ace[0]: ->Mask : 0x00020003
->Dacl : ->Ace[0]: ->SID: S-1-1-0
->Dacl : ->Ace[1]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[1]: ->AceFlags: 0x0
->Dacl : ->Ace[1]: ->AceSize: 0x14
->Dacl : ->Ace[1]: ->Mask : 0x000f000f
->Dacl : ->Ace[1]: ->SID: S-1-5-18
->Dacl : ->Ace[2]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[2]: ->AceFlags: 0x0
->Dacl : ->Ace[2]: ->AceSize: 0x18
->Dacl : ->Ace[2]: ->Mask : 0x000f000f
->Dacl : ->Ace[2]: ->SID: S-1-5-32-544
->Dacl : ->Ace[3]: ->AceType: ACCESS_ALLOWED_ACE_TYPE
->Dacl : ->Ace[3]: ->AceFlags: 0x0
->Dacl : ->Ace[3]: ->AceSize: 0x14
->Dacl : ->Ace[3]: ->Mask : 0x00020003
->Dacl : ->Ace[3]: ->SID: S-1-5-12
->Sacl : is NULL
总结
最后我们来总结一下,从一个对象可以找到的信息。对象指针都是指向对象体的。
对象头:
使用计数
打开句柄计数
相应类型对象的指针
标志和偏移(指明存在哪些可选部分,对存在的部分指明在对象头之前的偏移)
安全描述符
对象头之前的可选信息(哪些存在由对象头中的标志和偏移决定):
OBJECT_NAME 名字,对象所在目录
OBJECT_HANDLE_DB 使用对象的进程
OBJECT_QUOTA_CHARGES PagedPool,NonPagedPool 的使用情况
OBJECT_CREATOR_INFO 打开进程ID,本类型对象链表
类型对象:
该类型对象链表头(绝大多数的类型,并不构造这个链)
类型名(Unicode字符串)
类型序号(用一个数表示的类型)
其他的信息
该类型的一些例程
前面对对象头及其相关结构做了介绍,并对 Directory 类型,Type 类型 对象的对象体也做了介绍。Process 类型,Thred 类型 对象将在其他地方做更多介绍。需要特别说明的是,通过 Directory 可以找到的对象只是系统存在的所有对象中的一小部分。很多对象是被进程使用的,在有关进程的介绍中,我们将看到如何找到更多的对象。
欢迎交流,欢迎交朋友,