写在前面

  此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。

  看此教程之前,问几个问题,基础知识储备好了吗?上一节教程学会了吗?上一节课的练习做了吗?没有的话就不要继续了。


华丽的分割线


练习及参考

本次答案均为参考,可以与我的答案不一致,但必须成功通过。

1️⃣ 拆两个进程的4GB物理页。

点击查看答案

  自己拆吧,也不是真让你把所有的都拆了。这玩意建议还是写个驱动来进行拆解。不过我的教程没写,之后才会讲解。故拆几个比较有特点的就行了,之后学了驱动后可以回来再把这道题给完整地做了。

  首先讨论我们怎么拆,一个物理页有4KB的大小,如果线性地址在同一个物理页,那么它的高20位一定是相同的。故如果完整的拆完,我们需要拆0x1000 * n线性地址。怎么拆已经给你说明白了。

  需要拆解的线性地址类型:0x0 - 0x10000 和 0x10000 - 0x7FFFFFFF 和 高2G三部分,每个部分拆解2-3个即可。这里就不拆了。


2️⃣ 定义一个只读类型的变量,再另一个线性地址指向相同的物理页,通过修改PDE/PTE属性,实现可写。

点击查看答案

  这题目说实话还是有一些坑,主要是独自编写验证代码上。可以点击下方“查看代码”进行查看。

  首先运行我们的代码,报内存访问错误,这个是正常现象,因为它在所谓的常量区。

  先让它跑起来,看到常量所在的线性地址,如下图所示:

  然后我们拆分线性地址,为了图省事,可以用计算器或者自己写个工具。如下图所示:

  然后转到WinDbg中,找到它的进程结构体,找到CR3

  根据10-10-12分页结构进行查看,最终看到如下图所示结果:

  最后发现是因PTE属性限制导致数据无法修改,故用!ed 41795088 410e4027修改它,使它具有可写属性。

  然后我们就能成功的访问常量只读地址了:


点击查看代码
#include "stdafx.h"

const int test = 5;

int main(int argc, char* argv[])
{
int* tmp=(int*)&test;
printf("%p\n",tmp);
*tmp=8;
printf("%d\n",*tmp);
return 0;
}

3️⃣ 分析0x8043F00C线性地址的PDE属性。

点击查看答案

  在虚拟机打开一个notepad进程,然后转到Windbg暂停虚拟机找到它的CR3(我的是0x1b262000)。然后输入!dd 1b262000+0x201*4找到该线性地址对应的PDE,值为004001e3,故该PDE为一个大页,有效可写,并为仅超级用户可用,是全局的,且被访问过和写过。


4️⃣ 修改一个高2G线性地址的PDE/PTE属性,实现Ring3可读。

点击查看答案

  既然是做了第三题了,那我们直接拿第三题给的线性地址开刀好了。首先必须在合适的地方下断点,运行我们的代码,获取的CR34a818000,故!dd 4a818000+0x201*4得到PDE后值为004001e3,但为了更好的实验,故把它指向的物理页的偏移的值改为0x1234。那么如何改呢?这个是个大页,故后22位直接是物理页的偏移。故!ed 0043F00C 1234即可。

  然后我们继续进行检验,如下图所示:

  4660就是16进制的0x1234,故实验成功。


点击查看代码
#include "stdafx.h"
#include <iostream> int main(int argc, char* argv[])
{ int* test=(int*)0x8043F00C; printf("读取到值了:%d",*test);
system("pause");
return 0;
}

5️⃣在0线性地址挂上物理页并执行shellcode调用MessageBox

点击查看答案

  这道题还是有一点坑,不过坑不太深,自己能跳出来。代码我提供了且有注释,自己打开看看。

  通过查看0 地址不能访问是因为没有正确的PTE,如果挂上正确的PTE,那么这个地址就可以访问了。那我们开始用代码进行试验。首先在合适的地方下断点,运行我们的代码,获取的CR33daa3000,且申请的一个物理页的地址是0x3D0000。故按照分页模式找到物理页的PTE,值为3db54067。然后把它填到0 地址的地方。

  然后我们继续进行检验,如下图所示:

  我们成功弹出了一个信息框,故实验成功。


