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)$。
只有进程和线程句柄。每个句柄值全局唯一。

句柄及句柄表的一些属性:

  1. 句柄是可以重复使用的,如一个线程终止之后,系统可以将该句柄值分配给其他新创建句柄。
  2. 句柄表是分级的,最多分为三级。为了节省内存,句柄表初始时只有一级,当句柄增多后会动态调整为二级或三级句柄表。
  3. 进程句柄上限:使用 24 个有效位,句柄上限为 $2^{24} - 1 == 16,777,215$。

13.png

1 进程句柄表

1.1 普通进程句柄表

每个进程的 EPROCESS.ObjectTable 成员指向当前进程的句柄表(_HANDLE_TABLE)。句柄表 HANDLE_TABLE.TableCode 成员需要仔细进行解析。

每个进程的句柄表最多可扩展为三层,初始时为了节约内存空间,只有一层。当进程的引用的句柄增加到一层句柄表无法容纳时(大于 256 个),可根据句柄数量动态扩展至二层或三层。

14.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kd> !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
ffffaf8f`88cd61c0 300 fontdrvhost.ex
ffffaf8f`88cce080 308 fontdrvhost.ex
...

// System Process
kd> dt nt!_EPROCESS ffffaf8f`846a1040 -y ObjectTable
+0x570 ObjectTable : 0xffff9e02`1244b700 _HANDLE_TABLE

句柄表 HANDLE_TABLE 结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kd> dt nt!_HANDLE_TABLE
+0x000 NextHandleNeedingPool : Uint4B
+0x004 ExtraInfoPages : Int4B
+0x008 TableCode : Uint8B
+0x010 QuotaProcess : Ptr64 _EPROCESS
+0x018 HandleTableList : _LIST_ENTRY
+0x028 UniqueProcessId : Uint4B
+0x02c Flags : Uint4B
+0x02c StrictFIFO : Pos 0, 1 Bit
+0x02c EnableHandleExceptions : Pos 1, 1 Bit
+0x02c Rundown : Pos 2, 1 Bit
+0x02c Duplicated : Pos 3, 1 Bit
+0x02c RaiseUMExceptionOnInvalidHandleClose : Pos 4, 1 Bit
+0x030 HandleContentionEvent : _EX_PUSH_LOCK
+0x038 HandleTableLock : _EX_PUSH_LOCK
+0x040 FreeLists : [1] _HANDLE_TABLE_FREE_LIST
+0x040 ActualEntry : [32] UChar
+0x060 DebugInfo : Ptr64 _HANDLE_TRACE_DEBUG_INFO
主要成员 解释
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
kd> dt nt!_HANDLE_TABLE_ENTRY
+0x000 VolatileLowValue : Int8B
+0x000 LowValue : Int8B
+0x000 InfoTable : Ptr64 _HANDLE_TABLE_ENTRY_INFO
+0x008 HighValue : Int8B
+0x008 NextFreeHandleEntry : Ptr64 _HANDLE_TABLE_ENTRY
+0x008 LeafHandleValue : _EXHANDLE
+0x000 RefCountField : Int8B
+0x000 Unlocked : Pos 0, 1 Bit // 表示当前Handle entry 条目是否正在被使用
+0x000 RefCnt : Pos 1, 16 Bits // 当前这个句柄值被引用的次数,也叫缓存次数
+0x000 Attributes : Pos 17, 3 Bits // bit 17表示当前句柄是否可被继承
// bit 18 指示关闭对象是否应生成审计消息
// bit 19 “Protect from close”位指示是否允许调用者关闭此句柄
// 可以使用 SetHandlelnformation 函数修改该位
+0x000 ObjectPointerBits : Pos 20, 44 Bits // 句柄所属的内核对象
+0x008 GrantedAccessBits : Pos 0, 25 Bits // 当前进程对该句柄的访问权限
+0x008 NoRightsUpgrade : Pos 25, 1 Bit // 指示如果句柄被复制到具有更高权限的进程,是否应该升级访问权限
+0x008 Spare1 : Pos 26, 6 Bits
+0x00c Spare2 : Uint4B

解释 OBJECT_HEADER.PointerCount 和 HANDLE_TABLE_ENTRY.RefCnt 的关系:

$OBJECT\_HEADER.PointerCount = 该进程句柄个数 + ObReferenceObject函数引用次数 + HANDLE\_TABLE\_ENTRY.RefCnt对该句柄的缓存个数$

对该公式的解释:

  1. 一个内核对象对应多个句柄(该进程句柄个数),每一个句柄都可能被引用多次(HANDLE_TABLE_ENTRY.RefCnt对该句柄的缓存个数)。

  2. 通过 ObReferenceObject 函数可以增加内核对象的引用计数。

  3. 句柄条目 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;$$

获取内核对象地址

过程如下:

