Windows内核实验 ——— 零地址读写
前言
我们都知道在C语言中0地址是无法进行读写的,会造成读写异常。本质上来说是因为0地址是一个不存在的物理页面,但是我们可以尝试进行对其修改,将其PTE进行修改,进而我们可以对其进行读写操作
实验过程
我们可以先通过!pte 0
来测试零地址是不可读写的
可以观察到下面的not valid
,证明其不可读写的特性。但是我们之前提到了对PTE修改的计算公式,我们可以利用该计算方式来修改零地址处的PTE值,进而使其可读写
1 |
|
我们此处采用代码来对其进行修改,将零地址设置为可读写
1 |
|
上述代码我们构造了一个可读写的全局变量signFlag
,我们将其PTE的值赋给零地址, 让其获得可读写的属性
此时访问零地址和访问
signFLag
处地址是一样的,因为我们已经将零地址的PTE挂在了signFlag
上,其指向的物理地址是一样的
我们执行完上述代码后我们再次使用!pte 0
来进行查看对应零地址的页属性
可以看到其不再是not valid
,而是可写可读,我们可以使用Windbg
进一步的访问零地址,观察0x18
地址处的数据是否被修改为了0xAABBCCDD
同时我们访问signFlag
对应的页地址0x18
处的数据,我们可以看到其内容也是一样的
需要注意的是
signFlag
最后的三位(16进制下)需要替换为 0,才是对应的页地址的起始位置
总结
对于上述的代码是不完全安全的,存在有一定的概率导致蓝屏。因为有TLB表的存在
TLB表作为页表缓冲存放着一些一些页表文件。当CPU要在主内存寻址时,不是直接在内存的物理地址里查找的,而是通过一组虚拟地址转换到主内存的物理地址,TLB就是负责将虚拟内存地址翻译成实际的物理内存地址,而CPU寻址时会优先在TLB中进行寻址
在上述代码中我们修改了映射关系将零地址的PTE内存映射到了我们自己定义的一共可读可写的页上而未对TLB表进行刷新,当CPU寻址时仍在未刷新的部分进行寻找,进而引发读写异常进而导致蓝屏
对于上述操作我们可以利用一些TLB的性质来对其进行刷新操作,x86_32
仅提供了两种硬件接口来刷新TLB表项:
-
向
cr3
寄存器写入值时,会导致处理器自动刷新非全局页的TLB表项 -
在Pentium Pro以后,
invlpg
汇编指令用来无效指定线性地址的单个TLB表项无效。
我们使用第一个最简单的来进行刷新即可
1 |
|
我们可以通过手动写入cr3来完成对应的刷新工作,进而规避出现读写异常的情况发生