[windows内核]PDE&PTE

Cr3

描述:

在所有的寄存器中,只有Cr3存储的是物理地址,其它寄存器存的都是线性地址
Cr3所存储的物理地址指向了一个页目录表(PDT)
在Windows中,一个页的大小通常为4KB,即一个页可以存储1024个页目录表项(PDE)

下面是手册中的描述在第3卷2.5 CONTROL REGISTERS
在这里插入图片描述
物理页结构图:

在这里插入图片描述
注意:但其实上面这种结构方式是错误的,这个以后再解释,先这样理解

PDE(页目录表项)

描述:

页目录表(PDT)的每一项元素称为页目录表项(PDE)
每个页目录表项指向一个页表(PTT)
每个页表的大小为4KB,即一个页表可以存储1024个页表项(PTE)

PTE(页表项)

描述:

页表(PTT)的每一个元素称为页表项(PTE)
页表项(PTE)所指向的才是真正的物理页

特点:

  1. PTE可以指向一个物理页,也可以不指向物理页
  2. 多个PTE可以指向同一个物理页
  3. 一个PTE只能指向一个物理页

物理页的属性

物理页的属性=PDE属性& PTE属性
在这里插入图片描述
下面两张图看得更直观一些

PDE(页目录表项)
在这里插入图片描述
PTE(页表项)
在这里插入图片描述
P 位 – 存在位,表示当前条目是否在物理内存中

R/W 位 – 读写权限位,为 0 表示只读,为 1 表示可读写

U/S 位 – 也称为权限位,页或一组页的特权级,为 0 表示系统级,对应 CPL 0、1、2,为 1 表示用户级,对应 CPL 3。

PWT – 页表缓冲写入机制,为 0 表示 write-back 模式,更新页表缓冲区时,只标记为已更新,不同步写内存,只有被新进入的数据取代时才更新到内存,为 1 表示 write-through 模式,更新页表缓冲区时,同步写内存,保证缓冲区与内存一致

PCD – 是否拒绝被缓冲,为 0 表示可以被缓冲,为 1 表示不可以被缓冲

A 位 – 是否被访问,CPU 会在访问到页面时将该位置 1,但不会清除,只有软件可以将 A 位复位

D 位 – 是否被写入,CPU 会在写入页面时将该位置 1,但不会清除,只有软件可以将 D 位复位

PS – PDE特有,页大小位,为 0 表示页大小为 4KB,且 PDE 指向页表,为 1 表示页大小为 4MB,且 PDE 指向 4MB 的整块内存

PAT – 奔腾3以后的 CPU 引入的页属性表标识位,为 1 开启页属性表后,通过一系列专用寄存器(MBR)为每个页提供了详细的属性设置

G 位 – 全局位,也称为脏位,如果该位与 CR4 寄存器的 PGE 位同时被置为 1,则该页或页目录项将不会在 TLB 中被逐出

20bits 基地址 – PDE 与 PTE 的高 20bits 都是下级页基址,无论是页目录表还是页表还是在内存中的页,他们都是 4KB 对齐的,也就是说他们的首地址低12位均为0,这样,只需要通过 20bits 的基地址 * 12 就可以得到计算后的 32 位物理地址了

10-10-12分页的补充

为什么要按10-10-12分页:

  1. 一个物理页的大小为4096字节,即2的12次方,若要遍历整个物理页,则需要12个比特位
  2. 一个页表有1024个页表项,1024等于2的十次方,即需要10个比特位
  3. 页目录表项同理,也需要10个比特位

实验

证明PTE特征
PTE可以指向一个物理页,也可以不指向物理页
在这里插入图片描述
在这里插入图片描述

可以发现有许多页表项都为0,没有指向任何物理页

实验2
通过修改页表使C语言能在0地址处读写
编译如下代码

#include <iostream>
#include <windows.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);

	getchar();
	return 0;
}

运行
在这里插入图片描述
得到地址后中断进入windbg
使用WinDbg将虚拟机中断,将变量x所在的物理页挂载到线性地址0的PTE
拆分

0012ff3c

0000 0000 0001 0010 1111 1111 0011 1100

0000 0000 00   //0
01 0010 1111   //0x12F
1111 0011 1100 //0xF3C

在这里插入图片描述
修改
在这里插入图片描述
返回继续运行
在这里插入图片描述
在这里插入图片描述

成功对0地址进行了读写
由此可见,0地址是否能读写和其他无关只与PDE 和 PTE有关

实验3
通过修改物理页属性使字符串常量可修改

编译如下代码

#include <iostream>
#include <windows.h>



int main(int argc, char* argv[])
{
	char *str = (char*)"Hello World";

	printf("线性地址:%x\n", str);

	getchar();						// 让程序执行到这里

	//修改只读变量
	str[0] = 'M';

	printf("修改后的值:%s\n", str);

	getchar();
}

打开x32dbg,我们可以看到

在这里插入图片描述
去内存布局里寻找字符串位置,发现字符串再.rdata段,而rdata段只有可读属性,并没有可写属性

在这里插入图片描述
如果继续执行到修改的时候,就会产生一个0xc0000005异常

在这里插入图片描述
我们再次运行程序

在这里插入图片描述
得到str地址,进入windbg中断,得到Cr3

在这里插入图片描述
拆分地址

00413160

0000 0000 0100 0001 0011 0001 0110 0000

0000 0000 01   //1
00 0001 0011   //0x13
0001 0110 0000 //0x160

在这里插入图片描述
拆分

0x2277E005
0010 0010 0111 0111 1110 0000 0000 0101

从上面的图片可知道PTE的第1位是R/W 位,所以修改后为

0x2277E007
0010 0010 0111 0111 1110 0000 0000 0111

在这里插入图片描述
然后继续运行程序

在这里插入图片描述
成功修改str,所以可以看出内存的读写只与PDE 和 PTE有关于其他无关,这里可以看出一个有意思的东西,就是PTE虽然有R/W位但是没有标记内存是否可以执行,其实对于CPU来说任何它能识别的code都是可以执行的,但是为什么再程序里却有些内存有执行属性有些内存却没有,这里有兴趣的可以自行拓展一下

实验4
通过修改物理页属性使普通用户读取高2G内存

编译如下代码

#include <iostream>
#include <windows.h>



int main(int argc, char* argv[])
{
	PDWORD p = (PDWORD)0x84279a7a;

	getchar();			// 让程序运行到这里

	printf("读取高2G内存:%x \n", *p);
	
	getchar();
	return 0;
}

在这里插入图片描述
再次启动程序,运行,中断进入windbg,查看cr3

在这里插入图片描述

拆分

0x84279a7a

1000 0100 0010 0111 1001 1010 0111 1010

1000 0100 00   //0x210
10 0111 1001   //0x279
1010 0111 1010 //0xA7A

然后修改PDE与PTE的U/S位
在这里插入图片描述

通过上图我们可以知道U/S位在第二位
拆分

0x001C3063
000111000011000001100011

0x4279125
0100001001111001000100100101

修改后

0x001C3067
000111000011000001100111

0x4279125
0100001001111001000100100101

在这里插入图片描述
返回继续执行程序

在这里插入图片描述
在这里插入图片描述

成功读取高2G内存,由此可见读取的权限只与PDE 和 PTE有关,与其他无关

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值