Windows 句柄表
😄
0 句柄表
句柄表分为:进程句柄表(包括内核句柄表)、全局句柄表。区别如下:
| 类型 | 包含句柄类型 | ObjectPointerBits 指向内容 | 句柄特征 |
|---|---|---|---|
| 进程句柄表 | 1、该进程创建和打开的所有线程句柄。 2、该进程创建的子进程句柄。 3、该进程创建和打开的其他内核对象句柄。 |
对象头 _OBJECT_HEADER |
句柄值 >= 0,且 $Handle ≡ 0(mod\ 4)$ 0:在进程结束时使用。 -1:表示当前进程。 -2:表示当前线程。 除进程、线程句柄外,其他内核对象句柄值不是全局唯一的。 |
| 内核句柄表 | System 进程的句柄表。 | 对象头 _OBJECT_HEADER |
句柄值 <= 0,为负数。且 $Handle ≡ 0(mod\ 4)$ 除进程、线程句柄外,其他内核对象句柄值不是全局唯一的。 |
| 全局句柄表 | 1、所有用户模式、内核模式进程句柄。 2、所有用户模式、内核模式线程句柄。 |
对象体 _OBJECT_BODY |
句柄值 >= 0,且 $Handle ≡ 0(mod\ 4)$。 只有进程和线程句柄。每个句柄值全局唯一。 |
句柄及句柄表的一些属性:
- 句柄是可以重复使用的,如一个线程终止之后,系统可以将该句柄值分配给其他新创建句柄。
- 句柄表是分级的,最多分为三级。为了节省内存,句柄表初始时只有一级,当句柄增多后会动态调整为二级或三级句柄表。
- 进程句柄上限:使用 24 个有效位,句柄上限为 $2^{24} - 1 == 16,777,215$。

1 进程句柄表
1.1 普通进程句柄表
每个进程的 EPROCESS.ObjectTable 成员指向当前进程的句柄表(_HANDLE_TABLE)。句柄表 HANDLE_TABLE.TableCode 成员需要仔细进行解析。
每个进程的句柄表最多可扩展为三层,初始时为了节约内存空间,只有一层。当进程的引用的句柄增加到一层句柄表无法容纳时(大于 256 个),可根据句柄数量动态扩展至二层或三层。

1 | kd> !dml_proc |
句柄表 HANDLE_TABLE 结构如下:
1 | kd> dt nt!_HANDLE_TABLE |
| 主要成员 | 解释 | |
|---|---|---|
| TableCode | 主要有两个作用: 1、指示当前进程句柄表的层数(layers):layers = TableCode & 0x7。 layers = 0:句柄表为一层结构,TableCode 指向 _HANDLE_TABLE_ENTRY。layers = 1:句柄表为二层结构,TableCode 不直接指向 _HANDLE_TABLE_ENTRY。layers = 2:句柄表为三层结构,TableCode 不直接指向 _HANDLE_TABLE_ENTRY。2、TableCode 是一个指针,如下。 layers = 0:TableCode 指向 _HANDLE_TABLE_ENTRY。layers = 1:TableCode & ~0x7 指向第二级表,二级表存储的地址都指向 _HANDLE_TABLE_ENTRY 条目。layers = 2:TableCode & ~0x7 指向第二级表,二级表存储的地址指向第三级表, 三级表存储的地址都指向 _HANDLE_TABLE_ENTRY 条目。 |
|
| QuotaProcess | 指向句柄表所属的进程。 | |
| HandleTableList | 双向链表,指向其他进程的句柄表,可用于遍历其他句柄表。为 _HANDLE_TABLE 结构。 |
借鉴 《Windows Internals 7》 将最外层句柄表叫做 Subhandle table,它为 _HANDLE_TABLE_ENTRY 结构。
1 | kd> dt nt!_HANDLE_TABLE_ENTRY |
解释 OBJECT_HEADER.PointerCount 和 HANDLE_TABLE_ENTRY.RefCnt 的关系:
$OBJECT\_HEADER.PointerCount = 该进程句柄个数 + ObReferenceObject函数引用次数 + HANDLE\_TABLE\_ENTRY.RefCnt对该句柄的缓存个数$
对该公式的解释:
一个内核对象对应多个句柄(
该进程句柄个数),每一个句柄都可能被引用多次(HANDLE_TABLE_ENTRY.RefCnt对该句柄的缓存个数)。通过
ObReferenceObject函数可以增加内核对象的引用计数。句柄条目
HANDLE_TABLE_ENTRY.RefCnt表示当前这个句柄值被引用的次数。举例,某个进程对象
_EPROCESS对应的三个句柄值为240、168、1920;前两个句柄值的引用次数HANDLE_TABLE_ENTRY.RefCnt == 20,最后一个句柄值的引用次数为15;通过ObReferenceObject函数引用的次数为3次。则该进程对象OBJECT_HEADER.PointerCount = 3 + 20*2 + 15 + 3 = 61 = 0x3D。
ObjectPointerBits 成员是在 Windows 8 新增的成员,用它的值通过计算后来指向该句柄对应的内核对象,不同版本计算方法不同。本文关注 Windows 10 下的计算方法。
在 Windows 10 下该成员为 44 bits。
计算方法一如下:
$$Object\_Header = (HANDLE\_TABLE\_ENTRY.ObjectPointerBits << 4)\ |\ 0xFFFF0000’00000000$$
计算方法二如下:
$$Point = (PVOID)\&HANDLE\_TABLE\_ENTRY;$$
$$Object\_Header = ((Point >> 16)\ \&\ 0xFFFFFFFF’FFFFFFF0)\ |\ 0xFFFF0000’00000000;$$
获取内核对象地址
过程如下:

