引子:
一直在研究恶意代码方向与逆向软件方向,面试聊了windows内核与保护模式相关知识,有很多没有回答上来,确实研究过相关资料,但是没有深入研究,加上长时间没有复习,有些遗忘了
基本功不扎实,毕竟好久没写过驱动编程与复习内核/保护模式相关的知识,所以静下心来复习一下吧。
4GB的虚拟内存结构:
虚拟内存地址范围 | 描述 |
---|---|
0x00000000~0x0000FFFF | 64kb大小的空指针区域,当然就不可以访问了 |
0x00001000~0x7FFFFFFF | 加上上述的空指针区域,低2GB的用户态空间 |
0x80000000~0xFFFFFFFF | 高2GB的内核态空间 |
更多的详细划分请参考这篇博客:https://blog.csdn.net/wang010366/article/details/52730052
一个进程地址如何映射到物理地址的?
上述图片只是片面的阐释,一个虚拟内存地址通过页表转换,映射到物理内存地址,多个进程其实都是通过这种机制映射到物理内存。
图片二是虚拟地址映射,注意:缺少了分页的层级,页表中每一项都是一个分页。映射的物理地址过程如何实现的?先介绍Cr0系列的控制寄存器,面试的时候忘的一干二净,那就在学一遍:
寄存器名称 | 描述 |
---|---|
CR0 | 包含处理器标志控制位,如PE,PG,WP等 |
CR1 | 保留 |
CR2 | 专门用于保存缺页异常时的线性地址 |
CR3 | 保存进程页目录地址 |
CR4 | 扩展功能(如判断物理地址扩展模式等),Pentium系列(包括486的后期版本)处理器中才实现 |
➀什么是PE,PG,WP呢?
1、PE:CR0寄存器的第0位,Protection Enable,启用保护标志。如果该位为1,开启了保护模式,反之关闭,当开启保护模式的时候PE\PG都会置位。
2、PG:CR0寄存器的第31位,Paging,分页标志位。如果为1则开启分页机制,禁止分页的话线性地址等同于物理地址,若开启标志位意味着需要开启保护模式。
3、WP:CR0寄存器的第16位,Write Proctect,写保护标志。WP==1的时候意味着只读页面不可执行写操作,wp==0的时候意味着只读页面可执行写操作。
➁CR3寄存器保存了每个进程的页目录地址,什么叫做页目录呢?
Windows下开启保护模式与分页机制后,当前CR0寄存器的属性PE == 1 AND PG == 1,意味着进程中的虚拟地址将通过页表转换映射相对应的物理地址上,如图二所示,我们手工的获取来学习:
首先介绍一下分页机制其中一种记录方式:
非物理地址扩展模式 | 物理地址扩展模式 |
---|---|
非PAE模式 | PAE模式 |
PAE模式:Physical address extension,物理扩展模式。能够在32位操作系统访问超过4GB寻址大小的模式,允许将最多64GB 的物理内存用作常规的4 KB 页面,并扩展内核能使用的位数以将物理内存地址从32扩展到36。
非PAE模式:在非物理扩展模式下,32位最大只能4GB所以,即使你有8G的内存条,也是白费。
那么在非PAE模式下,操作系统分页机制如何实现的?每个分页4kb,一共4GB的内存,4194304KB大小也就是一共1048576个分页,那么如何高效的管理这些分页呢?
1024(PDT) × 1024(PTT) × 4096 = 4GB
1024(PTT) × 1024(PTE) × 4 = 4MB
1024(PDT) ×1024(PTE) = 1MB
如上述公式所示,就是通过这种方式来管理4GB的内存分页,PDT,PTT,PTE又是什么,如下所示
名称 | 描述 |
---|---|
页目录索引表(PDT) | 一级索引 |
页表索引表(PTT) | 二级索引 |
页表项(PTE) | 页表项 PDT(1024项PDE),PTT(1024项PTE) |
需要配合图二一起理解,操作系统会通过CR3寄存器获取当前进程的页表目录地址,然后根据虚拟地址拆分为10,10,12比例,找到页目录,找到页表,然后找到分页加上对应的偏移(物理内存),为了方便理解,当然也是动手写个小程序,在windbg下一探究竟。
➀编写测试代码,如下所示:
int main()
{
printf("虚拟地址:0x%X\n", "hello world");
cout << "hello world" << endl;
system("pause");
}
➁编译后拖入虚拟机,开启双击调试(测试环境win7 32位),运行测试程序(不要关闭回车),windbg下输入!process 0 0查看全部进程及各部分说明,如下所示:
➂上述进程说明中我们看到DirBase地址,这个就是当前进程指向的页目录,我们看看到底对不对?需要明确的字段PROCESS 0x86ca5c18是EPROCESS的地址,输入指令dt 0x86ca5c18 _EPROCESS来看一看,如下所示:
➃一个进程的页目录怎样找?在CreateProcess的第二个阶段,会初始化进程的执行体层EPROCESS数据结构与微内核层KPROCESS数据结构,系统DLL映像目标用户空间且初始化PEB操等等,来看_KPROCESS结构+0x18字段是什么?如下所示:
➄通过上述_KPROCESS获取了PDT的地址,与解析出来PDT数据一样的。每当CPU切片执行进程时候,CR3就会被系统切换,CR3是不是读取当前进程DirectoryTableBase字段作为切换数值有待研究,看一看PDT也就是页表转换的第一层结构,如下所示:
➅解释两个dd,因为这个以前也总是被同学问起,dd怎么啥东西都找不到,在windbg命令下,d系列命令只能查看虚拟地址, 查看物理地址需要使用!d系列命令,PDT是物理地址,如下所示:
➆根据上述一些理论性知识,如图九中所展示的每项便是页表地址(物理地址),根据虚拟地址,测试是否能通过页表转换找到映射的物理存储数据,所以第二步中没输出字符串虚拟地址(图是昨天的),重新来一下(已经关闭了随机基址),如下所示:
➇根据图片四把上述的虚拟地址进行分割,如下所示:
➈对应的二进制换算16进制 (1 <--> 19 <--> B30),其中1代表是页目录表中的第一项,查看页目录表之前需要对地址页目录的PTE了解,如下所示:
➉如上图所示,低12位是属性,高位是地址, 然后页表索引是19,数组元素是保存的指针,需要乘以4字节,如下所示:
⑪怎么什么都没有,还记着我们上述说过,这一台记录方式PAE与非PAE,我们现在所处的环境究竟是怎样,我们打开cmd,利用bcdedit命令,先做了解如下所示:
⑫修改当前pae模式以及nx模式,pae我们知道是物理扩展模式,nx是缓解机制,使某些内存区域不可执行,并使可执行区域不可写DEP,我们也要改为Always Off模式,如下所示:
修改指令如下:
名称 | 关闭指令 | 开启指令 |
---|---|---|
PAE | bcdedit /set pae ForceDisable | bcdedir /set forceEnable |
NX | bcdedit /set nx AlwaysOff | bcdedit /set nx OptIn |
修改后属性如下:
⑬重启系统后,再次按照上述步骤查找具体的分页数据,如下所示:
⑭那么解析虚拟地址之后,如何通过也目录找到具体页表呢?我们需要了解一下页目录中的每一项PDE数据,也就是指针如何分解的,如下所示:
⑮通过上图所示,我们知道低位12位是属性,高位才是地址,意味着我们只需要BaseAddress + 第几项PTT × 4(指针大小)就可以找到相对的页表指针,如下所示:
⑯找到了具体的页表地址,也就是0x342f6025,加上具体的偏移(虚拟地址分解出的低12位)就可以找到映射的物理内存数据保存,当然地址还是要把地位属性去掉,用地址+偏移即可,如下所示:
整个过程解析了虚拟地址将一个地址如何映射到物理内存,他们之间的存在的转换映射关系,当然省略很多机制内部机制。片面的理解保护模式下分页的重要性,我们跟多层面去学习研究虚拟内存与物理内存关系。