我不会教你代码怎么写,但是你将明白这里面的原理:
现在假设一种很客观的情况
对于页表映射,从windbg中 唯一能感知的就是 CR3 寄存器,
在x64的平台下使用4级分页举例:
现在我们只有 CR3寄存器 中的物理地址(也就是PML4所在的那块内存的物理地址).
我(或者说操作系统)如何来操纵这块物理内存呢????(我们不是8086,不能直接操作物理地址).
我需要一个虚拟地址来操作它. 你知道,我们需要经过MMU的4层地址转换,最后才能获得对应的物理地址.
我现在只想要PML4 这张表的起始虚拟地址(base) ,我不想得到其他的什么东西..物理地址就在CR3里面摆着.
现在你知道我们要干什么了.很好!!!!!!!! 我们要构建一个 虚拟地址 找到PML4 这张表的起始虚拟地址(base)
设想一下,假如一个虚拟地址 9 9 9 9 12 分页下.
PML4[9]=CR3的值;
PML4[9]=还等于CR3值;
PML4[9]=还等于CR3的值;
PML4[9]=还等于CR3的值;
物理页内偏移=0;
通过构建符合上面要求的虚拟地址
我们通过CPU的MMU部件的4层转换后,得到的物理地址是不是就是CR3保存的物理地址(也即是PML4的首物理地址)!!!!!!!!
(为什么上面一直用PML4来查,不用PDPT PDT PT这样查下来......扭转一下思路....cpu不管你是什么ppp,还是ggg .你给我cr3里放一个物理地址.,我就就按你要求的分页模式,逐级翻译. 对不对是你操作系统的要保证的事)
下面来演示:
- windbg中查看一下CR3 的值
- 列出所有的PMML4E
为什么要列出所有来,你知道的.我现在在翻译第一级.我需要在PML4表中,找到PML4的物理地址(暂时先忘记cr3吧.cr3保存的不就是PML4表的首物理地址吗?)
找到了,它和cr3的值一样
现在我们来算一下第一个[9]索引 (你知道的)
0x187f68-0x187000=f68 /8= 111101101
现在我们得到了虚拟地址第一个9位,你知道的.前面还有16位 基于扩高原则
再说一遍.4层地址翻译 过程中 我们实际在PML4表里面转圈.所以接下来我会直接给出虚拟地址了
[9]:111101101 [9]:111101101 [9]:111101101 [9]:111101101 [12]:0(我们不许要页内偏移)
基于虚拟地址的扩高原则 高[16]:1111111111111111
合起来:1111111111111111111101101111101101111101101111101101000000000000
得到一个PML4基址:FFFFF6FB7DBED000
赶紧来看一下对不对:
他妈的简直完美
你说这他妈的有什么用啊.我现在需要的是 我有一个 虚拟地址,我需要修改属性,我需要获得PML4E ,PDPTE,PDE,PTE .我要你这玩意有什么用
举例: 我要获得fffff880`04512070 这个虚拟地址的PXE(windbg的命名 等于PML4E)
拆前9位 得到PML4表中的索引
Binary: 11111111 11111111 11111000 10000000 00000100 01010001 00100000 01110000
11111000 1=1F1
FFFFF6FB7DBED000+1F1*8=?
你说哎,他妈的后面不是对不上吗???错了吧哈哈哈哈 你想一个页表只能存512项 就是0x200项
还是从0还是起算.实际索引只能是 0~1FF (0~19 是不是20个?)..你数一下图里面,最后一个应该是物理地址# 187ff8 处.整张PML4表已经结束了
好了,基于上面的我们来讨论一下第二层表 虚拟bae地址怎么来了....
设想一下,假如一个虚拟地址 9 9 9 9 12 分页下.
PML4[9]=CR3的值;
PML4[9]=还等于CR3值;
PML4[9]=还等于CR3的值;
PML4[9]=第一个PDPT表的物理地址(总共0x200个)
页面索引[12]=0;
不知道你理解了吗?可能还是不太清楚.
下面我们试着构建一下虚拟地址
[9]:111101101 [9]:111101101 [9]:111101101 [9]:????????[页内偏移]:0
问号里面到底是什么值呢? 我们暂时先认为是 0 吧 ,总之我们构建了一个这个样的虚拟地址
[9]:111101101 [9]:111101101 [9]:111101101 [9]:000000000[页内偏移]:000000000000
1111111111111111111101101111101101111101101000000000000000000000
得到一个虚拟地址:FFFFF6FB7DA00000
举例: 还是刚才的虚拟地址: fffff880`04512070
拆分这个虚拟地址:
11111111 11111111 11111000 1 0000000 00 000100 010 10001 0010 0000 01110000
得到:
[9]=0X1F1
[9]= 0X0
[9]= 0X22
[9]=0X112
[9]=0X70
需要明确的是,
刚才我们获得的是第一张 PDPT表的虚拟基址,总共有512张(一个pml4e管理)
现在我们有PDPT的基址 :FFFFF6FB7DA00000
当务之急是,我需要确定我到底在哪张PDPT表中.
这很好办, 第一个[9] =1F1 已经给了我们答案 .我们在(从0开始的)第1F1张 PDPT表中
所以我们的虚拟地址 将变成 :
FFFFF6FB7DA00000 +1F1* 0x1000=FFFF F6FB 7DBF 1000
是时候来确定我们是哪个PDPTE 了,利用第二个[9]=0
FFFF F6FB 7DBF 1000+0x8=FFFF F6FB 7DBF 1000
需要说明一下的是: 照目前的情况来看,好像我们是对了,前提是 PDPT表 是连续排列的.
我们还假设 PML4[0]--->PDPT[0] PML4[n]--->PDPT[n]
继续 PDT表
PML4[9]=CR3的值;
PML4[9]=还等于CR3值;
PML4[9]=第一个PDPT表的物理地址(总共0x200个)
PML4[9]=第一个PDT表的物理地址(总共0x200 *0x200个)
页面索引[12]=0;
[9]:111101101 [9]:111101101 [9]:000000000 [9]:000000000[页内偏移]:000000000000
1111111111111111111101101111101101000000000000000000000000000000
得到一个PDT表基址:FFFFF6FB40000000
可以看到 :我们确实找到了PT表的基址.验证一下,
还是这个地址: fffff880`04512070
拆分这个虚拟地址:
11111111 11111111 11111000 1 0000000 00 000100 010 10001 0010 0000 01110000
得到:
[9]=0X1F1
[9]= 0X0
[9]= 0X22
[9]=0X112
[9]=0X70
先确定一下,1个PML4E 管理512个PDPT表,总共管理 512 * 512 个PDT表
怎么确定我们在哪张PT表中?根据上面可以知道 ,我们在第1F1 张PDPTT表中的[0]张PDT表
FFFFF6FB40000000 + (1F1 * 一张PDPTT表的范围(512*8)+ 0 * 512 ))=FFFF F6FB 7E20 0000
现在我们确定在虚拟地址FFFF F6FB 7E20 0000 对应的PDT表中
具体在表的,在第[0x22 *8]个中
FFFF F6FB 7E20 0000+110=FFFF F6FB 7E20 0110
我们看下一得到的基址:
PML4表基址:FFFFF6FB7DBED000
PDPT表基址 :FFFFF6FB7DA00000
PDT表基址:FFFFF6FB40000000
发现了什么吗?确实没发现什么,但是在构造虚拟地址的时候,
我们有了PML4表基址后 ,接下来的求解过程好像就是 从低[9]开始给清0 了,发现了吗???
PDPT表基址 :FFFFF6FB7DBED000>>21 <<21=FFFFF6FB7DA00000
PDT表基址: FFFFF6FB7DA00000 >>30 <<30=FFFFF6FB40000000
最后我们没有求PT表的基址
按照上面的推理
PT表基址: FFFFF6FB40000000 >>39 <<39=FFFF F680 0000 0000
是时候来验证一下了:
实在是算不过来了.好心人 在评论区补充吧