列举当前进程表:
1
2
3
4
5
6
7
8
9
10
11
12
13
14kd> !dml_proc // !process 0 0
Address PID Image file name
ffffaf8f`846a1040 4 System
ffffaf8f`847bd080 48 Registry
ffffaf8f`879f02c0 154 smss.exe
ffffaf8f`87e9e140 1c4 csrss.exe
ffffaf8f`87a0a140 214 csrss.exe
ffffaf8f`87a32080 224 wininit.exe
ffffaf8f`87a08140 24c winlogon.exe
ffffaf8f`87f62100 298 services.exe
ffffaf8f`87f650c0 2a0 lsass.exe
ffffaf8f`88cd61c0 300 fontdrvhost.ex
ffffaf8f`88cce080 308 fontdrvhost.ex
...下面以系统进程
System为例子,对该进程的句柄表进行解析。找到句柄表入口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21kd> dt nt!_EPROCESS ffffaf8f`846a1040 -y ObjectTable
+0x570 ObjectTable : 0xffff9e02`1244b700 _HANDLE_TABLE
kd> dt 0xffff9e02`1244b700 nt!_HANDLE_TABLE
+0x000 NextHandleNeedingPool : 0x2800
+0x004 ExtraInfoPages : 0n0
+0x008 TableCode : 0xffff9e02`12dfe001
+0x010 QuotaProcess : (null)
+0x018 HandleTableList : _LIST_ENTRY [ 0xffff9e02`1245a5d8 - 0xfffff800`5894db40 ]
+0x028 UniqueProcessId : 4
+0x02c Flags : 0
+0x02c StrictFIFO : 0y0
+0x02c EnableHandleExceptions : 0y0
+0x02c Rundown : 0y0
+0x02c Duplicated : 0y0
+0x02c RaiseUMExceptionOnInvalidHandleClose : 0y0
+0x030 HandleContentionEvent : _EX_PUSH_LOCK
+0x038 HandleTableLock : _EX_PUSH_LOCK
+0x040 FreeLists : [1] _HANDLE_TABLE_FREE_LIST
+0x040 ActualEntry : [32] ""
+0x060 DebugInfo : (null)可以看到
TableCode & 0x7 == 0x1,说明System进程的句柄表为两层结构。地址TableCode & ~0x7指向第二级地址表。获取
_HANDLE_TABLE_ENTRY条目地址。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19kd> ?? (0xffff9e02`12dfe001&(~0x7))
unsigned int64 0xffff9e02`12dfe000
kd> dt nt!_HANDLE_TABLE_ENTRY 0xffff9e02`12dfe000
+0x000 VolatileLowValue : 0n-107743242698752
+0x000 LowValue : 0n-107743242698752
+0x000 InfoTable : 0xffff9e02`124ac000 _HANDLE_TABLE_ENTRY_INFO
+0x008 HighValue : 0n-107743232921600
+0x008 NextFreeHandleEntry : 0xffff9e02`12dff000 _HANDLE_TABLE_ENTRY
+0x008 LeafHandleValue : _EXHANDLE
+0x000 RefCountField : 0n-107743242698752
+0x000 Unlocked : 0y0
+0x000 RefCnt : 0y0110000000000000 (0x6000)
+0x000 Attributes : 0y101
+0x000 ObjectPointerBits : 0y11111111111111111001111000000010000100100100 (0xffff9e02124)
+0x008 GrantedAccessBits : 0y0110111111111000000000000 (0xdff000)
+0x008 NoRightsUpgrade : 0y1
+0x008 Spare1 : 0y000100 (0x4)
+0x00c Spare2 : 0xffff9e02以上是错误的,以下才是正确的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80// 获取二级表的地址
kd> dq 0xffff9e02`12dfe000
ffff9e02`12dfe000 ffff9e02`124ac000 ffff9e02`12dff000 // 每一项都指向 HANDLE_TABLE_ENTRY 条目
ffff9e02`12dfe010 ffff9e02`15b76000 ffff9e02`183d6000
ffff9e02`12dfe020 ffff9e02`18b4f000 ffff9e02`191fb000
ffff9e02`12dfe030 ffff9e02`197c6000 ffff9e02`19c22000
ffff9e02`12dfe040 ffff9e02`183cd000 ffff9e02`16ef1000
ffff9e02`12dfe050 00000000`00000000 00000000`00000000
ffff9e02`12dfe060 00000000`00000000 00000000`00000000
kd> dq ffff9e02`124ac000
ffff9e02`124ac000 00000000`00000000 00000000`00000000 // 0
ffff9e02`124ac010 af8f846a`1010ff8b 00000000`001fffff // 4, System
ffff9e02`124ac020 af8f8474`60500001 00000000`001fffff
ffff9e02`124ac030 af8f846b`24d0fe61 00000000`001f0001
ffff9e02`124ac040 9e021248`9470fe07 00000000`000f000f
ffff9e02`124ac050 9e021241`f7700001 00000000`000f000f
ffff9e02`124ac060 af8f846b`72300001 00000000`001f0003
ffff9e02`124ac070 9e021248`97c00001 00000000`000f000f
kd> ?? (0xaf8f846a`1010ff8b >> 0x10) & (0xFFFFFFFF`FFFFFFF0)
unsigned int64 0x0000af8f`846a1010
kd> ?? (0xaf8f846a`1010ff8b >> 0x10) & (0xFFFFFFFF`FFFFFFF0) | (0xFFFF0000`00000000)
unsigned int64 0xffffaf8f`846a1010
kd> dt nt!_OBJECT_HEADER 0xffffaf8f`846a1010
+0x000 PointerCount : 0n130126
+0x008 HandleCount : 0n4
+0x008 NextToFree : 0x00000000`00000004 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : 0xc3 ''
+0x019 TraceFlags : 0 ''
+0x019 DbgRefTrace : 0y0
+0x019 DbgTracePermanent : 0y0
+0x01a InfoMask : 0 ''
+0x01b Flags : 0x2 ''
+0x01b NewObject : 0y0
+0x01b KernelObject : 0y1
+0x01b KernelOnlyAccess : 0y0
+0x01b ExclusiveObject : 0y0
+0x01b PermanentObject : 0y0
+0x01b DefaultSecurityQuota : 0y0
+0x01b SingleHandleEntry : 0y0
+0x01b DeletedInline : 0y0
+0x01c Reserved : 0
+0x020 ObjectCreateInfo : 0xfffff800`58872840 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : 0xfffff800`58872840 Void
+0x028 SecurityDescriptor : 0xffff9e02`1241cead Void
+0x030 Body : _QUAD
kd> ? 0x10^0xc3
Evaluate expression: 211 = 00000000`000000d3
kd> db nt!ObHeaderCookie
fffff800`5891b72c d4 57 00 70 01 00 00 00-00 00 00 00 dd 01 00 00 .W.p............
kd> ? 0xd4^d3
Evaluate expression: 7 = 00000000`00000007
kd> dq ObTypeIndexTable+7*8
fffff800`5891beb8 ffffaf8f`8468f140 ffffaf8f`8468f820
fffff800`5891bec8 ffffaf8f`8468f2a0 ffffaf8f`8468f400
kd> dt nt!_OBJECT_TYPE ffffaf8f`8468f140
+0x000 TypeList : _LIST_ENTRY [ 0xffffaf8f`8468f140 - 0xffffaf8f`8468f140 ]
+0x010 Name : _UNICODE_STRING "Process"
+0x020 DefaultObject : (null)
+0x028 Index : 0x7 ''
+0x02c TotalNumberOfObjects : 0x4c
+0x030 TotalNumberOfHandles : 0x384
+0x034 HighWaterNumberOfObjects : 0x61
+0x038 HighWaterNumberOfHandles : 0x416
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b8 TypeLock : _EX_PUSH_LOCK
+0x0c0 Key : 0x636f7250
+0x0c8 CallbackList : _LIST_ENTRY [ 0xffff9e02`1288b400 - 0xffff9e02`1288b400 ]
kd> dt nt!_EPROCESS 0xffffaf8f`846a1010+0x30 -y ImageFileName
+0x5a8 ImageFileName : [15] "System"方法二:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100kd> ?? (0xffff9e02`12dfe001&(~0x7))
unsigned int64 0xffff9e02`12dfe000
// 一级表
kd> dq 0xffff9e02`12dfe000
ffff9e02`12dfe000 ffff9e02`124ac000 ffff9e02`12dff000 // 一级表中的第 1、2 张表
ffff9e02`12dfe010 ffff9e02`15b76000 ffff9e02`183d6000 // 一级表中的第 3、4 张表
ffff9e02`12dfe020 ffff9e02`18b4f000 ffff9e02`191fb000
ffff9e02`12dfe030 ffff9e02`197c6000 ffff9e02`19c22000
ffff9e02`12dfe040 ffff9e02`183cd000 ffff9e02`16ef1000
ffff9e02`12dfe050 00000000`00000000 00000000`00000000
ffff9e02`12dfe060 00000000`00000000 00000000`00000000
// 二级表
kd> dq 0xffff9e02`124ac000 // nt!_HANDLE_TABLE_ENTRY
ffff9e02`124ac000 00000000`00000000 00000000`00000000 // 一级表中、第 1 张表、第 1 项
ffff9e02`124ac010 af8f846a`1010ff8b 00000000`001fffff // 一级表中、第 1 张表、第 2 项
ffff9e02`124ac020 af8f8474`60500001 00000000`001fffff
ffff9e02`124ac030 af8f846b`24d0fe61 00000000`001f0001
ffff9e02`124ac040 9e021248`9470fe07 00000000`000f000f
ffff9e02`124ac050 9e021241`f7700001 00000000`000f000f
ffff9e02`124ac060 af8f846b`72300001 00000000`001f0003
ffff9e02`124ac070 9e021248`97c00001 00000000`000f000f
kd> dt nt!_HANDLE_TABLE_ENTRY 0xffff9e02`124ac010
+0x000 VolatileLowValue : 0n-5796268604331589749
+0x000 LowValue : 0n-5796268604331589749
+0x000 InfoTable : 0xaf8f846a`1010ff8b _HANDLE_TABLE_ENTRY_INFO
+0x008 HighValue : 0n2097151
+0x008 NextFreeHandleEntry : 0x00000000`001fffff _HANDLE_TABLE_ENTRY
+0x008 LeafHandleValue : _EXHANDLE
+0x000 RefCountField : 0n-5796268604331589749
+0x000 Unlocked : 0y1
+0x000 RefCnt : 0y0111111111000101 (0x7fc5)
+0x000 Attributes : 0y000
+0x000 ObjectPointerBits : 0y10101111100011111000010001101010000100000001 (0xaf8f846a101)
+0x008 GrantedAccessBits : 0y0000111111111111111111111 (0x1fffff)
+0x008 NoRightsUpgrade : 0y0
+0x008 Spare1 : 0y000000 (0)
+0x00c Spare2 : 0
// 关注 ObjectPointerBits
kd> ?? (0xaf8f846a101 << 4) |(0xFFFF0000`00000000)
unsigned int64 0xffffaf8f`846a1010
kd> dt nt!_OBJECT_HEADER 0xffffaf8f`846a1010
+0x000 PointerCount : 0n130126
+0x008 HandleCount : 0n4
+0x008 NextToFree : 0x00000000`00000004 Void
+0x010 Lock : _EX_PUSH_LOCK
+0x018 TypeIndex : 0xc3 ''
+0x019 TraceFlags : 0 ''
+0x019 DbgRefTrace : 0y0
+0x019 DbgTracePermanent : 0y0
+0x01a InfoMask : 0 ''
+0x01b Flags : 0x2 ''
+0x01b NewObject : 0y0
+0x01b KernelObject : 0y1
+0x01b KernelOnlyAccess : 0y0
+0x01b ExclusiveObject : 0y0
+0x01b PermanentObject : 0y0
+0x01b DefaultSecurityQuota : 0y0
+0x01b SingleHandleEntry : 0y0
+0x01b DeletedInline : 0y0
+0x01c Reserved : 0
+0x020 ObjectCreateInfo : 0xfffff800`58872840 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : 0xfffff800`58872840 Void
+0x028 SecurityDescriptor : 0xffff9e02`1241cead Void
+0x030 Body : _QUAD
kd> ? 0x10^0xc3
Evaluate expression: 211 = 00000000`000000d3
kd> db nt!ObHeaderCookie
fffff800`5891b72c d4 57 00 70 01 00 00 00-00 00 00 00 dd 01 00 00 .W.p............
kd> ? 0xd3^d4
Evaluate expression: 7 = 00000000`00000007
kd> dq nt!ObTypeIndexTable+7*8
fffff800`5891beb8 ffffaf8f`8468f140 ffffaf8f`8468f820
fffff800`5891bec8 ffffaf8f`8468f2a0 ffffaf8f`8468f400
...
kd> dt nt!_OBJECT_TYPE 0xffffaf8f`8468f140
+0x000 TypeList : _LIST_ENTRY [ 0xffffaf8f`8468f140 - 0xffffaf8f`8468f140 ]
+0x010 Name : _UNICODE_STRING "Process"
+0x020 DefaultObject : (null)
+0x028 Index : 0x7 ''
+0x02c TotalNumberOfObjects : 0x4c
+0x030 TotalNumberOfHandles : 0x384
+0x034 HighWaterNumberOfObjects : 0x61
+0x038 HighWaterNumberOfHandles : 0x416
+0x040 TypeInfo : _OBJECT_TYPE_INITIALIZER
+0x0b8 TypeLock : _EX_PUSH_LOCK
+0x0c0 Key : 0x636f7250
+0x0c8 CallbackList : _LIST_ENTRY [ 0xffff9e02`1288b400 - 0xffff9e02`1288b400 ]
kd> dt nt!_EPROCESS 0xffffaf8f`846a1010+0x30 -y ImageFileName
+0x5a8 ImageFileName : [15] "System"
根据句柄获取对象

以 System 进程为例,默认情况下 Windbg 附加到该进程下(如果没有则需要附加到该进程)。
获取进程地址。
1
2
3
4
5
6
7
8
9
10
11
12kd> !dml_proc
Address PID Image file name
ffffaf8f`846a1040 4 System
ffffaf8f`847bd080 48 Registry
ffffaf8f`879f02c0 154 smss.exe
ffffaf8f`87e9e140 1c4 csrss.exe
ffffaf8f`87a0a140 214 csrss.exe
ffffaf8f`87a32080 224 wininit.exe
ffffaf8f`87a08140 24c winlogon.exe
ffffaf8f`87f62100 298 services.exe
ffffaf8f`87f650c0 2a0 lsass.exe
...进程 System 地址为
0xffffaf8f846a1040。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21kd> dt nt!_EPROCESS ffffaf8f`846a1040 -y ObjectTable
+0x570 ObjectTable : 0xffff9e02`1244b700 _HANDLE_TABLE
kd> dt 0xffff9e02`1244b700 nt!_HANDLE_TABLE
+0x000 NextHandleNeedingPool : 0x2800
+0x004 ExtraInfoPages : 0n0
+0x008 TableCode : 0xffff9e02`12dfe001
+0x010 QuotaProcess : (null)
+0x018 HandleTableList : _LIST_ENTRY [ 0xffff9e02`1245a5d8 - 0xfffff800`5894db40 ]
+0x028 UniqueProcessId : 4
+0x02c Flags : 0
+0x02c StrictFIFO : 0y0
+0x02c EnableHandleExceptions : 0y0
+0x02c Rundown : 0y0
+0x02c Duplicated : 0y0
+0x02c RaiseUMExceptionOnInvalidHandleClose : 0y0
+0x030 HandleContentionEvent : _EX_PUSH_LOCK
+0x038 HandleTableLock : _EX_PUSH_LOCK
+0x040 FreeLists : [1] _HANDLE_TABLE_FREE_LIST
+0x040 ActualEntry : [32] ""
+0x060 DebugInfo : (null)可以看到句柄表的地址为
0xffff9e0212dfe001,其为两层结构。任意选择一个句柄来进行验证(如果默认没有附加到该进程需要手动附加
!process 0xffffaf8f846a1040)。如下选择一个句柄值为
0x2654。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20kd> !handle
PROCESS ffffaf8f846a1040
SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 001ad002 ObjectTable: ffff9e021244b700 HandleCount: 2056.
Image: System
Kernel handle table at ffff9e021244b700 with 2056 entries in use
0004: Object: ffffaf8f846a1040 GrantedAccess: 001fffff (Protected) Entry: ffff9e02124ac010
Object: ffffaf8f846a1040 Type: (ffffaf8f8468f140) Process
ObjectHeader: ffffaf8f846a1010 (new version)
HandleCount: 4 PointerCount: 130126
...
2654: Object: ffffaf8f89845e10 GrantedAccess: 00000003 (Inherit) (Audit) Entry: ffff9e0216ef1950
Object: ffffaf8f89845e10 Type: (ffffaf8f846b9bc0) File
ObjectHeader: ffffaf8f89845de0 (new version)
HandleCount: 1 PointerCount: 1
Directory Object: 00000000 Name: \Windows\System32\config\COMPONENTS.LOG2 {HarddiskVolume4}
...向上取整
⌈0x2654/0x400⌉ = 0xa,说明该句柄在第10张表里(索引为9)。如下,应该在0xffff9e0216ef1000指向的表里。1
2
3
4
5
6
7
8
9
10
11
12// 获取二级表的地址
kd> ?? (0xffff9e02`12dfe001&(~0x7))
unsigned int64 0xffff9e02`12dfe000
kd> dq 0xffff9e02`12dfe000
ffff9e02`12dfe000 ffff9e02`124ac000 ffff9e02`12dff000 // 每一项都指向 HANDLE_TABLE_ENTRY 条目
ffff9e02`12dfe010 ffff9e02`15b76000 ffff9e02`183d6000
ffff9e02`12dfe020 ffff9e02`18b4f000 ffff9e02`191fb000
ffff9e02`12dfe030 ffff9e02`197c6000 ffff9e02`19c22000
ffff9e02`12dfe040 ffff9e02`183cd000 ffff9e02`16ef1000
ffff9e02`12dfe050 00000000`00000000 00000000`00000000
ffff9e02`12dfe060 00000000`00000000 00000000`00000000计算该句柄值在第十张表的索引:
(0x2654 - 9*0x400)/4 = 0x95。找到句柄值
0x2654在第十张表的索引值为0x95。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16kd> dt nt!_HANDLE_TABLE_ENTRY 0xffff9e02`16ef1000+0x95*0x10
+0x000 VolatileLowValue : 0n-5796262993798889471
+0x000 LowValue : 0n-5796262993798889471
+0x000 InfoTable : 0xaf8f8984`5de00001 _HANDLE_TABLE_ENTRY_INFO
+0x008 HighValue : 0n3
+0x008 NextFreeHandleEntry : 0x00000000`00000003 _HANDLE_TABLE_ENTRY
+0x008 LeafHandleValue : _EXHANDLE
+0x000 RefCountField : 0n-5796262993798889471
+0x000 Unlocked : 0y1
+0x000 RefCnt : 0y0000000000000000 (0)
+0x000 Attributes : 0y000
+0x000 ObjectPointerBits : 0y10101111100011111000100110000100010111011110 (0xaf8f89845de)
+0x008 GrantedAccessBits : 0y0000000000000000000000011 (0x3)
+0x008 NoRightsUpgrade : 0y0
+0x008 Spare1 : 0y000000 (0)
+0x00c Spare2 : 0根据公示:
$$Object\_Header = (HANDLE\_TABLE\_ENTRY.ObjectPointerBits << 4)\ |\ 0xFFFF0000’00000000$$
有如下:
1
2kd> ?? (0xaf8f89845de<<4) | 0xFFFF0000`00000000
unsigned int64 0xffffaf8f`89845de0 // _OBJECT_HEADER找到该句柄对应的对象类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 找到 TypeIndex
kd> dt nt!_OBJECT_HEADER 0xffffaf8f`89845de0 -y TypeIndex
+0x018 TypeIndex : 0xac ''
// 找到 ObHeaderCookie 最低字节
kd> db nt!ObHeaderCookie
fffff800`5891b72c d4 57 00 70 01 00 00 00-00 00 00 00 dd 01 00 00 .W.p............
// 计算 ObTypeIndexTable 表的索引
kd> ? 0xac^0xd4^0x5d
Evaluate expression: 37 = 00000000`00000025
// 找到该索引对应的地址
kd> dq nt!ObTypeIndexTable+0x25*8
fffff800`5891bfa8 ffffaf8f`846b9bc0 ffffaf8f`846b9380
...该索引对应的
_OBJECT_TYPE为0xffffaf8f846b9bc0。1
2kd> dt nt!_OBJECT_TYPE 0xffffaf8f`846b9bc0 -y Name
+0x010 Name : _UNICODE_STRING "File"解析文件对象。
1
2kd> dt nt!_FILE_OBJECT 0xffffaf8f`89845de0+0x30 -y FileName
+0x058 FileName : _UNICODE_STRING "\Windows\System32\config\COMPONENTS.LOG2"和一开始使用
!handle 0x2654解析一致。
1.2 内核进程句柄表
内核句柄表指的就是 System 进程的句柄表,该句柄表和普通进程句柄表无差异。只不过 nt!ObpKernelHandleTable 全局指针可以直接指向 System 进程的句柄表 EPROCESS.ObjectTable(_HANDLE_TABLE)
1 | kd> !dml_proc |
通过全局指针 nt!ObpKernelHandleTable 查看:
1 | kd> dq nt!ObpKernelHandleTable |
2 全局句柄表
全局句柄表由全局指针 nt!PspCidTable 指向 _HANDLE_TABLE。全局句柄表有以下几点特征:
- 全局句柄表中,只有进程(
_EPROCESS)、线程(_ETHREAD)对象的句柄。 - 全局句柄表
_HANDLE_TABLE_ENTRY.ObjectPointerBits经过计算后直接指向进程/线程对象体,而不是对象头。 PspCidTable中存放的对象是系统中所有的进程线程对象,其索引就是PID和TID。
1 | kd> dq nt!PspCidTable |
是两层结构。
1 | kd> ?? 0xffff9e02`183a7001 & (~0x7) |
Notepad.exe
1 | kd> !dml_proc |
向上取整 ⌈0x644/0x400⌉ = 0x2,说明该句柄在第 2 张表里(表索引为 1)。如下,应该在 0xffff9e02183a8000 指向的表里。
计算该句柄值在第二张表的索引:(0x644 - 1*0x400)/4 = 0x91。
1 | kd> dt nt!_HANDLE_TABLE_ENTRY 0xffff9e02`183a8000+0x10*0x91 |
根据 ObjectPointerBits 计算进程对象地址:
1 | kd> ?? ((0xaf8f8950508 << 4)&0xFFFFFFFF`FFFFFFF0)|0xFFFF0000`00000000 |
获取镜像文件名称:
1 | kd> dt nt!_EPROCESS 0xffffaf8f`89505080 -y ImageFileName |