点击查看代码
#include "stdafx.h"
#include <iostream>
#include <windows.h> char shellcode[]={
0x6A,0x00, //PUSH 0
0x6A,0x00, //PUSH 0
0x6A, 0x00 , //PUSH 0
0x6A ,0x00 , //PUSH 0
0xB8, 0x00 ,0x00 ,0x00, 0x00 , //MOV EAX,0000
0xFF, 0xD0 , //CALL EAX
0xC3 //RET
}; int main(int argc, char* argv[])
{ int msgbox=(int)MessageBoxW;
*(int*)&shellcode[9]=msgbox; //我们并不能直接把 shellcode 的 PTE 挂到 0 地址上。
//因为还有偏移,需要单独申请一个独立的物理页。
LPVOID scaddr = VirtualAlloc(NULL,1024,MEM_COMMIT,PAGE_READWRITE);
memcpy(scaddr,shellcode,sizeof(shellcode)); printf("Shellcode物理页:%p\n",scaddr); //下断点,挂物理页
_asm
{
mov eax,0;
call eax;
} system("pause");
return 0;
}

6️⃣ 逆向分析MmIsAddressValid函数。

点击查看答案

  本题目主要目的是为了如何查询PDEPTE。逆向参考结果如下:


PAE 分页

  PAE分页是啥,其实他就是2-9-9-12分页的英文缩写。为什么要有2-9-9-12分页,其实还是物理页不够用了,需要扩展。但想要足够的物理页,位数在那里,你想大也大不了。那么我就需要扩展物理页地址的位数,于是乎2-9-9-12分页诞生了,它整体分页的结构如下:

  与10-10-12分页不同的地方就是,多了一层名为页目录指针表的东西,英文缩写为PDPTT。每个PDEPTE被扩展为8个字节,物理地址描述的位数扩展为24位,故可以描述更多的物理页,下面详细查看它们的结构。

  首先看PDPTT的结构。由2-9-9-122可知第一部分由两位二进制组成,那么最多有4种结果。也就是为什么有五个成员,它的结构如下图所示:

  然后是`PDE`,既然学过了`10-10-12`分页,直接看下面的结构示意图吧:

非大页

大页

  然后是PTE,同理不多说了:

  我们之前做一道作业题目知道,我写的shellcode写到一个页上,它并没有执行权限,但它不是代码,仍然可以被我执行。为了弥补这个漏洞,Intel给我们补了一个硬件层面上的漏洞,它是一个位,处于PDEPTE的最高位,如下图所示:

  如果最高位是1,说明被保护。如果这个是数据,且这个X位被置为1,则会被报出异常不能执行。反之,和正常的10-10-12分页没什么两样。

  一个进程的线性地址仍是4GB的线性空间,有再多的物理页有啥用呢?在10-10-12分页下,假设进程一启动,就把所有的物理页都挂上,且没有任何交换。那么只能启动一个;如果在2-9-9-12分页下,同样的情况,它可以启动4个进程。这个就是2-9-9-12分页的意义。

  Windows也提供了基于硬件层面X位的保护,如下图所示:

  可以看出你自己写的普通程序压根和这个位无缘,但是还以启用的。但是有些打开之后发现提出不支持基于硬件的保护,那是因为虚拟机没开这个选项,如下图所示,转中如下图选项即可:

练习

由于本节内容和10-10-12分页相似,答案不会提供,自己实现,务必把本节练习做完后看下一个讲解内容。不要偷懒,实验是学习本教程的捷径。

  俗话说得好,光说不练假把式,如下是本节相关的练习。如果练习没做好,就不要看下一节教程了,越到后面,不做练习的话容易夹生了,开始还明白,后来就真的一点都不明白了。本节练习比较多,请保质保量的完成。

1️⃣ 定义一个只读类型的变量,再另一个线性地址指向相同的物理页,通过修改PDE/PTE属性,实现可写。

2️⃣ 自己实验有和没有DEP保护的程序,看看效果。(本题将在下一篇提供参考)

3️⃣ 修改一个高2G线性地址的PDE/PTE属性,实现Ring3可读。

4️⃣ 在0线性地址挂上物理页并执行shellcode调用MessageBox

5️⃣ 逆向分析MmIsAddressValid函数。

下一篇

  保护模式篇——TLB与CPU缓存

