保护模式篇之PAE分页,详细介绍 2-9-9-12 分页
此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。
看此教程之前,问几个问题,基础知识储备好了吗?上一节教程学会了吗?上一节课的练习做了吗?没有的话就不要继续了。
🔒 华丽的分割线 🔒
本次答案均为参考,可以与我的答案不一致,但必须成功通过。
1️⃣ 拆两个进程的<code>4GB</code>物理页。
🔒 点击查看答案 🔒
自己拆吧,也不是真让你把所有的都拆了。这玩意建议还是写个驱动来进行拆解。不过我的教程没写,之后才会讲解。故拆几个比较有特点的就行了,之后学了驱动后可以回来再把这道题给完整地做了。
首先讨论我们怎么拆,一个物理页有<code>4KB</code>的大小,如果线性地址在同一个物理页,那么它的高20位一定是相同的。故如果完整的拆完,我们需要拆<code>0x1000 * n</code>线性地址。怎么拆已经给你说明白了。
需要拆解的线性地址类型:0x0 - 0x10000 和 0x10000 - 0x7FFFFFFF 和 高2G三部分,每个部分拆解2-3个即可。这里就不拆了。
2️⃣ 定义一个只读类型的变量,再另一个线性地址指向相同的物理页,通过修改<code>PDE</code>/<code>PTE</code>属性,实现可写。
这题目说实话还是有一些坑,主要是独自编写验证代码上。可以点击下方“查看代码”进行查看。
首先运行我们的代码,报内存访问错误,这个是正常现象,因为它在所谓的常量区。

