概述
CPU是如何利用页目录和页表 等数据结构将一个32位的虚拟地址翻译为32位的物理地址的。其过程可以概括为如下步骤。
① 通过CR3寄存器定位到页目录的起始地址,正因如此,CR3寄存器又称为页目录基地址寄存器(PDBR)。取线性地址的高10位作为索引选取页目录的一个表项,也就是PDE。 ② 判断PDE的PS位,如果为1,代表这个PDE指向的是一个4MB的大内存页,PDE的高10位便是4MB内存页的基地址的高10位,线性地址的低22位是页内偏移。将二者合并到一起便得到了物理地址。如果PS位为0,那么根据PDE中的页表基地址(取PDE的高20位,低12位设为0)定位到页表。
③ 取线性地址的12位到21位(共10位)作为索引选取页表的一个表项,也就是PTE。
④ 取出PTE中的内存页基地址(取PTE的高20位,低12位设为 0)。
⑤ 取线性地址的低12位作为页中偏移与上一步的内存页基地址相加便得到物理地址。
摘自《软件调试2》
32位经典分页模式(10-10-12)
使用这种分页模式 时,线性地址和物理地址的空间都是4GB大小。
如下图所示,使用r cr4查看cr4寄存器值。
CR4中的位 | 代表值 |
---|---|
PSE(Page SizeExtensions) | CR4[4] 为1时启用4MB内存页,为0时限制内存页为4KB。 |
PAE(Physical AddressExtension) | CR4[5] 为1时支持36位或36位以上的物理内存地址,为0时限定物理地址为32位。 |
直接使用r cr3命令打开的是系统[system]进程的页目录基地址DirBase项。这里打开的是一个DUMP文件,可以直接使用r cr3定位崩溃程序的cr3地址。
以realbug模块的起始地址和结束地址为界,搜索其中的所有ASCII字符串。执行s -sa f8c2e000 f8c35000命令。我们的目的就是要使用虚拟地址找到物理地址,通过内存中存储的字符串来验证,这里以f8c2e04d为例。
下面我们把虚拟地址f8c2e04d转换为物理地址。使用命令.Formats f8c2e04d展开为二进制。
11111000 11 000010 1110 0000 01001101
10-10-12模式,其中高10位是页目录(PDE)索引,中间10位是页表(PTE)索引,低12位是页内偏移。使用? 0y1111100011命令转换为其他格式。页目录(PDE)索引是0x3e3,页表索引是0x2e,页内偏移是0x4D。
CR3的高20位就是页目录基地址的高20位,低12位为0,因此,ca83000就是ImBuggy进程的页目录基地址。
!dd 0ca83000+3e34 L1,命令中的L1用来限制要观察的内存长度,表示只显示一个32位数据。低12位(163)代表的是内存页的属性。
!dd 0x0101a000+2e4 L1,低12位(163)和上面一样代表的是内存页的属性。
!db 0x0d56604d 可以看到刚才我们搜索到的ASCII字符串。由此可见物理地址是对的。
其实,只要执行WinDBG的!pte命令便可以自动帮我们执行以上步骤:
!pte f8c2e04d
最后一行中的pfn是Page Frame Number的缩写,即页帧号。这是内存管理中的一个常用术语,代表以页为单位的物理内存编号。以上面结果为例,pfn是d566,加上低12位04d便得到f8c2e04d对应的物理地址d56604d。
PAE分页(2-9-9-12)
和上面一样,先检查CR4值中的配置。PAE开启,就是2-9-9-12分页机制。
查看CR3值
!dq 072c0260 L4
将线性地址f8bdd04d转化为二进制。
11 111000 101 11101 1101 0000 01001101
页目录(PDE)索引是0x1c5,页表索引是0x1dd,页内偏移是0x4D。
线性地址对应的页目录表基地址为0x1028d000。
!dq 1028d000+1c58 L1
!dq 1033000+1dd8 L1
将低12位的标志位替换为0, 便得到内存页的基地址10561000,加上最低12位代表的页内偏移,便得到了线性地址f8bdd04d对应的物理地址0x1056104d。
差别
与32位分页相比,32位线性地址中的页目录索引位和页 表索引位都从10位减少到9位,所以每张页目录和页表的总表项数也 由1024项减少为512,同时每个表项的大小由4个字节增加到8个字 节,所以每张页目录或者页表的总大小仍然是4KB。总体来看,虽然 每张页目录和页表的表项数少了一半,但因为增加了一级映射,页目 录的数量由原来的1张变为最多4张,所以支持的最大页面数仍然是4×512×512 = 220,即2M个。
参考文献
《软件调试2》