📄 jiurl玩玩win2k 对象.htm
字号:
标志位被设立说明了,SecurityDescriptor
不为空。这里是一个PSECURITY_DESCRIPTOR指针,我们将在后面进行讨论<BR><BR>ObjectHeader四个可选信息中
HandleDBOffset,QuotaChargesOffset 值都为0,表示没有相应的结构,NameOffset 值为
10,表示有相应的OBJECT_NAME结构,并且位于 ObjectHeader地址-0x10处。注意 OBJECT_NAME
结构长为0x10。可以看到 OBJECT_NAME 和 ObjectHeader 是紧挨着的。下面我们就来看看 OBJECT_NAME
中的内容。<BR><BR>kd> dd 8141ecb8-10 l 4<BR>dd 8141ecb8-10 l 4<BR>8141eca8
00000000 00040002 81452148 00000000<BR><BR>/*000*/ POBJECT_DIRECTORY
Directory;<BR>00000000<BR>// 所在Directory的指针为0,是由于对象"\"是根,不在任何目录下。比如
directory对象\KnownDlls,它的这个值就不为0。我得到的值为 8141ecd0 ,正是 对象"\" 的 ObjectBody
的地址。<BR><BR><BR>/*004*/ UNICODE_STRING Name;<BR>0002 0004 81452148<BR>//
UNICODE_STRING 的 PWSTR Buffer 值为 81452148<BR>kd> du 81452148<BR>du
81452148<BR>81452148 "\"<BR>// 可以看到对象的名字叫"\"<BR><BR>/*00C*/ DWORD
Reserved;<BR>00000000<BR><BR>对象头的简单分析就到这里,对于不同类型的对象,对象头的格式是一样的。<BR><BR><BR><B>Directory
类型的对象</B></P>
<P>
现在我们继续分析Directory类型的对象体。不同类型的对象由于用途不同,对象体的结构是完全不一样的,就象图1中的情况那样。某种类型对象体的内容是由该类型的需要所决定的。对于
Directory 类型对象的用途是什么呢?这里的 Directory 指的不是文件系统目录,而是一个对象的目录,从 WinObj
这样的工具我们就能看到这一点。对象目录的组织使用和文件系统目录的组织相似的层次结构。从前面使用kd得到的输出,我们可以看到kd至少可以从Directory类型的对象体中得到下面的信息。<BR>kd>
!object \<BR>!object \<BR>Object: 8141ecd0 Type: (81452820)
Directory<BR>...<BR>HashBucket[ 13 ]: e14088a0 Port
'SeRmCommandPort'<BR>HashBucket[ 14 ]: 810e8540 Mutant
'NlsCacheMutant'<BR>e2fdb4c0 Port 'LsaAuthenticationPort'<BR>81421450
Device 'Dfs'<BR>810fc870 Event 'LanmanServerAnnounceEvent'<BR>HashBucket[
16 ]: 81416530 Directory 'Driver'<BR>HashBucket[ 17 ]: e17c79c0 Port
'DbgSsApiPort'<BR>...<BR><BR>我们来看 Directory类型的对象体 OBJECT_DIRECTORY
的定义<BR><BR>#define OBJECT_HASH_TABLE_SIZE 37<BR><BR>typedef struct
_OBJECT_DIRECTORY<BR>{<BR>/*000*/ POBJECT_DIRECTORY_ENTRY HashTable
[OBJECT_HASH_TABLE_SIZE];<BR>/*094*/ POBJECT_DIRECTORY_ENTRY
CurrentEntry;<BR>/*098*/ BOOLEAN CurrentEntryValid;<BR>/*099*/ BYTE
Reserved1;<BR>/*09A*/ WORD Reserved2;<BR>/*09C*/ DWORD
Reserved3;<BR>/*0A0*/ }<BR>OBJECT_DIRECTORY;<BR><BR>typedef struct
_OBJECT_DIRECTORY_ENTRY<BR>{<BR>/*000*/ struct _OBJECT_DIRECTORY_ENTRY
*NextEntry;<BR>/*004*/ POBJECT Object;<BR>/*008*/
}<BR>OBJECT_DIRECTORY_ENTRY;<BR><BR>从定义中我们看到 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作为哈希表项数,效果还是不错的,对象比较均匀的分布在哈希表中,而且很多哈希表项对应的链上只有一个对象,有多个对象的链也都不是很长。<BR><BR>下面我们来看实际的例子,从前面
kd 的输出<BR>kd> !object \<BR>!object \<BR>Object: 8141ecd0 Type:
(81452820) Directory<BR>ObjectHeader: 8141ecb8<BR>...<BR>我们看到对象"\"的对象头在
8141ecb8 ,对象体在
8141ecd0,对象头结构大小为0x18,也可以看出对象头和对象体是紧挨在一起的。下面我们来分析目录类型对象"\"的对象体。<BR><BR>kd>
dd 8141ecd0 l a0/4<BR>dd 8141ecd0 l a0/4<BR>8141ecd0 e1008c68 e2f7c008
00000000 e10073c8<BR>8141ece0 00000000 00000000 00000000
e2bac088<BR>8141ecf0 00000000 e2bea1a8 e10001e8 00000000<BR>8141ed00
00000000 e13891a8 e2bea328 00000000<BR>8141ed10 e131eb08 e2baf188 e132f208
e1008ba8<BR>8141ed20 e17b6708 e13d4268 e17b65e8 e135a768<BR>8141ed30
e1007f28 00000000 e10004a8 e1007708<BR>8141ed40 00000000 00000000 00000000
e1008b08<BR>8141ed50 e10003a8 e2ffe508 00000000 e17bca68<BR>8141ed60
00000000 8141ecf4 00780001 00000000<BR><BR>前面使用kd得到的输出是<BR>HashBucket[ 00
]: 8141b930 Directory 'ArcName'<BR>...<BR>HashBucket[ 14 ]: 810e8540
Mutant 'NlsCacheMutant'<BR>e2fdb4c0 Port
'LsaAuthenticationPort'<BR>81421450 Device 'Dfs'<BR>810fc870 Event
'LanmanServerAnnounceEvent'<BR>...<BR>HashBucket[ 35 ]: 810f5f50 Directory
'KnownDlls'<BR><BR>哈希表的第0项值为 e1008c68,是个POBJECT_DIRECTORY_ENTRY<BR>kd>
dd e1008c68 l 2<BR>dd e1008c68 l 2<BR>e1008c68 00000000
8141b930<BR>可以看到指向的OBJECT_DIRECTORY_ENTRY的.NextEntry为空,.Object 为
8141b930<BR>用kd看到8141b930果然是一个对象,而且这个对象是一个目录类型的对象,通过它,又可以找到一些对象。<BR>kd>
!object 8141b930<BR>!object 8141b930<BR>Object: 8141b930 Type: (81452820)
Directory<BR>ObjectHeader: 8141b918<BR>HandleCount: 0 PointerCount:
12<BR>Directory Object: 8141ecd0 Name: ArcName<BR>HashBucket[ 00 ]:
813c4c90 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(3)'<BR>814070d0
SymbolicLink 'multi(0)disk(0)rdisk(0)'<BR>HashBucket[ 03 ]: 813c4c30
SymbolicLink 'multi(0)disk(0)rdisk(0)partition(4)'<BR>HashBucket[ 07 ]:
813c4bd0 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(5)'<BR>HashBucket[
10 ]: 813c4b70 SymbolicLink
'multi(0)disk(0)rdisk(0)partition(6)'<BR>HashBucket[ 14 ]: 813c4b10
SymbolicLink 'multi(0)disk(0)rdisk(0)partition(7)'<BR>HashBucket[ 17 ]:
813c4ab0 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(8)'<BR>HashBucket[
21 ]: 813c4a50 SymbolicLink
'multi(0)disk(0)rdisk(0)partition(9)'<BR>HashBucket[ 30 ]: 81420370
SymbolicLink 'multi(0)disk(0)rdisk(0)partition(1)'<BR>HashBucket[ 33 ]:
813e7230 SymbolicLink 'multi(0)disk(0)rdisk(0)partition(2)'<BR>813f9450
SymbolicLink 'multi(0)disk(0)fdisk(0)'<BR><BR>哈希表的第14项值为
e2bea328,是个POBJECT_DIRECTORY_ENTRY<BR>kd> dd e2bea328 l 2<BR>dd
e2bea328 l 2<BR>e2bea328 e13fee68 810e8540<BR>kd> !object
810e8540<BR>!object 810e8540<BR>Object: 810e8540 Type: (8141ccc0)
Mutant<BR>ObjectHeader: 810e8528<BR>HandleCount: 14 PointerCount:
15<BR>Directory Object: 8141ecd0 Name: NlsCacheMutant<BR>// 链表的第一项中,下一项地址为
e13fee68 ,对象地址为 810e8540<BR><BR>kd> dd e13fee68 l 2<BR>dd e13fee68 l
2<BR>e13fee68 e13c02e8 e2fdb4c0<BR>kd> !object e2fdb4c0<BR>!object
e2fdb4c0<BR>Object: e2fdb4c0 Type: (81416e80) Port<BR>ObjectHeader:
e2fdb4a8<BR>HandleCount: 1 PointerCount: 29<BR>Directory Object: 8141ecd0
Name: LsaAuthenticationPort<BR>// 链表的第二项中,下一项地址为 e13c02e8 ,对象地址为
e2fdb4c0<BR><BR>kd> dd e13c02e8 l 2<BR>dd e13c02e8 l 2<BR>e13c02e8
e178c908 81421450<BR>kd> !object 81421450<BR>!object
81421450<BR>Object: 81421450 Type: (81416920) Device<BR>ObjectHeader:
81421438<BR>HandleCount: 0 PointerCount: 2<BR>Directory Object: 8141ecd0
Name: Dfs<BR>// 链表的第三项中,下一项地址为 e178c908 ,对象地址为 81421450<BR><BR>kd> dd
e178c908 l 2<BR>dd e178c908 l 2<BR>e178c908 00000000 810fc870<BR>kd>
!object 810fc870<BR>!object 810fc870<BR>Object: 810fc870 Type: (8141e460)
Event<BR>ObjectHeader: 810fc858<BR>HandleCount: 1 PointerCount:
3<BR>Directory Object: 8141ecd0 Name: LanmanServerAnnounceEvent<BR>//
链表的第四项中,下一项地址为空,表示链表结束 ,对象地址为
810fc870<BR><BR>我们看到了目录类型对象中通过哈希表和链表来保存另一些对象的指针,包括目录对象。现在我们只要知道了目录类型对象"\"的地址("\"是根目录,它保存有下一级目录对象的指针)我们就可以找到所有目录下的对象。Win2k中全局变量
ObpRootDirectoryObject 中保存着目录对象"\"的地址。在 Win2k Build 2195 上,这个全局变量的地址为
8046ac24 。可以通过kd得到<BR>kd> ? ObpRootDirectoryObject<BR>?
ObpRootDirectoryObject<BR>Evaluate expression: -2142852060 =
8046ac24<BR>dd 8046ac24 l 1<BR>8046ac24 8141ecd0<BR><BR>我们前面已经看到了 8141ecd0
是目录对象"\"的对象体的地址。<BR><BR>我们在WinObj中,看不到任何的 Process, Thread 类型的对象。这是由于任何的
Directory Object 中都没有他们的对象指针。他们当然是存在的,否则就没必要弄个 Process, Thread 类型。他们其实就是
EPROCESS 和 ETHREAD。也就是说,EPROCESS,ETHREAD之前就有OBJECT_HEADER。<BR><BR>kd>
!process 8 0<BR>!process 8 0<BR>Searching for Process with Cid ==
8<BR>PROCESS 8141e020 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid:
0000<BR>DirBase: 00030000 ObjectTable: 81452a68 TableSize: 108.<BR>Image:
System<BR>\\ System 进程的 EPROCESS 地址为 8141e020<BR>kd> !object
8141e020<BR>!object 8141e020<BR>Object: 8141e020 Type: (814524e0)
Process<BR>ObjectHeader: 8141e008<BR>HandleCount: 2 PointerCount:
36<BR><BR>kd> dd 8141e008 l 6<BR>dd 8141e008 l 6<BR>8141e008 00000024
00000002 814524e0 22000000<BR>8141e018 804699c0
e1000618<BR><BR>很多的对象都不放在任何目录下。<BR>关于 Directory Object
就介绍到这里。<BR><BR><B>Type 类型的对象</B></P>
<P> 在前面介绍 OBJECT_HEADER 的时候,我们没有分析其中的 /*008*/
POBJECT_TYPE ObjectType; 这个域。这是一个非常重要的域,它是一个指针,指向包含对象类型的信息。前面例子中<BR>kd>
!object \<BR>!object \<BR>Object: 8141ecd0 Type: (81452820)
Directory<BR>...<BR>kd> dd 8141ecb8 l 6<BR>dd 8141ecb8 l 6<BR>8141ecb8
00000023 00000000 81452820 32000010<BR>8141ecc8 00000001 e10010f8<BR>kd
可以知道"\"是 Directory 类型的对象,就是通过 POBJECT_TYPE ObjectType找到相关结构,获得的信息。<BR>实际上
OBJECT_TYPE 也是一个对象,它就是 Type
类型的对象。它也象图1中显示的那样,有对象头,对象体和可选信息。<BR><BR>我们看到了"\"的 Type Object 地址为 81452820
,我们现在先分析一下这个 Type Object 的头。<BR><BR>kd> !object 81452820<BR>!object
81452820<BR>Object: 81452820 Type: (81452920) Type<BR>ObjectHeader:
81452808<BR>HandleCount: 0 PointerCount: 1<BR>Directory Object: 8141ebf0
Name: Directory<BR>头部应该在 0x81452820-0x18=0x81452808
处(原因是头部紧挨着对象体,头部在前,头部大小0x18个字节)从 kd 的输出中,看到 kd 也是这么认为的。<BR>kd> dd
81452808 l 6<BR>dd 81452808 l 6<BR>81452808 00000001 00000000 81452920
17000020<BR>81452818 00000000 00000000<BR><BR>/*000*/ DWORD PointerCount;
// number of references<BR>00000001<BR>/*004*/ DWORD HandleCount; //
number of open handles<BR>00000000<BR>/*008*/ POBJECT_TYPE
ObjectType;<BR>81452920<BR>/*00C*/ BYTE NameOffset; // ->
OBJECT_NAME<BR>20<BR>/*00D*/ BYTE HandleDBOffset; // ->
OBJECT_HANDLE_DB<BR>00<BR>/*00E*/ BYTE QuotaChargesOffset; // ->
OBJECT_QUOTA_CHARGES<BR>00<BR>/*00F*/ BYTE ObjectFlags; //
OB_FLAG_*<BR>17 <BR>17(hex)=00010111(bin) OB_FLAG_CREATE_INFO
OB_FLAG_KERNEL_MODE OB_FLAG_CREATOR_INFO
OB_FLAG_PERMANENT <BR><BR>/*010*/ union<BR>{ // OB_FLAG_CREATE_INFO ?
ObjectCreateInfo : QuotaBlock<BR>/*010*/ PQUOTA_BLOCK
QuotaBlock;<BR>/*010*/ POBJECT_CREATE_INFO ObjectCreateInfo;<BR>/*014*/
};<BR>00000000<BR>/*014*/ PSECURITY_DESCRIPTOR
SecurityDescriptor;<BR>00000000<BR><BR>看到存在
OBJECT_NAME,并且在偏移20处,也就是0x81452808-0x20处。<BR>kd> dd 81452808-20 l
4<BR>dd 81452808-20 l 4<BR>814527e8 8141ebf0 00140012 e1001948
00000000<BR>// 所在目录的对象地址为 8141ebf0<BR>kd> !object 8141ebf0<BR>!object
8141ebf0<BR>Object: 8141ebf0 Type: (81452820) Directory<BR>...<BR>//
和从WinObj 中看到的一样是Directory类型对象"Type"<BR>kd> du e1001948<BR>du
e1001948<BR>e1001948 "Directory"<BR>// 类型对象的名字叫
"Directory"<BR><BR>OBJECT_HEADER中的标志OB_FLAG_CREATOR_INFO被设立,表明
OBJECT_CREATOR_INFO 结构存在,这个可选结构不像其他三个需要指明偏移,这个结构如果存在就一定位于 OBJECT_HEADER
之前,并紧挨着 OBJECT_HEADER。<BR><BR>OBJECT_CREATOR_INFO 结构定义如下<BR>typedef struct
_OBJECT_CREATOR_INFO<BR>{<BR>/*000*/ LIST_ENTRY ObjectList; //
OBJECT_CREATOR_INFO<BR>/*008*/ HANDLE UniqueProcessId;<BR>/*00C*/ WORD
Reserved1;<BR>/*00E*/ WORD Reserved2;<BR>/*010*/
}<BR>OBJECT_CREATOR_INFO;<BR><BR>typedef struct _LIST_ENTRY {//from
ntdef.h<BR>struct _LIST_ENTRY *Flink;<BR>struct _LIST_ENTRY *Blink;<BR>}
LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER
PRLIST_ENTRY;<BR><BR>.ObjectList
同一类型的对象被这个双向链表链在一起。<BR><BR>.UniqueProcessId 为创建这个对象的进程ID。<BR><BR>//
由于在对象头之前,并紧挨着对象头,大小为0x10字节,所以<BR>kd> dd 81452808-10 l 4<BR>dd
81452808-10 l 4<BR>814527f8 814526f8 814528f8 00000000 00000000<BR>/*000*/
LIST_ENTRY ObjectList; // OBJECT_CREATOR_INFO<BR>814526f8
814528f8<BR>/*008*/ HANDLE UniqueProcessId;<BR>00000000<BR><BR>我们用 kd 遍历一下
ObjectList<BR><BR>kd> dd 814527f8 l 4<BR>dd 814527f8 l 4<BR>814527f8
814526f8 814528f8 00000000 00000000<BR>// 814527f8 就是
81452808-10<BR>kd> ? 814526f8+28<BR>? 814526f8+28<BR>Evaluate
expression: -2126174432 = 81452720<BR>// 由于 OBJECT_CREATOR_INFO 大小
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -