首页
社区
课程
招聘
[原创]四级分页下的页表自映射与基址随机化原理介绍
2022-8-24 19:33 18519

[原创]四级分页下的页表自映射与基址随机化原理介绍

2022-8-24 19:33
18519

四级分页下的页表自映射与基址随机化原理介绍

一、x64分页基础

1.介绍

​ IA-32e模式下,虚拟地址宽度为64位,但只有低48位有效,最多可以寻址256TB,高16位用作符号拓展(全0或全1)。CPU 分页机制变为4级,分别对应 PML4、PDPT、PD、PT,并将48位虚拟地址按 9-9-9-9-12 索引格式划分。

 

 

其中,Cr3 寄存器中的物理地址指向 PML4 表的首地址。上图中表项均占8个字节,物理页面大小仍然为4KB。

2.实例

Windbg 中手动拆分64位虚拟地址,并按照上面的分页规则计算出物理地址。实验选用 idt 表首地址进行拆分。(在计算物理地址时,需要对页表项的属性位清0。)

1
2
3
4
5
6
7
8
9
10
11
kd> r idtr
idtr=fffff8037888e000
kd> dq fffff8037888e000
fffff803`7888e000  761e8e00`00107e00 00000000`fffff803
fffff803`7888e010  761e8e04`00108140 00000000`fffff803
fffff803`7888e020  761e8e03`00108600 00000000`fffff803
fffff803`7888e030  761eee00`00108ac0 00000000`fffff803
fffff803`7888e040  761eee00`00108e00 00000000`fffff803
fffff803`7888e050  761e8e00`00109140 00000000`fffff803
fffff803`7888e060  761e8e00`00109680 00000000`fffff803
fffff803`7888e070  761e8e00`00109b80 00000000`fffff803

将虚拟地址按照 9-9-9-9-12 格式划分(注意低48位有效)

1
2
3
4
5
6
7
fffff803`7888e000 -> f803`7888e000
 
1 1111 0000            0x1f0          PML4I
0 0000 1101            0xd            PDPTI
1 1100 0100            0x1c4          PTI
0 1000 1110            0x8e           PDI
000000000000           0x0            Offset

访问 Cr3 + PML4I * 8 指向的物理地址得到 PDPTE 的物理地址

1
2
3
4
5
6
7
8
9
10
11
kd> r cr3
cr3=0000000052c76000
kd> !dq 52c76000+1f0*8
#52c76f80 00000000`00c08063 00000000`00000000
#52c76f90 00000000`00000000 00000000`00000000
#52c76fa0 00000000`00000000 00000000`00000000
#52c76fb0 0a000000`0bafc863 00000000`00000000
#52c76fc0 00000000`00000000 00000000`00000000
#52c76fd0 00000000`00000000 00000000`00000000
#52c76fe0 00000000`00000000 00000000`00000000
#52c76ff0 00000000`00000000 00000000`00ca8063

访问 PDPTE + PDPTI * 8 指向的物理地址得到 PTE 的物理地址

1
2
3
4
5
6
7
8
9
kd> !dq c08000+d*8
#  c08068 00000000`00c09063 00000000`00000000
#  c08078 00000000`00000000 00000000`00000000
#  c08088 00000000`00000000 00000000`00000000
#  c08098 00000000`00000000 00000000`00000000
#  c080a8 00000000`00000000 00000000`00000000
#  c080b8 00000000`00000000 00000000`00000000
#  c080c8 00000000`00000000 00000000`00000000
#  c080d8 00000000`00000000 00000000`00000000

访问 PTE + PTI * 8 指向的物理地址得到 PDE 的物理地址

1
2
3
4
5
6
7
8
9
kd> !dq c09000+1c4*8
#  c09e20 00000000`00ca7063 0a000000`03996863
#  c09e30 0a000000`0f5bc863 0a000000`0f5bd863
#  c09e40 0a000000`0f5be863 0a000000`0f5bf863
#  c09e50 0a000000`032c0863 0a000000`032c1863
#  c09e60 0a000000`040c3863 0a000000`02bc4863
#  c09e70 0a000000`02bc5863 0a000000`02bc6863
#  c09e80 0a000000`02bc7863 0a000000`02bc8863
#  c09e90 0a000000`02bc9863 0a000000`02bca863

访问 PDE + PDI * 8 指向的物理地址得到物理页面

1
2
3
4
5
6
7
8
9
kd> !dq ca7000+8e*8
#  ca7470 89000000`0588e121 89000000`0588f963
#  ca7480 89000000`05890963 89000000`05891963
#  ca7490 89000000`05892963 89000000`05893963
#  ca74a0 00000000`00000000 89000000`05895963
#  ca74b0 89000000`05896963 89000000`05897963
#  ca74c0 89000000`05898963 89000000`05899963
#  ca74d0 89000000`0589a963 89000000`0589b963
#  ca74e0 00000000`00000000 89000000`0589d963

访问 物理页面 + Offset 指向的物理地址得到内容

