Windows 分页

线性地址通过4层页表变成物理地址,让CPU实际可以访问到物理地址。
也就是说,线性地址其实是由四层页表+偏移组合而成的

线性地址组成
- 在PML4 四级页表中的表项索引 索引内内容是 PDPT 的基地址
- 拿着得到的PDPT的基地址 与在PDPT 页目录指针表的表项索引 得到PD的基地址
- 拿着得到的PD的基地址 与在PD页目录表的表项索引 得到PT的基地址
- 拿着得到的PT的基地址 与在PT页表的表项索引 得到Page 页的基地址
- 然后拿着页的基地址+ Offset 得到真实物理地址 12位最大就是0xFFF 所以一个页通常是0x1000
- 也就是0x0-0xFFF 4KB页大小
在32位系统下,windows有两种分页机制,2-9-9-12分页以及10-10-12分页,而在64位下只有一种分页机制,那就是9-9-9-9-12分页
在x64体系中,只有48位是用来确定虚拟地址的,前面的16位成为符号拓展位,这16位只有为全1或者为全0两种情况。除了这两种情况之外的地址都是不合法的。
这16位为全1时表示这个地址是一个内核空间的地址,而为全0时表示这个地址是一个用户空间的地址。而在windows操作系统中,由于种种原因,又只用了这48位线性地址中的 44位 。也就是说windows分配的线性地址前20位都是要么为全0,要么为全1
PS位

除了四级页表后的PDPTE PDE 这两个页表都可能拥有PS位
PS为一般在第7位
在PDPTE如果PS位为1 就认为是映射1GB 大页
- PD PT Offset就变成一个大的Offset 然后PDPTE是这个1GB物理大页的基地址
- Offset大小就是 9+9+12 = 30位大小(PD的页表的表项索引 + PT页表的表项索引 + Offset)
如果在PDE PS位为1 那么就认为是映射2MB 大页
- PT Offset 就变成一个大的Offset 然后PDE是这个2MB大页的基地址
- Offset 大小就是9 +12 = 21 位 (PT页表的表项索引 + Offset) 0x1FFFFF字节
CR3寄存器
- x64 PML4基地址存储在CR3寄存器内。
例子
00007ff8`c5810000
011111111
111100011
000101100
000010000
000000000000

CR3是1ad000
第一层应该就是1ad000 + 255(8个1) * 8(每一个PML4E都是寄存器大小 8 字节) = 0x1ad7f8

得到PML4E8A0000000F27C867
在 x64 页表中,PML4E 的高 40 位存储下一级页表(PDPT)的物理基地址,低 12 位为属性标志(如存在位、读写权限等)
| 名称 | 值 | 类型 | |
|---|---|---|---|
| pdpt | 0x8a0000000f27c000 | unsigned __int64 |
得到PDPT基地址 f27c000
然后就是 f27c000+ 1e3 * 8 = f27cf18

0a000000`0f27d867
相同手法获得PDT 基地址 f27d000
000101100 = 0x2c 然后0x2c * 8 = 0x160
PT在f27d160

pt基地址为f27e000 + 0x80 = f27e080

最终结果0xf185000

Amd64VtoP: Virt 00007ff8c5810000, pagedir 00000000001ad000Amd64VtoP: PML4E 00000000001ad7f8Amd64VtoP: PDPE 000000000f27cf18Amd64VtoP: PDE 000000000f27d160Amd64VtoP: PTE 000000000f27e080Amd64VtoP: Mapped phys 000000000f185000Virtual address 7ff8c5810000 translates to physical address f185000.
成功手算出了物理地址
PTE -> PDE
那么我们其实也可以反推
因为PTE =
Windows 内存管理
VAD
微软和Intel手册叫的不同
- PXE - PM4LE
- PPE - PDPTE
- PDE - PDE
- PTE - PTE