先让它跑起来,看到常量所在的线性地址,如下图所示:
然后我们拆分线性地址,为了图省事,可以用计算器或者自己写个工具。如下图所示:
然后转到<code>WinDbg</code>中,找到它的进程结构体,找到<code>CR3</code>:
根据<code>10-10-12</code>分页结构进行查看,最终看到如下图所示结果:
最后发现是因<code>PTE</code>属性限制导致数据无法修改,故用<code>!ed 41795088 410e4027</code>修改它,使它具有可写属性。
然后我们就能成功的访问常量只读地址了:
🔒 点击查看代码 🔒
3️⃣ 分析<code>0x8043F00C</code>线性地址的<code>PDE</code>属性。
在虚拟机打开一个<code>notepad</code>进程,然后转到<code>Windbg</code>暂停虚拟机找到它的<code>CR3</code>(我的是<code>0x1b262000</code>)。然后输入<code>!dd 1b262000+0x201*4</code>找到该线性地址对应的<code>PDE</code>,值为<code>004001e3</code>,故该<code>PDE</code>为一个大页,有效可写,并为仅超级用户可用,是全局的,且被访问过和写过。
4️⃣ 修改一个高2G线性地址的<code>PDE</code>/<code>PTE</code>属性,实现<code>Ring3</code>可读。
既然是做了第三题了,那我们直接拿第三题给的线性地址开刀好了。首先必须在合适的地方下断点,运行我们的代码,获取的<code>CR3</code>是<code>4a818000</code>,故<code>!dd 4a818000+0x201*4</code>得到<code>PDE</code>后值为<code>004001e3</code>,但为了更好的实验,故把它指向的物理页的偏移的值改为<code>0x1234</code>。那么如何改呢?这个是个大页,故后<code>22</code>位直接是物理页的偏移。故<code>!ed 0043F00C 1234</code>即可。
然后我们继续进行检验,如下图所示:
<code>4660</code>就是16进制的<code>0x1234</code>,故实验成功。
5️⃣在<code>0</code>线性地址挂上物理页并执行<code>shellcode</code>调用<code>MessageBox</code>。
这道题还是有一点坑,不过坑不太深,自己能跳出来。代码我提供了且有注释,自己打开看看。
通过查看<code>0 地址</code>不能访问是因为没有正确的<code>PTE</code>,如果挂上正确的<code>PTE</code>,那么这个地址就可以访问了。那我们开始用代码进行试验。首先在合适的地方下断点,运行我们的代码,获取的<code>CR3</code>是<code>3daa3000</code>,且申请的一个物理页的地址是<code>0x3D0000</code>。故按照分页模式找到物理页的<code>PTE</code>,值为<code>3db54067</code>。然后把它填到<code>0 地址</code>的地方。
我们成功弹出了一个信息框,故实验成功。
6️⃣ 逆向分析<code>MmIsAddressValid</code>函数。
本题目主要目的是为了如何查询<code>PDE</code>和<code>PTE</code>。逆向参考结果如下:
<code>PAE</code>分页是啥,其实他就是<code>2-9-9-12</code>分页的英文缩写。为什么要有<code>2-9-9-12</code>分页,其实还是物理页不够用了,需要扩展。但想要足够的物理页,位数在那里,你想大也大不了。那么我就需要扩展物理页地址的位数,于是乎<code>2-9-9-12</code>分页诞生了,它整体分页的结构如下:
与<code>10-10-12</code>分页不同的地方就是,多了一层名为<code>页目录指针表</code>的东西,英文缩写为<code>PDPTT</code>。每个<code>PDE</code>和<code>PTE</code>被扩展为8个字节,物理地址描述的位数扩展为<code>24位</code>,故可以描述更多的物理页,但个数减半,变成了512个。下面详细查看它们的结构。
首先看<code>PDPTT</code>的结构。由<code>2-9-9-12</code>的<code>2</code>可知第一部分由两位二进制组成,那么最多有4种结果。也就是为什么有五个成员,它的结构如下图所示:
然后是<code>PDE</code>,既然学过了<code>10-10-12</code>分页,直接看下面的结构示意图吧:
非大页
大页
然后是<code>PTE</code>,同理不多说了:
我们之前做一道作业题目知道,我写的<code>shellcode</code>写到一个页上,它并没有执行权限,但它不是代码,仍然可以被我执行。为了弥补这个漏洞,<code>Intel</code>给我们补了一个硬件层面上的漏洞,它是一个位,处于<code>PDE</code>和<code>PTE</code>的最高位,如下图所示:
如果最高位是<code>1</code>,说明被保护。如果这个是数据,且这个<code>X</code>位被置为<code>1</code>,则会被报出异常不能执行。反之,和正常的<code>10-10-12</code>分页没什么两样。
一个进程的线性地址仍是<code>4GB</code>的线性空间,有再多的物理页有啥用呢?在<code>10-10-12</code>分页下,假设进程一启动,就把所有的物理页都挂上,且没有任何交换。那么只能启动一个;如果在<code>2-9-9-12</code>分页下,同样的情况,它可以启动4个进程。这个就是<code>2-9-9-12</code>分页的意义。
<code>Windows</code>也提供了基于硬件层面<code>X位</code>的保护,如下图所示:
可以看出你自己写的普通程序压根和这个位无缘,但是还以启用的。但是有些打开之后发现提出不支持基于硬件的保护,那是因为虚拟机没开这个选项,如下图所示,转中如下图选项即可:
由于本节内容和<code>10-10-12</code>分页相似,答案不会提供,自己实现,务必把本节练习做完后看下一个讲解内容。不要偷懒,实验是学习本教程的捷径。
俗话说得好,光说不练假把式,如下是本节相关的练习。如果练习没做好,就不要看下一节教程了,越到后面,不做练习的话容易夹生了,开始还明白,后来就真的一点都不明白了。本节练习比较多,请保质保量的完成。
1️⃣ 定义一个只读类型的变量,再另一个线性地址指向相同的物理页,通过修改<code>PDE</code>/<code>PTE</code>属性,实现可写。
2️⃣ 自己实验有和没有<code>DEP</code>保护的程序,看看效果。(本题将在下一篇提供参考)
3️⃣ 修改一个高2G线性地址的<code>PDE</code>/<code>PTE</code>属性,实现<code>Ring3</code>可读。
4️⃣ 在<code>0</code>线性地址挂上物理页并执行<code>shellcode</code>调用<code>MessageBox</code>。
5️⃣ 逆向分析<code>MmIsAddressValid</code>函数。
保护模式篇——TLB与CPU缓存
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
本文来自博客园,作者:寂静的羽夏 ,一个热爱计算机技术的菜鸟
转载请注明原文链接:https://www.cnblogs.com/wingsummer/p/15364648.html