24-PDT/PTT基址

概述

在上一篇的总结中,我们有一个小小的愿望:把整个 4GB 线性地址的 U/S 位改成 1。我们要去 WinDbg 中手动一个一个的修改吗?显然不可能,给你一年时间你也未必改的完。

既然如此,我们必然要能通过程序来修改 PDE 和 PTE 了。要知道,无论如何,程序中使用的都只能是线性地址。这可为难了,我们并不知道页目录基址。可别想着把 CR3 中保存的那个页目录基址拿来用,CR3 中保存的那可是物理地址,你拿过来,程序也没法用。所以,我们必须要想到,会不会存在这么个线性地址,它直接就可以访问 PDT。

整个 CPU 中,除了 CR3 保存的是页目录的物理地址,以及页目录和页表中保存的是物理页索引号(也可以当成物理地址),就没有其它地方保存物理地址了。

给某个线性地址挂上 PDT 或 PTT 的物理页

除非,有一个线性地址,它指向了页目录。这很容易做到,在《读写空指针》那一篇你已经学会了。无非就是把那一篇的图10中的 PTT 的第一个 PTE 修改为 PDT 的物理地址而已。就好比图1这样。

 


24-PDT/PTT基址
图1 让线性地址0x00000000 映射到页目录的物理地址

 

如此一来,访问 PDT 不就轻而易举,只要 0x00000000, 0x00000004, 0x00000008, ... , 0x00000ffc 这样便可以访问到所有的 PDE 了。

那么,如何再去访问 PTT 呢?要知道,PTT 可多达 1024 个。比如再把 PTT 中的第一个 PTE 修改一下,指向这个 PTT 自己,这样又可以访问整个 PTT 了。就像图2这样。

 


24-PDT/PTT基址
图2 让线性地址0x00000000 映射到PTT的物理地址。

 

如此一来,你又能按照同样的方法去挨个访问这个PTT中所有的PTE了。

虽然这个方法比你手工改1024个PDE和1024×10241024×1024个PTE要强的多,可是你不觉得这仍然很烦吗?

PTT 本身也在物理页

首先,我们要承认一个事实,PTT 本身也占用一个物理页。既然如此,我们有理由可以构造出一个特殊的 PTT,这个PTT中的每个表项,它指向了所有的 PTT。如果你觉得有点乱,看下面的图3.

 


24-PDT/PTT基址
图3 自己构造一个 PTT,让所有PTE指向1024个PTT,包括它自己.

 

你会说,这个特殊的PTT不就是页目录吗?慢着慢着,我还没说它是页目录,这玩意是我自己手工在 WinDbg 中构造出来的。不要笑,我搞了一个通宵。

修改PDE指向PTT768

好了,还差一步。看图4.

 


24-PDT/PTT基址
图4 页目录页表全貌

 

到这里大功告成了,如此一来,访问所有页表不是轻而易举了。

继续改进

我知道,有朋友觉得这么做很傻。搞了一个通宵,就是为了让一个PTT指向所有其它PTT。为何自己不好好利用一下页目录呢?换句话说,如果 PTT768就是页目录……

 


24-PDT/PTT基址
图5 PTT768,其实就是页目录

 

好吧,我承认,我骗了你,我没搞一个通宵。我确实利用了页目录。这个页目录,既充当了页目录,又充当了页表,不是吗?

768是什么?

768 并不是我随便写的,而是有预谋的。

768 的16 进制,就是 0x300,刚刚那个特殊PTT768,也就是第 0x300 个页表,很自然的,PTT768中的第 0x300 个 PTE,里头填写的是 PTT 768 的物理地址。

然而,这个PTT768又是页目录。

PTT768充当着三重身份——页目录、页表、普通物理页。

PDT 基址

如此,PTT768的线性地址的三段式就是300-300-0,页目录的0x300号PDE指向的页表(还是PTT768)的第 0x300 号PTE指向的物理页(还是PTT768)。这个三段式换算成线性地址就是 0xc0300000

PTT 基址

一共有1024个PTT,第 0 个PTT的那就是页目录的0x300号PDE指向的页表(还是PTT768)的第 0 号PTE指向的物理页(PTT0),它的线性地址三段式300-000-000

总结

本篇有点绕。无论如何,你都要记住这个上帝PTT768,它有三重身份。下节,我们会重点分析0xc0300000

其实,无论是 Windows 还是 Linux,都存在着这么个上帝 PTT,只要你有一双发现它的眼睛。