Windows内核实验 ——— 零地址读写

前言

我们都知道在C语言中0地址是无法进行读写的,会造成读写异常。本质上来说是因为0地址是一个不存在的物理页面,但是我们可以尝试进行对其修改,将其PTE进行修改,进而我们可以对其进行读写操作

实验过程

我们可以先通过!pte 0来测试零地址是不可读写的

可以观察到下面的not valid,证明其不可读写的特性。但是我们之前提到了对PTE修改的计算公式,我们可以利用该计算方式来修改零地址处的PTE值,进而使其可读写

1
PTE ->  ((addr >> 12) << 3) + 0xC0000000

我们此处采用代码来对其进行修改,将零地址设置为可读写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include<stdio.h>
#include<Windows.h>

#define PTE(addr) ((DWORD *)(((addr >> 12) << 3) + 0xC0000000))

// 0x004197B0 signFlag addr
volatile DWORD g_var; // 加 volatile 防止编译器优化
DWORD signFlag = 0x12345678;

void __declspec(naked) IdtEntry() {

// 修改零地址的页为signFlag的页地址
PTE(0)[0] = PTE(0x004197B0)[0];
PTE(0)[1] = PTE(0x004197B0)[1];

g_var = *(DWORD *)0x18; // 0 地址指向 0x18 地址
*(DWORD *)0x18 = 0xAABBCCDD; // 修改 0x18 地址处的数据

__asm {
iretd
}
}

void crash() {
__asm {
int 0x20
}
}

int main() {
if ((DWORD)IdtEntry != 0x00401040) {
printf("Your addr is: %p\n", IdtEntry);
return 0;
}

crash();

printf("signFlag addr : %p\n", &signFlag);
printf("g_var addr : %p\n", &g_var);

system("pause");
return 0;
}

上述代码我们构造了一个可读写的全局变量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表项:

  1. cr3寄存器写入值时,会导致处理器自动刷新非全局页的TLB表项

  2. 在Pentium Pro以后,invlpg汇编指令用来无效指定线性地址的单个TLB表项无效。

我们使用第一个最简单的来进行刷新即可

1
2
3
4
__asm{
mov eax, cr3
mov cr3, eax
}

我们可以通过手动写入cr3来完成对应的刷新工作,进而规避出现读写异常的情况发生


Windows内核实验 ——— 零地址读写
https://equinox-shame.github.io/2023/06/05/Windows内核实验 — 零地址读写/
作者
梓曰
发布于
2023年6月5日
许可协议