前言:PDE_PTE的学习笔记
这篇学完后,就可以知道物理地址中的任何的一个内存地址,没有地方是访问不了的,最重要的就是保护模式,其实也就是绕过保护模式来实现任意物理地址的读写,更准确的就是学习PDE和PTE来实现对物理页的修改操作
上篇学习分页10-10-12的笔记中,潦草的概述的相关的一级,二级物理页,这篇笔记来学习记录相关的物理页的知识点
PDE与PTE
从这张图中可以看出来,其实一级物理页对应的就是页目录表,页目录表中有1024个页目录项(也就是PDE结构,每个占四个字节),所以页目录项的大小为4096个字节
接着就是二级物理页对应的就是页表,页表中有1024个页目录项(也就是PTE结构,每个占四个字节),所以页表的大小为4096个字节
其中PTE指向的物理页,其实就是代表着一个线性地址在物理内存中的真正存储的地址
知识点:
1、PTE可以没有物理页,且只能对应一个物理页.
2、多个PTE也可以指向同一个物理页.
物理页的属性
PTE所指向的物理页的属性是由PDE中的属性和PTE的属性共同决定的 => 物理页的属性 = PDE属性 & PTE属性
PDE的结构
PTE的结构
P位
通过观察下面的PDE和PTE的结构,这里一些属性我们需要了解,这里可以先看P位的属性,想要看物理页是否有效,就需要看PDE与PTE的P位是否都为1,只有都为1的才是有效的物理页
R/W位
当PDE和PTE的R/W = 0 只读属性
当PDE和PTE的R/W = 1 可读可写
练习,查分0地址,观察PTE是否有物理页
测试代码如下,经过windbg调试,可以发现一个进程中0地址的PTE中是没有物理页的
#include<windows.h>
#include<stdio.h>
int main()
{
int* p = 0;
printf("%x\n", p);
getchar();
return 0;
}
练习,线性地址0为什么不能读写?实现将0地址设置可读可写,尝试读写数据
默认线性地址0不能读写的原因就是默认一个进程中0地址的PTE中是没有物理页的
所以如果想要实现0地址设置可读可写,那么我们就需要修改其线性0地址所对应的PTE和PDE的属性,也就是十六进制的后三位
测试代码如下图所示,然后来构造定义的x变量的地址为0x12ff7c,拆分如下
0012ff7C
->
0000 0000 00
01 0010 1111 -> 4BC
f7C
然后接着就是该线性地址0x12ff7c的物理地址,如下图所示,这个地址0x3a14f867,就是PTE中存储的地址的值,那么这个值就是指向一个物理页的地址
这里将0地址中默认存储的PTE为00000000
的值改为3a14f867
(这里的867后12位也需要一样的,因为都是需要可读可写),也就是上面变量x的PTE中存储的值,那么此时0线性地址中指向的物理页和变量x所指向的物理页是同一个物理页
#include<windows.h>
#include<stdio.h>
int main()
{
int x=1;
printf("&x = %x\n", &x);
getchar();
printf("NULL地址数据:%x\n",*(int*)NULL);
*(int*)NULL = 0x12345678;
printf("NULL地址数据:%x\n",*(int*)NULL); // 0x12345678
return 0;
}
练习,为变量x再映射一个地址,并通过新的地址来读取变量x的值
自己的想法是这样的,通过新的地址来读取变量x的值,然后这个新的地址又不能跟变量x的线性地址一样,所以想要让一样就需要让PTE都是指向同一个物理页即可,那么就需要让偏移量一样,所以这里自己主动申请一个地址,然后在相同10-10-12,其中12后三位的偏移地址计算,计算公式为 ((int)pAddr & 0xFFFFF000) |((int)&x & 0x00000FFF)
测试代码如下:
#include<windows.h>
#include<stdio.h>
int main()
{
int x=0x1;
printf("&x = %x\n", &x);
int* pAddr = (int*)VirtualAlloc(NULL,0x1000,MEM_COMMIT,PAGE_READWRITE);
memset(pAddr, 0, 0x1000);
int* pi = pAddr;
if(pAddr != NULL ){
printf("virtualAlloc succeed, addr is %x\n", pAddr);
printf("addr: %x\n", ((int)pAddr & 0xFFFFF000) |((int)&x & 0x00000FFF)); // get
}else{
printf("virtualAlloc failed!\n", pAddr);
}
pi = (int*)(((int)pAddr & 0xFFFFF000) |((int)&x & 0x00000FFF));
printf("*pi -> %d\n", *pi);
printf("pi -> %x\n", pi);
getchar();
printf("%d\n", *pi);
return 0;
}
getchar()之后,然后修改新申请的(同变量的偏移地址)的线性地址来进行修改相关的PTE,如下图所示
0012ff7C (变量x)
->
0000 0000 00
01 0010 1111 *4 -> 4BC
f7C
003a0f7c (新申请的地址,后12位同偏移)
->
0000 0000 00
11 1010 0000 *4 = E80
f7c
x变量的PTE为36b91867,如下图所示
接着找到新申请的地址003a0f7c的地方
找到新申请的地址003a0f7c,将其线性地址所指向的PTE修改为新的PTE(也就是修改为X线性地址的PTE),命令为!ed 36978e80 36b91867
结果如下
练习,10-10-12分页模式物理内存能够识别的最多范围是多少?
10-10-12,分别就是2^10 * 2^10 * 2^12 ,也就是2^32,那么最多范围就是4G寻址
练习,如何判断2个线性地址是否在同一个物理页?
一个进程中,2个线性地址可以通过线性地址的前5位(十六进制的情况下)是否相同来判断是否在同一个物理页