1
2
3
4
5
6
7
8
9
kd> !dq 0588e000
# 588e000 761e8e00`00107e00 00000000`fffff803
# 588e010 761e8e04`00108140 00000000`fffff803
# 588e020 761e8e03`00108600 00000000`fffff803
# 588e030 761eee00`00108ac0 00000000`fffff803
# 588e040 761eee00`00108e00 00000000`fffff803
# 588e050 761e8e00`00109140 00000000`fffff803
# 588e060 761e8e00`00109680 00000000`fffff803
# 588e070 761e8e00`00109b80 00000000`fffff803

与访问虚拟内存得到的结果一致。

二、页表自映射

1.介绍

​ 在64位模式下,高等级页表项都指向低等级页表项的物理地址,依次类推,直到最低级别页表项,即可获取物理页面进而读取内容。在此过程中 Cr3 寄存器中存储了最高级页表(PML4)的表基物理地址。为了更好的管理这些页表,微软采取了最高级页表基址自映射的方式实现仅仅利用8字节物理内存,就可以在每次访问分页管理相关的内存时,少做一次页表查询操作来优化速度。

2.原理

​ 在四级页表的最高级 PML4 页表中存在一项,里面保存了 PML4 页表的表基物理地址,即 Cr3 。假设这一项在 PML4 表中的索引为 0x100,如下图所示:

 

 

此时满足:( ![物理地址] 表示读取物理地址的内容)

