在上一篇文章《驱动开发:内核枚举DpcTimer定时器》
中我们通过枚举特征码的方式找到了DPC
定时器基址并输出了内核中存在的定时器列表,本章将学习如何通过特征码定位的方式寻找Windows 10
系统下面的PspCidTable
内核句柄表地址。
PspCidTable 就是这样的一种表(内核句柄表)
,表的内部存放的是进程EPROCESS
和线程ETHREAD
的内核对象,并通过进程PID
和线程TID
进行索引,ID号以4递增,内核句柄表不属于任何进程,也不连接在系统的句柄表上,通过它可以返回系统的任何对象。
首先第一步先要得到PspCidTable
函数内存地址,输入dp PspCidTable
即可得到,如果在程序中则是调用MmGetSystemRoutineAddress
取到。
PspCidTable是一个HANDLE_TALBE
结构,当新建一个进程时,对应的会在PspCidTable
存在一个该进程和线程对应的HANDLE_TABLE_ENTRY
项。在windows10
中依然采用动态扩展
的方法,当句柄数少的时候就采用下层表,多的时候才启用中层表或上层表。
总结起来一个句柄表有一个上层表,一个上层表最多可以有256
个中层表的入口指针,每个中层表最多可以有256
个下层表的入口指针,每个下层表最多可以有256
个进程和线程对象的指针。PspCidTable
表可以看成是HANDLE_TBALE_ENTRY
项的多级索引。
如上图所示TableCode
是指向句柄表的指针,低二位(二进制)记录句柄表的等级:0(00)表示一级表,1(01)表示二级表,2(10)表示三级表。这里的 0xffffdc88-7d09b001
就说名它是一个二级表。
x64 系统中,每张表的大小是 0x1000(4096)
,一级表中存放的是 _handle_table_entry
结构(大小 = 16)
,二级表和三级表存放的是指针(大小 = 8)
。
继续查看第一张一级表,输入dp 0xffffdc887962a000
命令,我们知道一级句柄表是根据进程或线程ID来索引的,且以4累加,所以第一行对应id = 0
,第二行对应id = 4
。根据尝试,PID = 4
的进程是System
。
所以此处的第二行0xb281de28-3300ffa7
就是加密后的System
进程的EPROCESS
结构,对于Win10系统来说解密算法(value >> 0x10) & 0xfffffffffffffff0
是这样的,我们通过代码计算出来。
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
101
102
103
/
/
获取 PspCidTable
/
/
By: LyShark.com
BOOLEAN get_PspCidTable(ULONG64
*
tableAddr)
{
/
/
获取 PsLookupProcessByProcessId 地址
UNICODE_STRING uc_funcName;
RtlInitUnicodeString(&uc_funcName, L
"PsLookupProcessByProcessId"
);
ULONG64 ul_funcAddr
=
MmGetSystemRoutineAddress(&uc_funcName);
if
(ul_funcAddr
=
=
NULL)
{
return
FALSE;
}
DbgPrint(
"PsLookupProcessByProcessId addr = %p \n"
, ul_funcAddr);
/
/
前
40
字节有 call(PspReferenceCidTableEntry)
/
*
0
: kd> uf PsLookupProcessByProcessId
nt!PsLookupProcessByProcessId:
fffff802`
0841cfe0
48895c2418
mov qword ptr [rsp
+
18h
],rbx
fffff802`
0841cfe5
56
push rsi
fffff802`
0841cfe6
4883ec20
sub rsp,
20h
fffff802`
0841cfea
48897c2438
mov qword ptr [rsp
+
38h
],rdi
fffff802`
0841cfef
488bf2
mov rsi,rdx
fffff802`
0841cff2
65488b3c2588010000
mov rdi,qword ptr gs:[
188h
]
fffff802`
0841cffb
66ff8fe6010000
dec word ptr [rdi
+
1E6h
]
fffff802`
0841d002
b203 mov dl,
3
fffff802`
0841d004
e887000000 call nt!PspReferenceCidTableEntry (fffff802`
0841d090
)
fffff802`
0841d009
488bd8
mov rbx,rax
fffff802`
0841d00c
4885c0
test rax,rax
fffff802`
0841d00f
7435
je nt!PsLookupProcessByProcessId
+
0x66
(fffff802`
0841d046
) Branch
*
/
ULONG64 ul_entry
=
0
;
for
(
INT
i
=
0
; i <
100
; i
+
+
)
{
/
/
fffff802`
0841d004
e8
87
00
00
00
call nt!PspReferenceCidTableEntry (fffff802`
0841d090
)
if
(
*
(PUCHAR)(ul_funcAddr
+
i)
=
=
0xe8
)
{
ul_entry
=
ul_funcAddr
+
i;
break
;
}
}
if
(ul_entry !
=
0
)
{
/
/
解析 call 地址
INT
i_callCode
=
*
(
INT
*
)(ul_entry
+
1
);
DbgPrint(
"i_callCode = %p \n"
, i_callCode);
ULONG64 ul_callJmp
=
ul_entry
+
i_callCode
+
5
;
DbgPrint(
"ul_callJmp = %p \n"
, ul_callJmp);
/
/
来到 call(PspReferenceCidTableEntry) 内找 PspCidTable
/
*
0
: kd> uf PspReferenceCidTableEntry
nt!PspReferenceCidTableEntry
+
0x115
:
fffff802`
0841d1a5
488b0d8473f5ff
mov rcx,qword ptr [nt!PspCidTable (fffff802`
08374530
)]
fffff802`
0841d1ac
b801000000 mov eax,
1
fffff802`
0841d1b1
f0480fc107 lock xadd qword ptr [rdi],rax
fffff802`
0841d1b6
4883c130
add rcx,
30h
fffff802`
0841d1ba
f0830c2400 lock
or
dword ptr [rsp],
0
fffff802`
0841d1bf
48833900
cmp
qword ptr [rcx],
0
fffff802`
0841d1c3
0f843fffffff
je nt!PspReferenceCidTableEntry
+
0x78
(fffff802`
0841d108
) Branch
*
/
for
(
INT
i
=
0
; i <
0x120
; i
+
+
)
{
/
/
fffff802`
0841d1a5
48
8b
0d
84
73
f5 ff mov rcx,qword ptr [nt!PspCidTable (fffff802`
08374530
)]
if
(
*
(PUCHAR)(ul_callJmp
+
i)
=
=
0x48
&&
*
(PUCHAR)(ul_callJmp
+
i
+
1
)
=
=
0x8b
&&
*
(PUCHAR)(ul_callJmp
+
i
+
2
)
=
=
0x0d
)
{
/
/
解析 mov 地址
INT
i_movCode
=
*
(
INT
*
)(ul_callJmp
+
i
+
3
);
DbgPrint(
"i_movCode = %p \n"
, i_movCode);
ULONG64 ul_movJmp
=
ul_callJmp
+
i
+
i_movCode
+
7
;
DbgPrint(
"ul_movJmp = %p \n"
, ul_movJmp);
/
/
得到 PspCidTable
*
tableAddr
=
ul_movJmp;
return
TRUE;
}
}
}
return
FALSE;
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint((
"Uninstall Driver Is OK \n"
));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint((
"hello lyshark \n"
));
ULONG64 tableAddr
=
0
;
get_PspCidTable(&tableAddr);
DbgPrint(
"PspCidTable Address = %p \n"
, tableAddr);
Driver
-
>DriverUnload
=
UnDriver;
return
STATUS_SUCCESS;
}
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/
/
获取 PspCidTable
/
/
By: LyShark.com
BOOLEAN get_PspCidTable(ULONG64
*
tableAddr)
{
/
/
获取 PsLookupProcessByProcessId 地址
UNICODE_STRING uc_funcName;
RtlInitUnicodeString(&uc_funcName, L
"PsLookupProcessByProcessId"
);
ULONG64 ul_funcAddr
=
MmGetSystemRoutineAddress(&uc_funcName);
if
(ul_funcAddr
=
=
NULL)
{
return
FALSE;
}
DbgPrint(
"PsLookupProcessByProcessId addr = %p \n"
, ul_funcAddr);
/
/
前
40
字节有 call(PspReferenceCidTableEntry)
/
*
0
: kd> uf PsLookupProcessByProcessId
nt!PsLookupProcessByProcessId:
fffff802`
0841cfe0
48895c2418
mov qword ptr [rsp
+
18h
],rbx
fffff802`
0841cfe5
56
push rsi
fffff802`
0841cfe6
4883ec20
sub rsp,
20h
fffff802`
0841cfea
48897c2438
mov qword ptr [rsp
+
38h
],rdi
fffff802`
0841cfef
488bf2
mov rsi,rdx
fffff802`
0841cff2
65488b3c2588010000
mov rdi,qword ptr gs:[
188h
]
fffff802`
0841cffb
66ff8fe6010000
dec word ptr [rdi
+
1E6h
]
fffff802`
0841d002
b203 mov dl,
3
fffff802`
0841d004
e887000000 call nt!PspReferenceCidTableEntry (fffff802`
0841d090
)
fffff802`
0841d009
488bd8
mov rbx,rax
fffff802`
0841d00c
4885c0
test rax,rax
fffff802`
0841d00f
7435
je nt!PsLookupProcessByProcessId
+
0x66
(fffff802`
0841d046
) Branch
*
/
ULONG64 ul_entry
=
0
;
for
(
INT
i
=
0
; i <
100
; i
+
+
)
{
/
/
fffff802`
0841d004
e8
87
00
00
00
call nt!PspReferenceCidTableEntry (fffff802`
0841d090
)
if
(
*
(PUCHAR)(ul_funcAddr
+
i)
=
=
0xe8
)
{
ul_entry
=
ul_funcAddr
+
i;
break
;
}
}
if
(ul_entry !
=
0
)
{
/
/
解析 call 地址
INT
i_callCode
=
*
(
INT
*
)(ul_entry
+
1
);
DbgPrint(
"i_callCode = %p \n"
, i_callCode);
ULONG64 ul_callJmp
=
ul_entry
+
i_callCode
+
5
;
DbgPrint(
"ul_callJmp = %p \n"
, ul_callJmp);
/
/
来到 call(PspReferenceCidTableEntry) 内找 PspCidTable
/
*
0
: kd> uf PspReferenceCidTableEntry
nt!PspReferenceCidTableEntry
+
0x115
:
fffff802`
0841d1a5
488b0d8473f5ff
mov rcx,qword ptr [nt!PspCidTable (fffff802`
08374530
)]
fffff802`
0841d1ac
b801000000 mov eax,
1
fffff802`
0841d1b1
f0480fc107 lock xadd qword ptr [rdi],rax
fffff802`
0841d1b6
4883c130
add rcx,
30h
fffff802`
0841d1ba
f0830c2400 lock
or
dword ptr [rsp],
0
fffff802`
0841d1bf
48833900
cmp
qword ptr [rcx],
0
fffff802`
0841d1c3
0f843fffffff
je nt!PspReferenceCidTableEntry
+
0x78
(fffff802`
0841d108
) Branch
*
/
for
(
INT
i
=
0
; i <
0x120
; i
+
+
)
{
/
/
fffff802`
0841d1a5
48
8b
0d
84
73
f5 ff mov rcx,qword ptr [nt!PspCidTable (fffff802`
08374530
)]
if
(
*
(PUCHAR)(ul_callJmp
+
i)
=
=
0x48
&&
*
(PUCHAR)(ul_callJmp
+
i
+
1
)
=
=
0x8b
&&
*
(PUCHAR)(ul_callJmp
+
i
+
2
)
=
=
0x0d
)
{
/
/
解析 mov 地址
INT
i_movCode
=
*
(
INT
*
)(ul_callJmp
+
i
+
3
);
DbgPrint(
"i_movCode = %p \n"
, i_movCode);
ULONG64 ul_movJmp
=
ul_callJmp
+
i
+
i_movCode
+
7
;
DbgPrint(
"ul_movJmp = %p \n"
, ul_movJmp);
/
/
得到 PspCidTable
*
tableAddr
=
ul_movJmp;
return
TRUE;
}
}
}
return
FALSE;
}
/
*
解析一级表
/
/
By: LyShark.com
BaseAddr:一级表的基地址
index1:第几个一级表
index2:第几个二级表
*
/
VOID parse_table_1(ULONG64 BaseAddr,
INT
index1,
INT
index2)
{
/
/
遍历一级表(每个表项大小
16
),表大小
4k
,所以遍历
4096
/
16
=
526
次
PEPROCESS p_eprocess
=
NULL;
PETHREAD p_ethread
=
NULL;
INT
i_id
=
0
;
for
(
INT
i
=
0
; i <
256
; i
+
+
)
{
if
(!MmIsAddressValid((PVOID64)(BaseAddr
+
i
*
16
)))
{
DbgPrint(
"非法地址= %p \n"
, BaseAddr
+
i
*
16
);
continue
;
}
ULONG64 ul_recode
=
*
(PULONG64)(BaseAddr
+
i
*
16
);
/
/
解密
ULONG64 ul_decode
=
(LONG64)ul_recode >>
0x10
;
ul_decode &
=
0xfffffffffffffff0
;
/
/
判断是进程还是线程
i_id
=
i
*
4
+
1024
*
index1
+
512
*
index2
*
1024
;
if
(PsLookupProcessByProcessId(i_id, &p_eprocess)
=
=
STATUS_SUCCESS)
{
DbgPrint(
"进程PID: %d | ID: %d | 内存地址: %p | 对象: %p \n"
, i_id, i, BaseAddr
+
i
*
0x10
, ul_decode);
}
else
if
(PsLookupThreadByThreadId(i_id, &p_ethread)
=
=
STATUS_SUCCESS)
{
DbgPrint(
"线程TID: %d | ID: %d | 内存地址: %p | 对象: %p \n"
, i_id, i, BaseAddr
+
i
*
0x10
, ul_decode);
}
}
}
/
*
解析二级表
/
/
By: LyShark.com
BaseAddr:二级表基地址
index2:第几个二级表
*
/
VOID parse_table_2(ULONG64 BaseAddr,
INT
index2)
{
/
/
遍历二级表(每个表项大小
8
),表大小
4k
,所以遍历
4096
/
8
=
512
次
ULONG64 ul_baseAddr_1
=
0
;
for
(
INT
i
=
0
; i <
512
; i
+
+
)
{
if
(!MmIsAddressValid((PVOID64)(BaseAddr
+
i
*
8
)))
{
DbgPrint(
"非法二级表指针(1):%p \n"
, BaseAddr
+
i
*
8
);
continue
;
}
if
(!MmIsAddressValid((PVOID64)
*
(PULONG64)(BaseAddr
+
i
*
8
)))
{
DbgPrint(
"非法二级表指针(2):%p \n"
, BaseAddr
+
i
*
8
);
continue
;
}
ul_baseAddr_1
=
*
(PULONG64)(BaseAddr
+
i
*
8
);
parse_table_1(ul_baseAddr_1, i, index2);
}
}
/
*
解析三级表
/
/
By: LyShark.com
BaseAddr:三级表基地址
*
/
VOID parse_table_3(ULONG64 BaseAddr)
{
/
/
遍历三级表(每个表项大小
8
),表大小
4k
,所以遍历
4096
/
8
=
512
次
ULONG64 ul_baseAddr_2
=
0
;
for
(
INT
i
=
0
; i <
512
; i
+
+
)
{
if
(!MmIsAddressValid((PVOID64)(BaseAddr
+
i
*
8
)))
{
continue
;
}
if
(!MmIsAddressValid((PVOID64)
*
(PULONG64)(BaseAddr
+
i
*
8
)))
{
continue
;
}
ul_baseAddr_2
=
*
(PULONG64)(BaseAddr
+
i
*
8
);
parse_table_2(ul_baseAddr_2, i);
}
}
VOID UnDriver(PDRIVER_OBJECT driver)
{
DbgPrint((
"Uninstall Driver Is OK \n"
));
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
DbgPrint((
"hello lyshark.com \n"
));
ULONG64 tableAddr
=
0
;
get_PspCidTable(&tableAddr);
DbgPrint(
"PspCidTable Address = %p \n"
, tableAddr);
/
/
获取 _HANDLE_TABLE 的 TableCode
ULONG64 ul_tableCode
=
*
(PULONG64)(((ULONG64)
*
(PULONG64)tableAddr)
+
8
);
DbgPrint(
"ul_tableCode = %p \n"
, ul_tableCode);
/
/
取低
2
位(二级制
11
=
3
)
INT
i_low2
=
ul_tableCode &
3
;
DbgPrint(
"i_low2 = %X \n"
, i_low2);
/
/
一级表
if
(i_low2
=
=
0
)
{
/
/
TableCode 低
2
位抹零(二级制
11
=
3
)
parse_table_1(ul_tableCode & (~
3
),
0
,
0
);
}
/
/
二级表
else
if
(i_low2
=
=
1
)
{
/
/
TableCode 低
2
位抹零(二级制
11
=
3
)
parse_table_2(ul_tableCode & (~
3
),
0
);
}
/
/
三级表
else
if
(i_low2
=
=
2
)
{
/
/
TableCode 低
2
位抹零(二级制
11
=
3
)
parse_table_3(ul_tableCode & (~
3
));
}
else
{
DbgPrint(
"LyShark提示: 错误,非法! "
);
return
FALSE;
}
Driver
-
>DriverUnload
=
UnDriver;
return
STATUS_SUCCESS;
}
至此文章就结束了,这里多说一句,实际上ZwQuerySystemInformation
枚举系统句柄时就是走的这条双链,枚举系统进程如果使用的是这个API函数,那么不出意外它也是在这些内核表中做的解析。
http://www.blogfshare.com/details-in-pspcidtbale.html
https://blog.csdn.net/whatday/article/details/17189093
https://www.cnblogs.com/kuangke/p/5761615.html
https://www.cnblogs.com/LyShark/p/16796158.html