保护模式篇——PAE分页的更多相关文章

  1. 羽夏看Win系统内核——保护模式篇

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  2. 保护模式篇——TLB与CPU缓存

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  3. x64 番外篇——保护模式相关

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  4. OSLab:开启保护模式

    日期:2019/5/22 关键词:操作系统:OS:保护模式:A20地址线激活:分页开启:二级页表的设置 PS:OSLAB实验课的整理. 本文主要内容是分析操作系统中一个简易的MBR. 建议先阅读:ht ...

  5. ASM:《X86汇编语言-从实模式到保护模式》第16章:Intel处理器的分页机制和动态页面分配

    第16章讲的是分页机制和动态页面分配的问题,说实话这个一开始接触是会把人绕晕的,但是这个的确太重要了,有了分页机制内存管理就变得很简单,而且能直接实现平坦模式. ★PART1:Intel X86基础分 ...

  6. 基础篇-Windows保护模式

    1 一般来说,80x86(80386及其以后的各代CPU)可以在三种模式下运转:实模式,保护模式,V86模式.实模式就是古老的MS-DOS的运行环境.Win95只利用了两种模式:保护模式和V86模式. ...

  7. PAE 分页模式详解

    2016-11-18 记得之前看windows内核原理与实现的时候,在内存管理部分,看到涉及到PAE模式的部分,提到此模式下可以让系统在虚拟地址还是32位宽的情况下,支持64GB的物理内存或者更多.当 ...

  8. x86 保护模式 十 分页管理机制

    x86   保护模式  十  分页管理机制 8.386开始支持分页管理机制 段机制实现虚拟地址到线性地址的转换,分页机制实现线性地址到物理地址的转换.如果不启用分页,那么线性就是物理地址 一  分页管 ...

  9. oslab oranges 一个操作系统的实现 实验三 认识保护模式(二):分页

    实验目的: 掌握内存分页机制 对应章节:3.3 实验内容: 1.认真阅读章节资料,掌握什么是分页机制 2. 调试代码,掌握分页机制基本方法与思路 – 代码3.22中,212行---237行,设置断点调 ...

随机推荐

  1. NOIP模拟50

    过分的神圣,往往比恶魔更加恶质. 前言 最大的一个收获就是不要动不动就码线段树,一定要审清楚题目之后再码!! T1 一开始理解错题了,以为答案是就是 \(\dfrac{\operatorname{le ...

  2. MyBatis的Mapper代理笔记

    MaBatis--Mapper代理 目前使用SqlSession进行增删改查的缺点: 没有办法实现多参传值 书写的时候没有接口,后期的维护低 使用Mapper的动态代理方式来解决问题 具体实现 首先我 ...

  3. 文件流转换(一般用于axios设置接收文件流设置时responseType: 'blob')

    文件流转换 一般用于axios设置接收文件流设置时responseType: 'blob'当接口报错时,前端因已设置responseType: 'blob'无法再接收json格式数据,会把json格式 ...

  4. Java一般命名规范

    一.项目名称 最好用英文,所有单词全部用小写,如testjavaproject.studentmanagement等,当然也也可以用中文,如"学生管理系统"等. 二.Java pr ...

  5. 剑指 Offer 60. n个骰子的点数

    剑指 Offer 60. n个骰子的点数 把n个骰子扔在地上,所有骰子朝上一面的点数之和为s.输入n,打印出s的所有可能的值出现的概率. 你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n ...

  6. 第七章:网络优化与正则化(Part2)

    文章相关 1 第七章:网络优化与正则化(Part1) 2 第七章:网络优化与正则化(Part2) 7.3 参数初始化 神经网络的参数学习是一个非凸优化问题.当使用梯度下降法来进行优化网络参数时,参数初 ...

  7. RMQ区间最值查询

    RMQ区间最值查询 概述 RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A, 回答若干询问RMQ(A,i,j)(i,j<= ...

  8. CSP-J 2021 游记

    今年是本人第一次参加CSP组的竞赛. Day 0 晚上复习了几套初赛试卷,做到晚上十点多结束.其实暑假已经做过不少了. Day 1 早上继续复习noip历年真题,在洛谷有题上面自己做题,一向只能考十几 ...

  9. Docker系列(16)- 容器数据卷

    什么是容器数据卷 docker的理念回顾 将应用和环境打包成一个镜像 数据?如果数据都在容器中,那么我们容器删除,数据就会丢失!新增一个需求:数据可以持久化 MySQL,容器删了等于删库跑路!新增一个 ...

  10. Centos8.X 搭建Prometheus+node_exporter+Grafana实时监控平台

    Prometheus Promtheus是一个时间序列数据库,其采集的数据会以文件的形式存储在本地中,因此项目目录下需要一个data目录,需要我们自己创建,下面会讲到 下载 下载好的.tar.gz包放 ...