15.png

  1. 列举当前进程表:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    kd> !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 为例子,对该进程的句柄表进行解析。

  2. 找到句柄表入口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    kd> 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 指向第二级地址表。

  3. 获取 _HANDLE_TABLE_ENTRY 条目地址。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    kd> ?? (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
    100
    kd> ?? (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"

根据句柄获取对象

15.png

以 System 进程为例,默认情况下 Windbg 附加到该进程下(如果没有则需要附加到该进程)。

  1. 获取进程地址。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    kd> !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
    21
    kd> 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,其为两层结构。

  2. 任意选择一个句柄来进行验证(如果默认没有附加到该进程需要手动附加 !process 0xffffaf8f846a1040)。

    如下选择一个句柄值为 0x2654。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    kd> !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。

  3. 找到句柄值 0x2654 在第十张表的索引值为 0x95。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    kd> 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
    2
    kd> ?? (0xaf8f89845de<<4) | 0xFFFF0000`00000000
    unsigned int64 0xffffaf8f`89845de0 // _OBJECT_HEADER
  4. 找到该句柄对应的对象类型。

    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
    2
    kd> dt nt!_OBJECT_TYPE 0xffffaf8f`846b9bc0 -y Name
    +0x010 Name : _UNICODE_STRING "File"
  5. 解析文件对象。

    1
    2
    kd> 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
2
3
4
5
6
7
8
9
kd> !dml_proc
Address PID Image file name
ffffaf8f`846a1040 4 System
ffffaf8f`847bd080 48 Registry
ffffaf8f`879f02c0 154 smss.exe
...

kd> dt nt!_EPROCESS 0xffffaf8f`846a1040 -y ObjectTable
+0x570 ObjectTable : 0xffff9e02`1244b700 _HANDLE_TABLE

通过全局指针 nt!ObpKernelHandleTable 查看:

1
2
3
kd> dq nt!ObpKernelHandleTable
fffff800`58844978 ffff9e02`1244b700 ffffaf8f`846b14e0
fffff800`58844988 ffffaf8f`846b1e80 00000000`00000000

2 全局句柄表

全局句柄表由全局指针 nt!PspCidTable 指向 _HANDLE_TABLE。全局句柄表有以下几点特征:

  1. 全局句柄表中,只有进程(_EPROCESS)、线程(_ETHREAD)对象的句柄。
  2. 全局句柄表 _HANDLE_TABLE_ENTRY.ObjectPointerBits 经过计算后直接指向进程/线程对象体,而不是对象头。
  3. PspCidTable 中存放的对象是系统中所有的进程线程对象,其索引就是 PID 和 TID。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
kd> dq nt!PspCidTable
fffff800`5891b5c8 ffff9e02`1248b680 ffffaf8f`84698bc0
fffff800`5891b5d8 ffffaf8f`846b9a60 00000000`00000000
fffff800`5891b5e8 00010000`00000002 00000000`00001000
fffff800`5891b5f8 00000000`00000000 00000000`00000000

kd> dt nt!_HANDLE_TABLE 0xffff9e02`1248b680
+0x000 NextHandleNeedingPool : 0x1800
+0x004 ExtraInfoPages : 0n0
+0x008 TableCode : 0xffff9e02`183a7001
+0x010 QuotaProcess : (null)
+0x018 HandleTableList : _LIST_ENTRY [ 0xffff9e02`1248b698 - 0xffff9e02`1248b698 ]
+0x028 UniqueProcessId : 0
+0x02c Flags : 1
+0x02c StrictFIFO : 0y1
+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)

是两层结构。

1
2
3
4
5
6
7
kd> ?? 0xffff9e02`183a7001 & (~0x7)
unsigned int64 0xffff9e02`183a7000
kd> dq 0xffff9e02`183a7000
ffff9e02`183a7000 ffff9e02`124b1000 ffff9e02`183a8000
ffff9e02`183a7010 ffff9e02`18b4e000 ffff9e02`19460000
ffff9e02`183a7020 ffff9e02`1a0f9000 ffff9e02`15ffe000
ffff9e02`183a7030 00000000`00000000 00000000`00000000

Notepad.exe

1
2
3
kd> !dml_proc
Address PID Image file name
ffffaf8f`89505080 644 notepad.exe

向上取整 ⌈0x644/0x400⌉ = 0x2,说明该句柄在第 2 张表里(表索引为 1)。如下,应该在 0xffff9e02183a8000 指向的表里。

计算该句柄值在第二张表的索引:(0x644 - 1*0x400)/4 = 0x91。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kd> dt nt!_HANDLE_TABLE_ENTRY 0xffff9e02`183a8000+0x10*0x91
+0x000 VolatileLowValue : 0n-5796263217361520011
+0x000 LowValue : 0n-5796263217361520011
+0x000 InfoTable : 0xaf8f8950`5080fa75 _HANDLE_TABLE_ENTRY_INFO
+0x008 HighValue : 0n0
+0x008 NextFreeHandleEntry : (null)
+0x008 LeafHandleValue : _EXHANDLE
+0x000 RefCountField : 0n-5796263217361520011
+0x000 Unlocked : 0y1
+0x000 RefCnt : 0y0111110100111010 (0x7d3a)
+0x000 Attributes : 0y000
+0x000 ObjectPointerBits : 0y10101111100011111000100101010000010100001000 (0xaf8f8950508)
+0x008 GrantedAccessBits : 0y0000000000000000000000000 (0)
+0x008 NoRightsUpgrade : 0y0
+0x008 Spare1 : 0y000000 (0)
+0x00c Spare2 : 0

根据 ObjectPointerBits 计算进程对象地址:

1
2
kd> ?? ((0xaf8f8950508 << 4)&0xFFFFFFFF`FFFFFFF0)|0xFFFF0000`00000000
unsigned int64 0xffffaf8f`89505080

获取镜像文件名称:

1
2
kd> dt nt!_EPROCESS 0xffffaf8f`89505080 -y ImageFileName
+0x5a8 ImageFileName : [15] "notepad.exe"