Windows 内核实验 ——— 多核复杂性

前言

在上一次中断提权实验中,我们采用的是单核,但是我们日常中使用的都是多核,那么同样的在多核中存在有一些特性,本次实验将关注于多核下的一些特性,同样因笔者知识有限,同样可能存在些不正确与不严谨的部分,还请多多指正

实验过程

我们还是一样的环境,因此不需要额外的搭建新的虚拟机,我们将原来的单处理器单内核进行修改

image-20230327211540410.png

保存后我们启动虚拟机,还是同样的使用 WinDbg 来对其进行调试

image-20230327212858642.png

我们中断下来时,可以发现多出来了一个 0,那么它是什么呢?还记得吗,我们之前将单内核修改为了 2,不难猜测这个是两个内核中的一个,我们查看 0 号内核的系统中断表

image-20230327213139013.png

简单观察一下,可以发现其中断表和我们之前提权实验相同,但是我们如果切换到 1 号内核呢,查看其内核

一般来说多个内核会存在有多个寄存器以及中断表,在内核 0 中存在有 eax 等寄存器,那么我们也可以在内核 1 中找到 eax,但是两个的值似乎不太一样,同理于我们将要查看的系统中断表

WinDbg 切换内核

1
~1

上述代码为切换到 1 号内核,我们修改波浪线后面的数字便可以切换到对应编号的内核

image-20230327213732832.png

我们可以观察到在 1 号内核中我们的系统中断表的地址与 0 号内核不一样,但是其地址里对应的内容是相同的,那么问题来了,如果我们对 0 号内核的系统中断表进行修改那么 1 号内核中的中断表会被修改吗?

image-20230327214716910.png

修改过后我们查看 1 号内核的系统中断表,如下图

image-20230327215013349.png

对此我们可以发现两个中断表并不一样,我们可以发现两个内核是相互独立的,如果我们拿提权实验的代码在虚拟机执行会怎么样?

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

DWORD g_tmp = 0;
void __declspec(naked) IdtEntry() { // 裸函数,不会产生对应的开辟栈帧过程
// 进入到此处是就是 ring 0 的权限
// 此处注意用一些 ring 3 的指令可能会存在些问题,一般进入到 ring 0 权限后尽量少做操作 ;
__asm {
push eax
mov eax, dword ptr ds : [0x8003f400] ; 因为现在处于 ring 0 需要我们指定对应的段寄存器
mov g_tmp, eax
pop eax
iretd ; 中断返回
}
}
void crash() {
__asm {
int 0x20
}
}
int main() {
if ((DWORD)IdtEntry != 0x00401040) {
printf("wrong addr : %p ", IdtEntry);
exit(-1);
}
crash();
printf("%x\n", g_tmp); // 打印对应地址内容
system("pause");
return 0;
}

需要注意的是我们需要将两个的内核的断点让其恢复,之后再执行对应程序,我们可以看到一下两种现象

image-20230327215920210.png
image-20230327220027517.png

为什么会出现上述情况?

在操作系统中两个内核是并行运行的,我们之前修改了 0 号内核,而未修改 1 号内核,当 CPU 调度 0 号内核运行时,那么我们便会打印出对应的地址值,而当调用 1 号内核时,因为我们没有修改中断表,因此无法实现提权的过程进而无法打印出对应地址处的值了

我们尝试在中断处理函数中将一些东西写入到内核中的地址去

1
2
3
4
5
6
7
8
9
10
11
12
13
void __declspec(naked) IdtEntry2() {
__asm {
cli; 将中断标志器置0,不允许中断
push eax
mov eax, cr0; CR0的位16是写保护 ,我们将写保护关闭后尝试在系统某些位置写入一些地址
and eax, ~10000h
mov cr0, eax
mov eax, 0xFFFFFFFF
mov ds : [0x80542520] , eax; 百分百会出问题,0x80542520为三环到零换转换函数 KiFastCallEntry地址,会经常被调用
; 因为我们将一个有写保护的地址进行修改,当我们的某个内核执行到此处时便会触发异常,导致蓝屏
iretd
}
}

当我们调用IdtEntry2函数时虚拟机会出现卡死状态,同时调试器自动断下,我们此时产生异常信息

image-20230328224552141

同时我们查看对应地址,可以发现已经成功修改了对应地址处值

image-20230328230636322

同时我们已经无法切换到对应的虚拟机中了

image-20230328231149484

上述过程说明了一个内核将我们的地址处的内容进行修改,当另一个核执行到此处时发生异常,也同样说明我们在多核下对内核的函数挂钩的难度较大,以及关闭分页保护进行写入是不安全的

image-20230328231910234

总结

我们此处开启了多核进行调试观察,我们可以简单的了解到在多核下内核与内核之间是并行关系,CPU通过调度来进行处理多核。我们也可以注意到多核下有许多相同的东西,且是相互独立的,我们修改其中一个内核的中断表而查看另一个的发现并没有变动,但是我们将其写入内存时便可以发现对应部分都被修改了,以此窥见多内核下的复杂性


Windows 内核实验 ——— 多核复杂性
https://equinox-shame.github.io/2023/03/28/Windows 内核实验 — 多核复杂性/
作者
梓曰
发布于
2023年3月28日
许可协议