1
![Cr3 + 0x100 * 8=  Cr3

用于分页管理的物理页面大小总计 512 512 512 * 4KB = 512GB,而一个 PML4 表项恰好可以管理512GB内存。

 

PML4 表中索引位置0x100的元素用于内存管理且满足上述关系,那么此时用于内存管理的虚拟地址空间为:

1
0xFFFF8000`00000000 ~ 0xFFFF807F`FFFFF000

按照 9-9-9-9-12 分页方式去拆分上述边界物理地址:(只使用低48位)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 起始地址
0x8000`00000000       
1 0000 0000                0x100
0 0000 0000                0x0
0 0000 0000                0x0
0 0000 0000                0x0
0000 0000 0000             0x0
 
// 结束地址
0x807F`FFFFF000
1 0000 0000                0x100
1 1111 1111                0x1FF
1 1111 1111                0x1FF
1 1111 1111                0x1FF
0000 0000 0000             0x0

常规查询流程:

1
2
3
4
5
6
7
8
9
10
11
// 起始地址
![Cr3 + 0x100 * 8] = PDPTE
![PDPTE + 0x0 * 8] = PDE
![PDE + 0x0 * 8] = PTE
![PTE + 0x0 * 8] = 物理页面
 
// 结束地址
![Cr3 + 0x100 * 8] = PDPTE
![PDPTE + 0x1FF * 8] = PDE
![PDE + 0x1FF * 8] = PTE
![PTE + 0x0 * 8] = 物理页面

根据上述等式,![Cr3 + 0x100 * 8] = Cr3,所以查询流程变为:

1
2
3
4
5
6
7
8
9
// 起始地址
![Cr3 + 0x0 * 8] = PDE
![PDE + 0x0 * 8] = PTE
![PTE + 0x0 * 8] = 物理页面
 
// 结束地址
![Cr3 + 0x1FF * 8] = PDE
![PDE + 0x1FF * 8] = PTE
![PTE + 0x0 * 8] = 物理页面

很神奇,查询页表操作由四次变成了三次,效率大大提升。而且只是使用了8字节的物理地址空间来保存 Cr3 。下图展示了优化后的查询过程:

 

3.规律

为了写代码方便读写页表属性,四级页表都应该有自己的表基虚拟地址,以便访问其中的元素。

3.1 推导最高级页表 PML4 的基址

PML4 页表基址有两个特点:

  • 属于虚拟地址
  • 虚拟地址的内容是Cr3

假设该虚拟地址按照 9-9-9-9-12 分页规则拆分得到的索引依次为 x、y、z、r,根据页表解析规则:

1
2
3
4
![Cr3 + x * 8] = PDPTE
![PDPTE + y * 8] = PDE
![PDE + z * 8] = PTE
![PTE + r * 8] = 物理页面 = Cr3

还需要满足 ![Cr3 + x * 8] = Cr3,所以当 x = y = z = r 的时候上述条件均满足。

3.2 推导 PDPT 表的基址

PDPT 页表基址有两个特点:

  • 属于虚拟地址
  • 虚拟地址的内容不再是Cr3,而是 ![Cr3 + 0 * 8] 指向的物理地址。

假设该虚拟地址按照 9-9-9-9-12 分页规则拆分得到的索引依次为 x、y、z、r,根据页表解析规则:

1
2
3
4
![Cr3 + x * 8] = PDPTE
![PDPTE + y * 8] = PDE
![PDE + z * 8] = PTE
![PTE + r * 8] = ![Cr3]

还需要满足 ![Cr3 + x * 8] = Cr3,所以当 x = y = z 且 r = 0 的时候上述条件均满足。

3.3 推导 PD、PT 表的基址

​ 方法同理。

3.4 结论

页内偏移均为0

  • PML4:PML4i == PDTi == PDi == PTi == Index
  • PDPT:PML4i == PDTi == PDi == Index && PTi == 0
  • PD:PML4i == PDTi == Index && PDi == 0 && PTi == 0
  • PT:PML4i == Index && PDTi == 0 && PDi == 0 && PTi == 0

三、基址随机化

1.原理

​ 上面得到结论中的 Index 就是自映射表项在 PML4 表中的索引,这个值的变化就是造成各级页表基址变化的原因。

 

系统重启前的 PML4 基址:

1
2
3
4
5
6
7
8
0xFB7DBEDF6000
1 1111 0110            0x1F6        PML4
1 1111 0110            0x1F6        PDPT
1 1111 0110            0x1F6        PD
1 1111 0110            0x1F6        PT
000000000000           0x0
 
Index为:0x1F6

系统重启后的PML4基址:

1
2
3
4
5
6
7
8
0x8D46A351A000
1 0001 1010            0x11A
1 0001 1010            0x11A
1 0001 1010            0x11A
1 0001 1010            0x11A
000000000000           0
 
Index为:0x11A

2.定位

​ 页表基址随机化导致写代码读写页表属性变得不方便,但可以利用页表自映射的一些结论来获取 PML4 表基址。PML4 表基址的内容为Cr3的值,并且位于 PML4 表所在的页面内。因为Cr3里保存了 PML4 的表基物理地址,所以可以通过映射Cr3物理地址的虚拟地址,遍历这个虚拟地址页面的512个地址,哪个地址符合上述条件,哪个地址就是 PML4 表基址。下面给出驱动代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ULONG64 GetPml4Base()
{
    PHYSICAL_ADDRESS pCr3 = { 0 };
    pCr3.QuadPart = __readcr3();
    PULONG64 pCmpArr = MmGetVirtualForPhysical(pCr3);
 
    int count = 0;
    while ((*pCmpArr & 0xFFFFFFFFF000) != pCr3.QuadPart)
    {
        if (++count >= 512)
        {
            return -1;
        }
        pCmpArr++;
    }
    return (ULONG64)pCmpArr & 0xFFFFFFFFFFFFF000;
}

得到了 PML4 表基址,就可以得到 Index 索引值,其他各级页表基址也就都可以得到了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ULONG64 GetPdptBase(ULONG64 ulPml4Base)
{
    return (ulPml4Base >> 21) << 21;
}
 
ULONG64 GetPdBase(ULONG64 ulPml4Base)
{
    return (ulPml4Base >> 30) << 30;
}
 
ULONG64 GetPtBase(ULONG64 ulPml4Base)
{
    return (ulPml4Base >> 39) << 39;
}

得到了 PML4 表基址,就可以得到 Index 索引值,其他各级页表基址也就都可以得到了。

四、参考文档

Getting Physical: Extreme abuse of Intel based Paging Systems - Part 2 - Windows (coresecurity.com)

 

关于WIndows内核自映射方案的通俗解释 - SivilTaram - 博客园 (cnblogs.com)

 

[原创]逆向TesSafe.sys有感:鹅厂是如何定位随机化的PTE_BASE-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com

 

x64内核研究04分页哔哩哔哩_bilibili

五、结语

有错误欢迎指出,一起交流进步。


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2022-8-24 20:31 被REPE编辑 ,原因:
收藏
点赞15
打赏
分享
打赏 + 101.00雪花
打赏次数 2 雪花 + 101.00
 
赞赏  Editor   +100.00 2022/09/05 恭喜您获得“雪花”奖励,安全圈有你而精彩!
赞赏  Jhno Snow   +1.00 2022/09/01 中国未来内核第一人,还得看我飞哥
最新回复 (10)
雪    币: 3350
活跃值: (3372)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 1 2022-8-24 20:00
2
0
感谢分享!
雪    币: 2580
活跃值: (3662)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
syser 2022-8-24 23:57
3
0
这篇详细
雪    币: 4427
活跃值: (3449)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木志本柯 2022-8-25 09:55
4
0
这么说想要访问内存 必须要有cr3咯
雪    币: 234
活跃值: (1763)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
IKAIL 2022-8-25 16:49
5
0
牛逼
雪    币: 613
活跃值: (331)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
无视无形幽冥 2022-8-25 23:19
6
0
大佬牛逼
雪    币: 1115
活跃值: (1921)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
Oday小斯 2022-8-26 13:21
7
0
感谢分享
雪    币: 149
活跃值: (1998)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
saloyun 2022-8-31 13:45
8
0
j讲得很详细,非常好。
雪    币: 1203
活跃值: (127)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Re_Dog 2022-9-1 10:59
9
0
学到了,学到了
雪    币: 5820
活跃值: (4387)
能力值: ( LV10,RANK:160 )
在线值:
发帖
回帖
粉丝
淡然他徒弟 1 2022-9-6 03:41
10
0
mark
雪    币: 1475
活跃值: (3210)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小希希 2023-1-29 13:41
11
0
mark
游客
登录 | 注册 方可回帖
返回