avatar

Catalog
页目录表、页表基址

考虑这样一个问题,我们现在可以通过在Windbg里找到线性地址所在的物理页,通过修改物理页的属性,就可以实现一些原本受限的功能。例如将常量区对应的物理页R/W属性修改为1,便可以修改位于常量区的元素。

但是,以上操作都是基于Windbg在双击调试的环境中实现的,那么一旦脱离了调试器,该如何通过代码来实现对物理页属性的修改呢?这就需要借助于页目录表基址和页表基址了。

页目录表基址

结论:C0300000就是页目录表基址,接下来我们来验证这个结论。

C0300000拆分

C0300000: 1100 0000 0011 0000 0000 0000 0000 0000

每部分位数 二进制 十六进制
10 11 0000 0000 300
10 11 0000 0000 300
12 0000 0000 0000 0

Cr3

这里以记事本(notepad.exe)为例,来验证一下,C0300000就是页目录表基址,首先查看记事本对应的Cr3指向的物理地址。

我们知道,Cr3指向的是PDT的首地址,这里可以看到4个PDE有值。

查看C0300000物理页

接下来的步骤就是比较熟悉的,根据拆分后的线性地址,寻找物理页的过程了。但是这一次,要慢点看。

这一步很容易理解,Cr3.base + 300*4,通过Cr3和线性地址的前10位,我们找到了PDE的值:7ea49063

什么?又重复了一遍?实际上不是,由于PDE的值为7ea49063,后12位是属性位,因此,7ea49000是我们要找的PTT的首地址,然后通过PTT.base + 300*4,就得到了PTE的值:7ea49063。

有了PTE的值,加上最后12位的偏移(此处为0),就可以找到物理页。

得到结果后,是不是很惊讶?C0300000这个线性地址对应的物理页上的物理地址,竟然和Cr3指向的物理地址完全一样!也就是说,以后不需要Cr3,只需在当前程序内,通过C0300000这个线性地址就可以得到当前程序PDT的首地址了

如何利用

是啊,C0300000这个地址有啥用呢?当然有用,而且非常有用。回到文章开头的问题,我们该如何在不使用Windbg的情况下,修改物理页的属性呢?

我们知道,想要修改物理页的属性,需要先修改物理页对应的PDE和PTE,那要如何找到PDE和PTE呢,由于在编写代码时,用到的都是线性地址,而C0300000这个线性地址刚好就可以找到PDT的首地址,这样我们拆分想要修改的物理页属性的线性地址,将前10位加上C0300000即可找到对应的PDE。

既然PDE找到了,那不就有了PTT的首地址,这样PTE不也就可以找到了吗?并不是这样,尽管找到了PDE,但是由于PDE里面存着的是物理地址,如果直接访问PDE里面存的那个地址,在代码中会转变为一个线性地址,因此并不能通过PDE获取PTT的首地址,也就不能获取到PTE了,想要找到PTE,还得需要用到另外一个基地址,就是页表基址。

页表基址

还是直接上结论,页表基址:C0000000

接下来我们来验证。

C0000000拆分

C0000000: 1100 0000 0000 0000 0000 0000 0000 0000

每部分位数 二进制 十六进制
10 11 0000 0000 300
10 00 0000 0000 0
12 0000 0000 0000 0

Cr3

这里还是以记事本(notepad.exe)为例:

我们查看Cr3指向的物理地址,当前共有4个PDE的有值的,而PDE的值,就是PTT的首地址,以第一个PDE(36c24067)为例,其中PTT的首地址为36c24000

查看C0000000物理页

步骤和之前一样,就直接看结果好了。

发现,C0000000这个线性地址所对应的物理页,刚好是36c24000,也就是第一个PDE对应的PTT的首地址。由此可以进一步推断,C0001000则是第二个PDE对应的PTT的首地址,以此类推。

再看10-10-12分页

现在再来看10-10-12分页时,看法就会有所不一样了。

  1. 实际上页表(PTT)被映射到了从0xC0000000到0xC03FFFFF的4M地址空间
  2. 在这1024个表中有一张特殊的表:页目录表(PDT)
  3. 页目录表(PDT)被映射到了0xC030000开始处的4KB大小的地址空间

总结

有了0xC0300000和0xC0000000能做什么?

掌握了这两个地址,就掌握了一个进程所有的物理内存读写权限。

公式总结:

  • 什么是PDI和PTI? 将32位线性地址拆分位10(PDI)-10(PTI)-12
  • 访问页目录表(PDT)的公式:0xC0300000 + PDI x 4
  • 访问页表(PTT)的公式:0xC0000000 + PDI x 1000 + PTI x 4(不用*号因为会被转义)

其它关于页的细节

  • 高2G有一些大页,即4MB页
  • 两个进程低2G几乎不同,高2G几乎相同
  • 一个进程低2G的内存空间,前64K与后64K是没有使用的(线性地址0 - 00010000 与 7FFF0000 - 7FFFFFFFF)

谁填充了这些表呢

进程本身可以通过0xC0300000和0xC0000000访问修改任意物理页,那么是谁为我们填充0xC0300000和0xC0000000的PDE与PTE呢?

进程的创建过程:当创建B进程时,先在A进程中将B进程所有信息全部构建好,然后切换Cr3即可。也就是说,最开始的这张表是由A进程填充的。

Author: cataLoc
Link: http://cataloc.gitee.io/blog/2020/03/20/%E9%A1%B5%E7%9B%AE%E5%BD%95%E8%A1%A8%E3%80%81%E9%A1%B5%E8%A1%A8%E5%9F%BA%E5%9D%80/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