保护模式(五)| 10-10-12 分页与 PDE/PTE
# 前言
上一篇我们学习了中断门、陷阱门和任务段。本篇将深入介绍 10-10-12 分页机制,以及 PDE 和 PTE 的结构与实验。
# 10-10-12 分页
10-10-12 分页是一种内存分页机制,将 32 位虚拟地址分为三个部分:10 位页目录索引(PDI)、10 位页表索引(PTI)、12 位页内偏移,通过两级页表(页目录表 + 页表)实现地址映射。
# 4GB 内存空间
每个程序运行时,操作系统都会为其分配一段 4GB 的内存空间。
但电脑的物理内存可能不够为每个进程都分配 4GB。实际上,进程被分配到的 "4GB 内存空间" 只是虚拟的内存空间,并不是真正的物理内存,虚拟内存与物理内存之间有一层转换关系。
# 有效地址 / 线性地址 / 物理地址
# 有效地址与线性地址
例如: mov eax, dword ptr ds:[0x12345678]
其中, 0x12345678 是有效地址, ds.Base + 0x12345678 是线性地址。
注意:当段寄存器的 Base 为 0 时,有效地址 = 线性地址(大多数时候都是如此)。但也有特殊情况,比如 FS 段寄存器的 Base 不为 0。
# 物理地址
系统 DLL(动态链接库)存在于物理地址中。当程序想要调用某个 DLL 时,DLL 便会映射一份线性地址给程序,程序通过线性地址找到 DLL 的物理地址。
# Cr3(控制寄存器)
每个进程都有一个 Cr3 的值(Cr3 本身是一个寄存器,一个核只有一套寄存器)。
# 实验:通过线性地址找到物理地址
一、将 XP 虚拟机设置为 10-10-12 分页模式。
右键 → 我的电脑 → 属性 → 高级。
将 /noexecute 改为 /execute ,保存,重启。
二、新建一个记事本,写入 “Hello World”。
三、使用 Cheat Engine 附加进程。
四、找到 “Hello World” 的线性地址。
修改记事本中的字符来确认真正的线性地址:
线性地址最终确定为: 000AB2C0 。
五、将线性地址拆分为 10-10-12 三组。
1 | 0x000AB2C0 |
六、在 WinDbg 获取进程的 Cr3。
命令: !process 0 0
DirBase 就是 Cr3。
七、通过 Cr3 找到字符串的物理地址。
注意:
- 找第一层和第二层时要将索引乘以 4(每个地址占 4 个字节)
- 每找到一层都要将地址的低 12 位(属性位) 清零再继续找下一层
第一层(PDE):
第二层(PTE):
第三层(物理页):
以字节形式查看:
# PDE 与 PTE
# Cr3
- 在所有寄存器中,只有 Cr3 存储的是物理地址,其他寄存器存的都是线性地址。
- Cr3 所存储的物理地址指向了一个 PDT(Page Directory Table,页目录表)。
- 在 10-10-12 分页模式中,一个页的大小为 4KB,即一个页可以存储 1024 个页目录表项(PDE)。
物理页结构图:
# PDE(Page Directory Entry,页目录表项)
- 页目录表(PDT) 的每一项元素称为页目录表项(PDE)。
- 每个 PDE 指向一个页表(PT,Page Table)。
- 每个页表的大小为 4KB,即一个页表可以存储 1024 个页表项(PTE)。
# PTE(Page Table Entry,页表项)
- 页表的每一个元素称为页表项(PTE)。
- PTE 指向的内存才是真正的物理页。
特征:
- PTE 可以指向一个物理页,也可以不指向物理页。
- 多个 PTE 可以指向同一个物理页。
# 物理页的属性
物理页的属性 = PDE 属性 & PTE 属性
| 属性位 | 含义 |
|---|---|
| P(第 0 位) | 有效位。P=1:有效;P=0:无效 |
| R/W | 读写位。R/W=0:只读;R/W=1:可读可写 |
| U/S | 权限位。U/S=0:特权用户;U/S=1:普通用户 |
| PS | 页大小。PDE 特有,位于第 7 位。 PS=0:PDE 指向页表 PS=1:PDE 直接指向物理页(低 22 位 = 页内偏移,最大 4MB,俗称 "大页") |
| A | 访问位。A=1:已被访问过;A=0:未被访问过 |
| D | 脏位。D=1:已被写过;D=0:未被写过 |
其他属性位需等学完控制寄存器与 TLB 才能讲解。
# 补充:为什么按 10-10-12 分页
- 一个物理页的大小为 4096 字节(2^12),遍历整个物理页需要 12 个比特位。
- 一个页表有 1024 个页表项(2^10),需要 10 个比特位。
- 页目录表项同理,也需要 10 个比特位。
# 实验 1:证明 PTE 可以不指向物理页
一、选择任意进程的 Cr3 作为目标,例如 notepad.exe 。
二、查看部分页表项,可以发现有许多页表项都为 0,没有指向任何物理页。
# 实验 2:通过修改页表使 C 语言能在 0 地址处读写
一、编译并运行以下代码。
1 |
|
运行后,首先输出 x 的地址:
二、使用 WinDbg 将虚拟机中断,将变量 x 的地址挂载到 PTE 为 0 的物理页上。
1 | 0x12ff7c |
三、继续运行程序,可以看到成功对 0 地址进行了读写。
# 实验 3:通过修改物理页属性使字符串常量可修改
一、编译并运行以下代码。
1 |
|
运行后得到 str 的地址:
二、修改 str 所在 PTE 的属性,将 R/W 位设置为 1。
1 | 0x40e11c |
三、继续运行程序,成功修改了字符串常量。
# 实验 4:通过修改物理页属性使普通用户能够读取高 2G 内存
一、编译并运行以下代码。
1 |
|
二、修改指针 p 保存的线性地址所指向的 PDE 和 PTE 的 U/S 位为 1。
1 | 0x8003f00c |
修改 PDE 的 U/S 位:
修改 PTE 的 U/S 位:
三、继续运行程序,成功读取了高 2G 内存。