Windows内核实验 ——— API调用

前言

我们之前几次的实验大多都是通过设置自己的中断函数来完成一些简单的小东西,如赋值以及修改值等,如果需要实现某高级复杂功能时便需要我们进行调用API来快速省心省力的完成。还是和之前一样,需要我们对系统中断表进行操作,获取到 Ring 0 的权限后进行调用。

实验过程

同样的我们还是先搭建出一个中断处理的模板

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
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>

//DWORD g_tmp = 0;
// eq 8003f500 0040ee00`00081040
void __declspec(naked) IdtEntry() { // 裸函数,不会产生对应的开辟栈帧过程
__asm {
iretd ; 中断返回
}
}

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

int main() {
if ((DWORD)IdtEntry != 0x00401040) {
printf("wrong addr : %p \n", IdtEntry);
exit(-1);
}
crash();
system("pause");
return 0;
}

接下来我们便从此模板下手,尝试调用 Ring 0 的有关API函数,在此之前我们需要先了解一些关于 fs 段寄存器的东西。在 Ring 3 下 fs 段寄存器的值被设置为了0x3B,指向的是,而在内核中设置为0x30,在 Ring 3 下 fs 指向TEB表,而在 Ring 0 中指向的是GDT表的KPCR结构,通常从 Ring 3 进入到Ring 0 时是通过中断门以及程序入口_KiSystemService进行完成的,其完成了push与pop的操作改变fs的值来进行切换,并保存了 Ring 3 的相关结构信息

参考:新手分享_再谈FS寄存器-软件逆向-看雪论坛

1
2
3
push 0x30
pop fs
sti ; 开启中断

通过上面的方式进行切换到 Ring 0 后,我们开启中断,尝试进行对 Ring 0 的 API 进行调用,调用完后我们使用以下代码再将 fs 设置为 Ring 3 下的0x3B

1
2
3
push 0x3B
pop fs
iretd ; 中断返回

通过上面的操作我们已经完成了从零环到三环之间的转换,我们此处尝试调用内核的内存分配函数来完成此次实验,我们使用函数指针来进行获取到对应的函数地址处理,通过这种方式来进行调用

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef DWORD(__stdcall* EX_ALLOCATE)(DWORD PoolType, DWORD NumberOfBytes); // 创建文件指针
EX_ALLOCATE ExAllocatePool = (EX_ALLOCATE)0x80537ff8; // 0x80537ff8 为 ExAllocatePool 函数地址
DWORD g_tmp;
void __declspec(naked) IdtEntry() { // 裸函数,不会产生对应的开辟栈帧过程
__asm {
push 0x30
pop fs; 仿照 _KiFastCallEntry 进入 Ring 0
sti; 开启中断
}
g_tmp = ExAllocatePool(0, 0x1000); // 调用 ExAllocatePool
__asm{
push 0x3B
pop fs ; 回到 Ring 3
iretd ; 中断返回
}
}

有点奇怪的是我们运行起来上面的代码时返回的地址处的内存没有被初始化,仍然有一些数据

image-20230419211633531

通过 Windbg 进行查看内存

我们尝试修改一下

发现可以被修改,当释放断点后继续运行程序时可以发现并没有蓝屏,此处猜测可能是跟 CPU Cache 有关系了,需要刷新 TLB 了

参考:Windows保护模式学习笔记(十)—— TLB_tlb满了怎么办_lzyddf的博客

总结

此次实验中我们简单的创建了一个零环到三环的转换过程,通过一个函数指针进行获取对应的函数地址,我们通过指针来进行调用对应API接口。

或者可以使用WDM.h中的ExAllocatePool来进行内存的分配,但是使用该头文件需要配置WDK,搭建相关驱动环境。

但是实验中我们创建的环境切换理论上并不安全,因为我们在期间开启了中断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void __declspec(naked) IdtEntry() { // 裸函数,不会产生对应的开辟栈帧过程
__asm {
push 0x30
pop fs; 仿照 _KiFastCallEntry 进入 Ring 0
sti; 开启中断
}
g_tmp = ExAllocatePool(0, 0x1000); // 调用 ExAllocatePool
__asm{
push 0x3B
pop fs ; 回到 Ring 3
; 此处遇到中断便会产生蓝屏信息
iretd ; 中断返回
}
}

如果我们在pop返回三环之后但在中断返回前遇到中断时便会导致系统出现蓝屏,但是因为计算机执行指令的速度十分的快,我们遇到中断的概率十分的低。处于安全性考虑我们需要在其前面加入cli进行关闭中断,对于上述操作我们同样可以在_KiServiceExit(零环退出函数)中看到


Windows内核实验 ——— API调用
https://equinox-shame.github.io/2023/04/20/Widnows内核实验 — API调用/
作者
梓曰
发布于
2023年4月20日
许可协议