目录
之前有说过x86保护模式下的分页.这里为了复习再说一遍,在这里可能为了简单介绍会遗漏些许.所以贴出之前的保护模式分页机制资料
https://cloud.tencent.com/developer/article/1998771
https://cloud.tencent.com/developer/article/1998773
https://cloud.tencent.com/developer/article/1998786 内存读
CPU提供了段的机制来进行内存保护,而我们学习保护模式的本质也是学习windows如何保护内存的. 微软没有使用段的机制
而是直接使用的页的机制.
例如:
0x00401000 mov eax,[0x12345678]
在虚拟地址执行汇编指令的时候本质是去GDT表中查段描述符表.然后从段描述符表中取出 段.base + 上偏移来进行定位内存的.
在讲解段之前已经说过了.但是我们知道操作系统在x32下.就没有使用段了.设置段.base都是0.
所以段.base(0) + 偏移就是线性地址.
而x32下内存保护模式就是先把逻辑地址(俗称虚拟地址)转为线性地址再去查表.
其实如果你学过C语言或者其它高级语言那么下面说的就很清楚了. 其实保护模式的本质就是查表. 也就是数组来保存地址的.
一个数组中保存了另一个数组的首地址. 另一个数组的首地址就保存了物理地址. 只不过有些许属性而已.
上面说了.我们需要的数据都会在内存中.而且是查表得来的.所以在windows内核中有一个寄存器保存的是我们的物理地址.
这个寄存器就是CR3寄存器.而每个进程都有一个物理地址.也称为DirBase 这个DirBase是记录的每个进程的物理地址起始地址的.
看下图:
通过CR3查询页目录表(也就是我所说的第一个数组) 然后数组中记录着另一个数组的起始地址(页表) 页表中记录着就是物理页所在的内存了.
每个页表是4kb大小(4096个字节) 也有的是4MB大小.如果判断是4MB的大小,这个就要等到后面学的属性位了.
在这里我们先按照4kb 10-10-12分页来进行讲解.后面会把双机调试的配置图贴出.便于自己的私下调试.
既然要看虚拟内存所在的物理页在哪里,那么第一步就是将虚拟内存(逻辑地址)转为线性地址. 但是我也说过了x32下没有使用段.所以虚拟内存就是线程地址了.
转换之后按照10-10-12来进行分位. 10-10-12的意思就是一个线性地址有32bit(位) 我们按照 10-10-12来将这个32bit位进行分组,然后转为10进制.高位10个二进制代表查询页目录表的索引. 第二个10位代表查询页表的索引.
例如:
步骤 | 数值 | 说明 |
---|---|---|
1.确定要看的逻辑地址(线性地址) | 0x00401000 | 我们要看的线性地址在物理页中哪里保存 |
2.线性地址转换为对应二进制位 | 00000000010000000001000000000000 | 第二步就是转为32位bit.然后下一步我们就分组 |
3.二进制按照10-10-12分组 | 0000000001 0000000001 000000000000 | 这个是分组后的数值 |
4.二进制分组转为索引 | 1 1 0 | 确定了页目录表是在第一项 页表也是第一项 |
所以有时候大家调试程序多的时候对0x00401000很熟悉.而内核中PDE PTE也是第一项.
既然明白了原理,那么我们就可以看任一进程中的线性地址所在的物理页了. 当然也可以进行更改. (后面会说)
首先我们要把起始地址拿到手. 我们的CR3寄存器记录的是物理地址,但是这个物理地址不是我们要查看的进程内的.当然每个进程当使用的时候,CR3都会切换为这个进程的CR3(物理地址起始位置)这里我们先不说如何切换.直接使用进程中的物理地址.
首先VC6.0写了个程序,调用VirtualAlloc申请一块内存.然后内存中填写为HelloWorld 而且地址也输出了.
那么我们就要看这个地址在物理内存哪里进行存放.
我们的进程名是1.exe
windbg使用 !process 0 0 来查找我们的进程
确认了我们的物理地址是 0x19b87000 下一步就是将我们要看的虚拟地址进行索引转化
0x3a0000 转化出来的索引为: PDE(页目录表) 0 PTE(页表)3A0
利用windbg的物理内存查看命令进行查看. 查看的时候记住 PDE PTE中的低12位为属性位.如有有值我们不用管.暂时填充为0进行查询.
当PTE查询之后,别忘了加上原来你要查看的虚拟地址的12位. 也就是我们所说的 10-10-12 10-10当索引 12当数值
最终查询出了HelloWord所在的地方.
明白了其原理我们则可以编写代码来实现自己的内存读函数了.
这里说下思路:
1.内核中遍历进程,或者ring3传入进程到内核.
2.内核中通过进程Pid找出对应的EPROCES结构 (PsLookupProcessByProcessId)
3.通过EPROCESS结构找到其DirBase的偏移.获取其偏移位置的DirBase的物理地址
4.通过10-10-12分页的模式,拆分传入的你想查看的这个进程的任一虚拟地址.
5.通过上述实际操作的原理,进行自己读取内存.
修改bootini进行设置即可.
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="10-10-12" /execute=optin /fastdetect /debug /debugport=com1 /baudrate=115200
其实主要就是 将noexecute 修改为 execute 并且添加了调试端口. 这里设置的是com1