要点回顾
前文简单了解了80x86的 10-10-12 分页机制,本篇文章继续学习。
PDE与PTE
CR3:唯一一个存储物理地址的寄存器。
在Windows中,页大小是4KB。在后期会接触到另一种页,有4MB大小,称为大页。
CR3里面存储的地址,指向的是页目录表(PDT),表中每个成员称为PDE(页目录表项/页目录项)。
页目录表(PDT)中每个成员(PDE)又指向另一张表,该表称为页表(PTT)。
页表(PTT)的大小与页目录表(PDT)一样是4KB,其中存储的成员有1024个,每个成员占4字节。
每个页表中的成员称为PTE(页表项),PTE指向的才是真正的物理页。
无论是PDE或者PTE,都是4字节。它的十六进制形式的后三位存储的是属性。
前文学习过了10-10-12分页形式,那么为什么要这么分呢?
一个物理页是4096字节,刚好是2的12次方。所以,需要有12个二进制位,才能找遍所有的物理页。
指向相同物理页
- PTE可以指向物理页,也可以没有指向物理页。
- 多个PTE可以指向一同一个物理页。
- 一个PTE只能指向一个物理页。
实验
拆分0地址,观察PTE是否有物理页,并向0地址读写数据。
1.寻找CR3
2.找到PDE
!dd 3ac17000 + 0,这个0也可以不用加直接写了。
3.找到PTE
不要忘了,把十六进制后3位修改为0。
!dd 0bb39000 + 0
PTE里存储的值是空的,说明当前的PTE没有指向物理页。
有编程经验的读者可能会知道,0地址是没办法往里面写入数据,也没有办法读取数据的,会报错。
那么现在就要往0地址中写入数据并且把数据读取出来,可不可以呢?
可以。
如果想向0地址写入数据或读取数据,只需要给它挂上一个物理页,即可。
代码
#include "stdafx.h"
int main(int argc, char* argv[])
{
int x = 1;
printf("x的地址:%x\n", &x);
getchar();
//向0地址写入数据
*(int*)0 = 123;
//读取0地址上的数据
printf("0地址数据:%x\n", *(int*)0);
return 0;
}
定义了一个变量(x),赋值为1。这个1里面肯定是有物理页的,当执行的时候,系统会分配内存给它。
*(int*)0 = 123;这行代码,如果直接运行肯定是会报错的,因为0的线性地址没有物理页,是写不进去的。
后面的读取也是一样,会报错。
当程序运行的时候,将 int x = 1; 的PDE、PTE里面的值挂到0上,即可。
7b转换为10进制,就是123。