【软件逆向-x64内核实验3-页机制的研究】此文章归类为:软件逆向。
在白皮书的第三卷2.5章对控制寄存器有详细的介绍,下面是白皮书中CR寄存器的结构图(这里要说明一下cr4的第12位是由被使用的这个位在2.5章的后半部分有介绍是控制是否开启5级分页的位否则是四级分页)
首先是smep和smap两个位,这部分因为之前的实验会用到所以已经在段机制的部分介绍过了,这里就不再赘述
其余一些位置的功能后面用到了会继续介绍,这边有个结构的概念就可以了
下面我们来梳理一下64位cpu中分页机制的变化过程:
简单的讲一下上面这张图
图里的第一行是CR3的描述
第二行PML5E是五级分页才会用到的这里不多赘述五级分页因为我还没有碰到五级分页的操作系统
第三行的PML4E是一级页表项当p=1时说明当前页表项有效否则是无效
第四行的PDPTE是二级页表项,当p=1时有效而且ps位=1的时候是1G的大页(第7位)1G的话大家可以拆分一下看看1G需要的页内寻址偏移为30位,我们的虚拟地址到拆分到二级的时候使用掉了18位正好剩下30位意味着我们的分页变为了9-9-30,如果ps=0的话就还按照9-9-9-9-12分页
第五行的PDE是三级页表项,当p=1时有效而且ps位=1的时候是2MB的大页(第7位)2MB的话大家可以拆分一下看看2MB需要的页内寻址偏移为21位,我们的虚拟地址到拆分到三级的时候使用掉了27位正好剩下30位意味着我们的分页变为了9-9-9-21,如果ps=0的话就还按照9-9-9-9-12分页
第六行的PTE是二级页表项目当p=1时有效
图中在4级以及以上的位置使用了PMLXE的描述方式,后面的则跟2-9-9-12时候的命名一样我觉得可能记起来比较麻烦所以就按照页表的级别来描述几级就是几级页表里面的项就是X级页表项这种叫法
各个位的介绍如下:
下面我们来拆分一个虚拟地址看一下
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
|
fffff803`
45299fc0
00209b00
`
00000000
00409300
`
00000000
0
: kd> r cr3
cr3
=
00000000001aa000
这是我环境中的GDT表中的一项,我的cr3是
=
00000000001aa000
,下面让我门来拆一下
11111111
11111111
11111000
00000011
01000101
00101001
10011111
10110000
上面是拆成二进制的形式
高
16
位不关注:fffff
1
级页表偏移:
11111000
0
-
-
1f0
2
级页表偏移:
0000011
01
-
-
0d
3
级页表偏移:
000101
001
-
-
29
4
级页表偏移:
01001
1001
-
-
99
页内偏移:fc0
kd> !dq
00000000001aa000
+
1f0
*
8
# 1aaf80 00000000`05209063 00000000`00000000
# 1aaf90 00000000`00000000 00000000`00000000
# 1aafa0 00000000`00000000 00000000`00000000
# 1aafb0 00000000`00000000 00000000`00000000
# 1aafc0 00000000`00000000 00000000`00000000
# 1aafd0 00000000`00000000 00000000`00000000
# 1aafe0 00000000`00000000 00000000`00000000
# 1aaff0 00000000`00000000 00000000`05123063
0
: kd> !dq
00000000
`
05209000
+
d
*
8
# 5209068 00000000`05215063 0a000000`33a85863
# 5209078 0a000001`0ed50863 0a000000`88607863
# 5209088 00000000`00000000 00000000`00000000
# 5209098 00000000`00000000 00000000`00000000
# 52090a8 00000000`00000000 00000000`00000000
# 52090b8 00000000`00000000 00000000`00000000
# 52090c8 00000000`00000000 00000000`00000000
# 52090d8 00000000`00000000 00000000`00000000
0
: kd> !dq
00000000
`
05215000
+
29
*
8
# 5215148 00000000`05122063 0a000001`1cd12863
# 5215158 0a000000`01009863 0a000000`0100a863
# 5215168 0a000000`0120b863 0a000000`05d35863
# 5215178 0a000001`03748863 0a000001`044a2863
# 5215188 0a000001`044a3863 0a000001`044a4863
# 5215198 0a000001`044a5863 0a000001`044be863
# 52151a8 0a000001`044bf863 0a000001`0442c863
# 52151b8 0a000001`0442d863 0a000001`0442e863
0
: kd> !dq
00000000
`
05122000
+
99
*
8
# 51224c8 89000000`06499963 89000000`0649a963
# 51224d8 89000000`0649b963 89000000`0649c963
# 51224e8 00000000`00000000 89000000`0649e963
# 51224f8 89000000`0649f963 89000000`064a0963
# 5122508 89000000`064a1963 89000000`064a2963
# 5122518 89000000`064a3963 00000000`00000000
# 5122528 89000000`064a5963 89000000`064a6963
# 5122538 89000000`064a7963 89000000`064a8963
0
: kd> !dq
6499000
+
fc0
# 6499fc0 00209b00`00000000 00409300`00000000
可以看到我们手工拆分查出来的跟虚拟地址里存的是一样的,我们还可以通过!pte命令来验证
0
: kd> !pte fffff803`
45299fc0
VA fffff80345299fc0
PXE at FFFFEEF77BBDDF80 PPE at FFFFEEF77BBF0068 PDE at FFFFEEF77E00D148 PTE at FFFFEEFC01A294C8
contains
0000000005209063
contains
0000000005215063
contains
0000000005122063
contains
8900000006499963
pfn
5209
-
-
-
DA
-
-
KWEV pfn
5215
-
-
-
DA
-
-
KWEV pfn
5122
-
-
-
DA
-
-
KWEV pfn
6499
-
G
-
DA
-
-
KW
-
V
|
然后我们就可以做几个实验来熟悉一下这个拆分过程
实验1:编写驱动,用代码拆分获取idt表的物理地址(复现我们刚刚手工的拆分过程)并手工拆分验证
实验代码:
x64Common.h
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
|
#pragma once
#include <wdm.h>
#include <intrin.h>
#pragma pack(1)
struct Attribute
{
UINT64 offset1 :
16
;
UINT64 p :
1
;
UINT64 dpl :
2
;
UINT64 s :
1
;
UINT64
type
:
4
;
UINT64 unuse :
6
;
UINT64 ist :
2
;
UINT64 selector :
16
;
UINT64 offset2 :
16
;
};
typedef struct _IDT_ENTRY64 {
union hightStruct
{
UINT64 lower;
struct Attribute attribute;
};
UINT64 hight;
}IDT_ENTRY64,
*
PIDT_ENTRY64;
typedef struct _IDTR
{
UINT16 limit;
UINT64 base;
}IDTR,
*
PIDTR;
/
/
/
<summary>
/
/
/
cr4结构体
/
/
/
<
/
summary>
typedef union _CR4 {
UINT64 value;
struct
{
UINT64 VME :
1
;
UINT64 PVI :
1
;
UINT64 TSD :
1
;
UINT64 DE :
1
;
UINT64 PSE :
1
;
UINT64 PAE :
1
;
UINT64 MCE :
1
;
UINT64 PGE :
1
;
UINT64 PCE :
1
;
UINT64 OSFXSR :
1
;
UINT64 OSXMMEXCPT :
1
;
UINT64 UMIP :
1
;
UINT64 LA57 :
1
;
UINT64 VMXE :
1
;
UINT64 SMXE :
1
;
UINT64 unuse1 :
1
;
UINT64 FSGSBASE :
1
;
UINT64 PCIDE :
1
;
UINT64 OSXSAVE :
1
;
UINT64 KL :
1
;
UINT64 SMEP :
1
;
UINT64 SMAP :
1
;
UINT64 PKE :
1
;
UINT64 CET :
1
;
UINT64 PKS :
1
;
UINT64 Ressrved :
63
-
24
;
}Fields;
}CR4,
*
PCR4;
static_assert(sizeof(CR4)
=
=
8
,
"sizeof CR4"
);
/
/
/
<summary>
/
/
/
cr3结构体
/
/
/
<
/
summary>
typedef union _CR3 {
UINT64 value;
struct
{
UINT64 ignore1 :
3
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 ignore2 :
7
;
UINT64 PPN :
40
;
UINT64 Reserved1 :
12
;
}Fields;
}CR3,
*
PCR3;
static_assert(sizeof(CR3)
=
=
8
,
"sizeof CR3"
);
/
/
/
<summary>
/
/
/
各页表项结构体
/
/
/
<
/
summary>
typedef union _PA {
UINT64 vaule;
LARGE_INTEGER AsLargeInteger;
struct
{
UINT64 PPO :
12
;
UINT64 PPN :
40
;
UINT64 UnUse1 :
12
;
}Fileds4KB;
struct
{
UINT64 PPO :
21
;
UINT64 PPN :
31
;
UINT64 UnUse1 :
12
;
}Fileds2MB;
struct
{
UINT64 PPO :
30
;
UINT64 PPN :
22
;
UINT64 UnUse1 :
12
;
}Fileds1GB;
}PA,
*
P_PA;
static_assert(sizeof(PA)
=
=
8
,
"sizeof PA"
);
/
/
/
<summary>
/
/
/
虚拟地址结构体
/
/
/
<
/
summary>
typedef union _VA {
UINT64 vaule;
LARGE_INTEGER AsLargeInteger;
struct
{
UINT64 VPO :
12
;
UINT64 VPN4 :
9
;
UINT64 VPN3 :
9
;
UINT64 VPN2 :
9
;
UINT64 VPN1 :
9
;
UINT64 UnUse1 :
16
;
}Fileds4KB;
struct
{
UINT64 VPO :
21
;
UINT64 VPN3 :
9
;
UINT64 VPN2 :
9
;
UINT64 VPN1 :
9
;
UINT64 UnUse1 :
16
;
}Fileds2MB;
struct
{
UINT64 VPO :
30
;
UINT64 VPN2 :
9
;
UINT64 VPN1 :
9
;
UINT64 UnUse1 :
16
;
}Fileds1GB;
}VA,
*
P_VA;
static_assert(sizeof(VA)
=
=
8
,
"sizeof VA"
);
typedef union _PML4E {
UINT64 value;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
5
;
UINT64 R :
1
;
UINT64 PPN :
36
;
UINT64 ign2 :
15
;
UINT64 XD :
1
;
}Fields4K;
}PML4E,
*
PPML4E, L1PTE,
*
PL1PTE;
static_assert(sizeof(PML4E)
=
=
8
,
"sizeof PML4E"
);
typedef union _PDPTE {
UINT64 value;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
1
;
UINT64 PS :
1
;
UINT64 ign2 :
3
;
UINT64 R :
1
;
UINT64 PPN :
36
;
UINT64 ign3 :
15
;
UINT64 XD :
1
;
}Fields4K;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
1
;
UINT64 PS :
1
;
UINT64 ign2 :
3
;
UINT64 R :
1
;
UINT64 Reserved :
18
;
UINT64 PPN :
18
;
UINT64 ign3 :
15
;
UINT64 XD :
1
;
}Fields1G;
}PDPTE,
*
PPDPTE, L2PTE,
*
PL2PTE;
static_assert(sizeof(PDPTE)
=
=
8
,
"sizeof PDPTE"
);
typedef union _PDE {
UINT64 value;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
1
;
UINT64 PS :
1
;
UINT64 ign2 :
3
;
UINT64 R :
1
;
UINT64 PPN :
36
;
UINT64 ign3 :
15
;
UINT64 XD :
1
;
}Fields4K;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
1
;
UINT64 PS :
1
;
UINT64 ign2 :
3
;
UINT64 R :
1
;
UINT64 Reserved :
9
;
UINT64 PPN :
27
;
UINT64 ign3 :
15
;
UINT64 XD :
1
;
}Fields2MB;
}PDE,
*
PPDE, L3PTE,
*
PL3PTE;
static_assert(sizeof(PDE)
=
=
8
,
"sizeof PDE"
);
typedef union _PTE {
UINT64 value;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
1
;
UINT64 PS :
1
;
UINT64 ign2 :
3
;
UINT64 R :
1
;
UINT64 PPN :
36
;
UINT64 ign3 :
15
;
UINT64 XD :
1
;
}Fields4K;
}PTE,
*
PPTE, L4PTE,
*
PL4PTE;
static_assert(sizeof(PDE)
=
=
8
,
"sizeof PTE"
);
#pragma pack()
/
/
各个页表存储了
512
个页表项
#define MIX_PAGETABLEENTRY_SIZE 512
/
/
-
-
-
-
-
-
内核导出函数声明:begin
-
-
-
-
-
-
/
/
extern PVOID __stdcall MmGetVirtualForPhysical(LARGE_INTEGER AsLargeInteger);
extern NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS
*
Process);
/
/
-
-
-
-
-
-
内核导出函数声明: end
-
-
-
-
-
-
/
/
|
main.c
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
|
#include <wdm.h>
/
/
#include <ntifs.h>
#define NTSTRSAFE_LIB
#include <ntstrsafe.h>
#include "x64Common.h"
VOID Unload(PDRIVER_OBJECT pDriver) {
KdPrint((
"unload\r\n"
));
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
NTSTATUS status
=
STATUS_SUCCESS;
pDriver
-
>DriverUnload
=
Unload;
KdPrint((
"start\r\n"
));
CR4 cr4
=
{ .value
=
__readcr4() };
cr4.Fields.SMAP
=
0
;
cr4.Fields.SMEP
=
0
;
__writecr4(cr4.value);
IDTR idtr
=
{
0
};
__sidt(&idtr);
VA idtVa
=
{ .vaule
=
idtr.base };
CR3 cr3
=
{ .value
=
__readcr3() };
PA pa
=
{
0
};
pa.Fileds4KB.PPN
=
cr3.Fields.PPN;
/
/
PL1PTE L1
=
MmGetVirtualForPhysical(pa.AsLargeInteger);
KdPrint((
"当前idt1级页表对应的虚拟地址地址0x%p\r\n"
, L1));
pa.Fileds4KB.PPN
=
L1[idtVa.Fileds4KB.VPN1].Fields4K.PPN;
KdPrint((
"当前idt1级页表项对应的物理地址地址0x%llx\r\n"
, pa.vaule));
PL2PTE L2
=
MmGetVirtualForPhysical(pa.AsLargeInteger);
KdPrint((
"当前idt2级页表对应的虚拟地址地址0x%p\r\n"
, L2));
pa.Fileds4KB.PPN
=
L2[idtVa.Fileds4KB.VPN2].Fields4K.PPN;
KdPrint((
"当前idt2级页表项对应的物理地址地址0x%llx\r\n"
, pa.vaule));
PL3PTE L3
=
MmGetVirtualForPhysical(pa.AsLargeInteger);
KdPrint((
"当前idt3级页表对应的虚拟地址地址0x%p\r\n"
, L3));
pa.Fileds4KB.PPN
=
L3[idtVa.Fileds4KB.VPN3].Fields4K.PPN;
KdPrint((
"当前idt3级页表项对应的物理地址地址0x%llx\r\n"
, pa.vaule));
PL4PTE L4
=
MmGetVirtualForPhysical(pa.AsLargeInteger);
KdPrint((
"当前idt4级页表对应的虚拟地址地址0x%p\r\n"
, L4));
pa.Fileds4KB.PPN
=
L4[idtVa.Fileds4KB.VPN4].Fields4K.PPN;
pa.Fileds4KB.PPO
=
idtVa.Fileds4KB.VPO;
KdPrint((
"当前idt对应的物理地址地址0x%llx\r\n"
, pa.vaule));
KdPrint((
"end\r\n"
));
return
status;
}
|
打印结果如下
1
2
3
4
5
6
7
8
9
10
|
start
当前idt1级页表对应的虚拟地址地址
0xFFFFC06030180000
当前idt1级页表项对应的物理地址地址
0x4f2e000
当前idt2级页表对应的虚拟地址地址
0xFFFFC06030190000
当前idt2级页表项对应的物理地址地址
0x4f2f000
当前idt3级页表对应的虚拟地址地址
0xFFFFC06032001000
当前idt3级页表项对应的物理地址地址
0x11cc6f000
当前idt4级页表对应的虚拟地址地址
0xFFFFC06400206000
当前idt对应的物理地址地址
0x2851000
end
|
手工拆分
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
|
1
: kd> r idtr
idtr
=
ffffc80040dd5000
Binary:
11111111
11111111
11001000
00000000
01000000
11011101
01010000
00000000
高
16
位不关注:fffff
1
级页表偏移:
11001000
0
-
-
190
2
级页表偏移:
0000000
01
-
-
01
3
级页表偏移:
000000
110
-
-
06
4
级页表偏移:
11101
0101
-
-
1d5
页内偏移:
000
kd> r cr3
cr3
=
00000000001aa000
1
: kd> !dq
00000000001aa000
+
190
*
8
# 1aac80 0a000000`04f2e863 00000000`00000000
# 1aac90 00000000`00000000 00000000`00000000
# 1aaca0 00000000`00000000 00000000`00000000
# 1aacb0 00000000`00000000 00000000`00000000
# 1aacc0 00000000`00000000 00000000`00000000
# 1aacd0 00000000`00000000 00000000`00000000
# 1aace0 00000000`00000000 00000000`00000000
# 1aacf0 00000000`00000000 00000000`00000000
1
: kd> !dq
4f2e000
+
8
# 4f2e008 0a000000`04f2f863 00000000`00000000
# 4f2e018 00000000`00000000 00000000`00000000
# 4f2e028 00000000`00000000 00000000`00000000
# 4f2e038 00000000`00000000 00000000`00000000
# 4f2e048 00000000`00000000 00000000`00000000
# 4f2e058 00000000`00000000 00000000`00000000
# 4f2e068 00000000`00000000 00000000`00000000
# 4f2e078 00000000`00000000 00000000`00000000
1
: kd> !dq
4f2f000
+
6
*
8
# 4f2f030 0a000001`1cc6f863 0a000001`1cce3863
# 4f2f040 0a000001`1cd78863 0a000001`1cd62863
# 4f2f050 0a000000`0411b863 0a000001`1ff1c863
# 4f2f060 0a000000`0651d863 0a000000`01f1e863
# 4f2f070 0a000000`0131f863 0a000000`0559f863
# 4f2f080 0a000000`013a0863 0a000000`010a2863
# 4f2f090 0a000000`013a3863 0a000000`010a4863
# 4f2f0a0 0a000000`013a5863 0a000000`011a6863
1
: kd> !dq
1
`
1cc6f000
+
1d5
*
8
#11cc6fea8 8a000000`02851121 8a000000`02852963
#11cc6feb8 8a000000`02853963 8a000000`02854963
#11cc6fec8 8a000000`02855963 8a000000`02856963
#11cc6fed8 8a000000`dffc1963 8a000000`dffc2963
#11cc6fee8 8a000000`dffc3963 00000000`00000000
#11cc6fef8 00000000`00000000 00000000`00000000
#11cc6ff08 00000000`00000000 00000000`00000000
#11cc6ff18 00000000`00000000 8a000000`dffbe963
1
: kd> !dq
2851000
# 2851000 48e08e00`00106300 00000000`fffff805
# 2851010 48e08e04`00106640 00000000`fffff805
# 2851020 48e08e03`00106b00 00000000`fffff805
# 2851030 48e0ee00`00106fc0 00000000`fffff805
# 2851040 48e0ee00`00107300 00000000`fffff805
# 2851050 48e08e00`00107640 00000000`fffff805
# 2851060 48e08e00`00107c80 00000000`fffff805
# 2851070 48e08e00`00108280 00000000`fffff805
1
: kd> dq idtr
ffffc800`
40dd5000
48e08e00
`
00106300
00000000
`fffff805
ffffc800`
40dd5010
48e08e04
`
00106640
00000000
`fffff805
ffffc800`
40dd5020
48e08e03
`
00106b00
00000000
`fffff805
ffffc800`
40dd5030
48e0ee00
`
00106fc0
00000000
`fffff805
ffffc800`
40dd5040
48e0ee00
`
00107300
00000000
`fffff805
ffffc800`
40dd5050
48e08e00
`
00107640
00000000
`fffff805
ffffc800`
40dd5060
48e08e00
`
00107c80
00000000
`fffff805
ffffc800`
40dd5070
48e08e00
`
00108280
00000000
`fffff805
|
测试应用程序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <Windows.h>
#include <stdio.h>
DWORD32 testVal
=
111
;
int
main() {
printf(
"pid: %d\r\n"
,GetCurrentProcessId());
while
(true)
{
system(
"pause"
);
printf(
"addr: 0x%p\r\n"
, &testVal);
printf(
"val: %d\r\n"
, testVal);
}
return
0
;
}
|
根据pid和addr编写我们的驱动代码
驱动:
x64Common.h
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
|
#pragma once
#include <ntifs.h>
#include <ntddk.h>
#define NTSTRSAFE_LIB
#include <ntstrsafe.h>
#include <intrin.h>
#pragma pack(1)
struct Attribute
{
UINT64 offset1 :
16
;
UINT64 p :
1
;
UINT64 dpl :
2
;
UINT64 s :
1
;
UINT64
type
:
4
;
UINT64 unuse :
6
;
UINT64 ist :
2
;
UINT64 selector :
16
;
UINT64 offset2 :
16
;
};
typedef struct _IDT_ENTRY64 {
union hightStruct
{
UINT64 lower;
struct Attribute attribute;
};
UINT64 hight;
}IDT_ENTRY64,
*
PIDT_ENTRY64;
typedef struct _IDTR
{
UINT16 limit;
UINT64 base;
}IDTR,
*
PIDTR;
/
/
/
<summary>
/
/
/
cr4结构体
/
/
/
<
/
summary>
typedef union _CR4 {
UINT64 value;
struct
{
UINT64 VME :
1
;
UINT64 PVI :
1
;
UINT64 TSD :
1
;
UINT64 DE :
1
;
UINT64 PSE :
1
;
UINT64 PAE :
1
;
UINT64 MCE :
1
;
UINT64 PGE :
1
;
UINT64 PCE :
1
;
UINT64 OSFXSR :
1
;
UINT64 OSXMMEXCPT :
1
;
UINT64 UMIP :
1
;
UINT64 LA57 :
1
;
UINT64 VMXE :
1
;
UINT64 SMXE :
1
;
UINT64 unuse1 :
1
;
UINT64 FSGSBASE :
1
;
UINT64 PCIDE :
1
;
UINT64 OSXSAVE :
1
;
UINT64 KL :
1
;
UINT64 SMEP :
1
;
UINT64 SMAP :
1
;
UINT64 PKE :
1
;
UINT64 CET :
1
;
UINT64 PKS :
1
;
UINT64 Ressrved :
63
-
24
;
}Fields;
}CR4,
*
PCR4;
static_assert(sizeof(CR4)
=
=
8
,
"sizeof CR4"
);
/
/
/
<summary>
/
/
/
cr3结构体
/
/
/
<
/
summary>
typedef union _CR3 {
UINT64 value;
struct
{
UINT64 ignore1 :
3
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 ignore2 :
7
;
UINT64 PPN :
40
;
UINT64 Reserved1 :
12
;
}Fields;
}CR3,
*
PCR3;
static_assert(sizeof(CR3)
=
=
8
,
"sizeof CR3"
);
/
/
/
<summary>
/
/
/
各页表项结构体
/
/
/
<
/
summary>
typedef union _PA {
UINT64 vaule;
LARGE_INTEGER AsLargeInteger;
struct
{
UINT64 PPO :
12
;
UINT64 PPN :
40
;
UINT64 UnUse1 :
12
;
}Fileds4KB;
struct
{
UINT64 PPO :
21
;
UINT64 PPN :
31
;
UINT64 UnUse1 :
12
;
}Fileds2MB;
struct
{
UINT64 PPO :
30
;
UINT64 PPN :
22
;
UINT64 UnUse1 :
12
;
}Fileds1GB;
}PA,
*
P_PA;
static_assert(sizeof(PA)
=
=
8
,
"sizeof PA"
);
/
/
/
<summary>
/
/
/
虚拟地址结构体
/
/
/
<
/
summary>
typedef union _VA {
UINT64 vaule;
LARGE_INTEGER AsLargeInteger;
struct
{
UINT64 VPO :
12
;
UINT64 VPN4 :
9
;
UINT64 VPN3 :
9
;
UINT64 VPN2 :
9
;
UINT64 VPN1 :
9
;
UINT64 UnUse1 :
16
;
}Fileds4KB;
struct
{
UINT64 VPO :
21
;
UINT64 VPN3 :
9
;
UINT64 VPN2 :
9
;
UINT64 VPN1 :
9
;
UINT64 UnUse1 :
16
;
}Fileds2MB;
struct
{
UINT64 VPO :
30
;
UINT64 VPN2 :
9
;
UINT64 VPN1 :
9
;
UINT64 UnUse1 :
16
;
}Fileds1GB;
}VA,
*
P_VA;
static_assert(sizeof(VA)
=
=
8
,
"sizeof VA"
);
typedef union _PML4E {
UINT64 value;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
5
;
UINT64 R :
1
;
UINT64 PPN :
36
;
UINT64 ign2 :
15
;
UINT64 XD :
1
;
}Fields4K;
}PML4E,
*
PPML4E, L1PTE,
*
PL1PTE;
static_assert(sizeof(PML4E)
=
=
8
,
"sizeof PML4E"
);
typedef union _PDPTE {
UINT64 value;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
1
;
UINT64 PS :
1
;
UINT64 ign2 :
3
;
UINT64 R :
1
;
UINT64 PPN :
36
;
UINT64 ign3 :
15
;
UINT64 XD :
1
;
}Fields4K;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
1
;
UINT64 PS :
1
;
UINT64 ign2 :
3
;
UINT64 R :
1
;
UINT64 Reserved :
18
;
UINT64 PPN :
18
;
UINT64 ign3 :
15
;
UINT64 XD :
1
;
}Fields1G;
}PDPTE,
*
PPDPTE, L2PTE,
*
PL2PTE;
static_assert(sizeof(PDPTE)
=
=
8
,
"sizeof PDPTE"
);
typedef union _PDE {
UINT64 value;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
1
;
UINT64 PS :
1
;
UINT64 ign2 :
3
;
UINT64 R :
1
;
UINT64 PPN :
36
;
UINT64 ign3 :
15
;
UINT64 XD :
1
;
}Fields4K;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
1
;
UINT64 PS :
1
;
UINT64 ign2 :
3
;
UINT64 R :
1
;
UINT64 Reserved :
9
;
UINT64 PPN :
27
;
UINT64 ign3 :
15
;
UINT64 XD :
1
;
}Fields2MB;
}PDE,
*
PPDE, L3PTE,
*
PL3PTE;
static_assert(sizeof(PDE)
=
=
8
,
"sizeof PDE"
);
typedef union _PTE {
UINT64 value;
struct
{
UINT64 P :
1
;
UINT64 R_W :
1
;
UINT64 US :
1
;
UINT64 PWT :
1
;
UINT64 PCD :
1
;
UINT64 A :
1
;
UINT64 ign :
1
;
UINT64 PS :
1
;
UINT64 ign2 :
3
;
UINT64 R :
1
;
UINT64 PPN :
36
;
UINT64 ign3 :
15
;
UINT64 XD :
1
;
}Fields4K;
}PTE,
*
PPTE, L4PTE,
*
PL4PTE;
static_assert(sizeof(PDE)
=
=
8
,
"sizeof PTE"
);
#pragma pack()
/
/
各个页表存储了
512
个页表项
#define MIX_PAGETABLEENTRY_SIZE 512
/
/
-
-
-
-
-
-
内核导出函数声明:begin
-
-
-
-
-
-
/
/
extern PVOID __stdcall MmGetVirtualForPhysical(LARGE_INTEGER AsLargeInteger);
/
/
extern NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS
*
Process);
/
/
-
-
-
-
-
-
内核导出函数声明: end
-
-
-
-
-
-
/
/
/
/
-
-
-
-
-
-
导出函数声明
-
-
-
-
-
-
-
/
/
/
/
/
<summary>
/
/
/
虚拟地址转换为物理地址
/
/
/
<
/
summary>
/
/
/
<param name
=
"cr3"
>目标进程的cr3<
/
param>
/
/
/
<param name
=
"pVa"
>虚拟地址结构指针<
/
param>
/
/
/
<param name
=
"outPa"
>出参:物理地址结构指针<
/
param>
/
/
/
<returns><
/
returns>
NTSTATUS Va2Pa(_In_ const PCR3 cr3, _In_ const P_VA pVa, _Out_ P_PA outPa);
|
x64Common.c
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
|
#include "x64Common.h"
NTSTATUS Va2Pa(PCR3 cr3, P_VA pVa, P_PA outPa)
{
__try {
PA pa
=
{ .Fileds4KB.PPN
=
cr3
-
>Fields.PPN };
PL1PTE L1PT
=
MmGetVirtualForPhysical(pa.AsLargeInteger);
PL1PTE pL1PTE
=
&L1PT[pVa
-
>Fileds4KB.VPN1];
if
(pL1PTE
-
>Fields4K.P
=
=
0
)
return
STATUS_UNSUCCESSFUL;
pa.Fileds4KB.PPN
=
pL1PTE
-
>Fields4K.PPN;
PL2PTE L2PT
=
MmGetVirtualForPhysical(pa.AsLargeInteger);
PL2PTE pL2PTE
=
&L2PT[pVa
-
>Fileds4KB.VPN2];
if
(pL2PTE
-
>Fields4K.P
=
=
0
)
return
STATUS_UNSUCCESSFUL;
if
(pL2PTE
-
>Fields1G.PS
=
=
1
) {
outPa
-
>Fileds1GB.PPN
=
pL2PTE
-
>Fields1G.PPN;
outPa
-
>Fileds1GB.PPO
=
pVa
-
>Fileds1GB.VPO;
return
STATUS_SUCCESS;
}
pa.Fileds4KB.PPN
=
pL2PTE
-
>Fields4K.PPN;
PL3PTE L3PT
=
MmGetVirtualForPhysical(pa.AsLargeInteger);
PL3PTE pL3PTE
=
&L3PT[pVa
-
>Fileds4KB.VPN3];
if
(pL3PTE
-
>Fields4K.P
=
=
0
)
return
STATUS_UNSUCCESSFUL;
if
(pL3PTE
-
>Fields2MB.PS
=
=
1
) {
outPa
-
>Fileds2MB.PPN
=
pL3PTE
-
>Fields2MB.PPN;
outPa
-
>Fileds2MB.PPO
=
pVa
-
>Fileds2MB.VPO;
return
STATUS_SUCCESS;
}
pa.Fileds4KB.PPN
=
pL3PTE
-
>Fields4K.PPN;
PL4PTE L4PT
=
MmGetVirtualForPhysical(pa.AsLargeInteger);
PL4PTE pL4PTE
=
&L4PT[pVa
-
>Fileds4KB.VPN4];
if
(pL4PTE
-
>Fields4K.P
=
=
0
)
return
STATUS_UNSUCCESSFUL;
outPa
-
>Fileds4KB.PPN
=
pL4PTE
-
>Fields4K.PPN;
outPa
-
>Fileds4KB.PPO
=
pVa
-
>Fileds4KB.VPO;
}
__except (
1
) {
KdPrint((
"Failed exper Va2Pa"
));
return
STATUS_UNSUCCESSFUL;
}
return
STATUS_SUCCESS;
}
|
main.c
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
|
#include "x64Common.h"
VOID Unload(PDRIVER_OBJECT pDriver) {
KdPrint((
"unload\r\n"
));
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pRegPath) {
NTSTATUS status
=
STATUS_SUCCESS;
pDriver
-
>DriverUnload
=
Unload;
KdPrint((
"start\r\n"
));
CR4 cr4;
cr4.value
=
__readcr4();
cr4.Fields.SMEP
=
0
;
cr4.Fields.SMAP
=
0
;
__writecr4(cr4.value);
PEPROCESS pEprocess;
DWORD64 pid
=
7756
;
status
=
PsLookupProcessByProcessId((HANDLE)pid, &pEprocess);
if
(!NT_SUCCESS(status))
KdPrint((
"获取eprocess失败\r\n"
));
KAPC_STATE apcState;
VA va
=
{ .vaule
=
0x00007FF749C63034
};
PA pa
=
{
0
};
KeStackAttachProcess(pEprocess, &apcState);
CR3 cr3
=
{ .value
=
__readcr3() };
status
=
Va2Pa(&cr3, &va, &pa);
if
(!NT_SUCCESS(status))
KdPrint((
"获取物理地址失败\r\n"
));
KdPrint((
"物理地址为:0x%llx"
, pa.vaule));
KeUnstackDetachProcess(&apcState);
KdPrint((
"end\r\n"
));
return
STATUS_SUCCESS;
}
|
得到打印出的物理地址:
1
2
3
4
|
start
物理地址为:
0x77ee3034
end
unload
|
手动修改地址内容为123
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
0
: kd> !dd
0x77ee3034
#77ee3034 0000006f 00000000 00000000 00000000
#77ee3044 00000000 00000000 00000000 00000000
#77ee3054 00000000 00000000 00000000 00000000
#77ee3064 00000000 00000000 00000000 00000000
#77ee3074 00000000 00000000 00000000 00000000
#77ee3084 00000000 00000000 00000000 00000000
#77ee3094 00000000 00000000 00000000 00000000
#77ee30a4 00000000 00000000 00000000 00000000
0
: kd> !ed
0x77ee3034
7b
0
: kd> !dd
0x77ee3034
#77ee3034 0000007b 00000000 00000000 00000000
#77ee3044 00000000 00000000 00000000 00000000
#77ee3054 00000000 00000000 00000000 00000000
#77ee3064 00000000 00000000 00000000 00000000
#77ee3074 00000000 00000000 00000000 00000000
#77ee3084 00000000 00000000 00000000 00000000
#77ee3094 00000000 00000000 00000000 00000000
#77ee30a4 00000000 00000000 00000000 00000000
|
查看测试程序的输出
更多【软件逆向-x64内核实验3-页机制的研究】相关视频教程:www.yxfzedu.com