声明:
(1)本篇文章实验针对 未开启内核隔离(KPTI) 的Window10系统
(2)本次实验的实现 并不代表R3调用R0可以完全只由Ring3完成
win10并不使用调用门、陷阱门、任务门, 一律只用中断门,本次实验采用中断门提权实现R3调用R0函数。
没开启KPTI, Cr3始终为内核Cr3。
环境工具
(1) 调式工具:Windbg
(2) 虚拟机:windows10 19044.2846
(3) IDE: VS2019
一、原理分析
1.IDT表、中断描述符
-
中断:只使用一张IDT表,内核可以根据栈上的CS判断先前模式。
IDT表地址位于KPCR偏移0x38处
nt!_KPCR
+0x038 IdtBase : Ptr64 _KIDTENTRY64
-
64位中断门描述符:
图来自Intel 第3卷 Figure 6-7. 64-Bit IDT Gate Descriptors
参照此图方便后续构造中断门
-
2.Fs、Gs寄存器
X86:0环时FS指向KPCR,3环时FS指向TEB
X64:0环时GS指向KPCR,3环时GS指向TEB
FS和GS的基址在3个MSR寄存器中:
IA32_FS_BASE (下标0xC0000100)
IA32_GS_BASE (下标0xC0000101)
IA32_KERNEL_GS_BASE (下标0xC0000102)
-
3.简单分析3环进0环过程
(1) 分析int 3中断
windbg输入指令: !idt
可看到3号中断函数是KiBreakpointTrap,这是因为笔者的测试机是未开启KPTI的.
而在开启KPTI时:3号中断函数调用的是KiBreakpointTrapShadow
-
用IDA对内核文件ntoskrnl.exe的KiBreakpointTrapShadow分析
由IDA分析可知,KiBreakpointTrapShadow 只是做了一些调用0环函数前的准备行为,如切换内核Cr3,切换内核rsp,切换内核gs等, 最后跳到了KiBreakpointTrap真正执行中断函数。
而未开启KPTI时,中断函数并不是KiBreakpointTrapShadow而是KiBreakpointTrap,而KiBreakpointTrap中并没有这些进内核的准备行为,笔者猜测进KiBreakpointTrap前肯定还执行过类似的代码, 目前还未查到
后面本人通过构建中断门进行测试发现,进去中断函数时rsp已经切换为0环的rsp, _KTrap_Frame的那5个寄存器也在栈中, cr3本身就是内核cr3 ,不需要改, 但是gs还没修改, 所以我们的构建中断门函数代码里边只用修改gs就可以调用内核函数了
-
二、代码实现
1.配置项目
1.创建vs空项目, 笔者项目名为IntGate
2.创建main.cpp文件和x64asm.asm文件(asm文件随便创建一个文件后, 将后缀为.asm就行)
-
3.右键项目->生成依赖性->生成自定义, 第四个打上勾
-
4.右键x64asm.asm,按如下图选择
5、打开项目属性设置固定基址, 并关闭增量链接
2.代码实现
代码如下(部分硬编码的地方需要更改):
main.cpp
#include <Windows.h>
#include <stdio.h>
extern "C" void InterruptEntry();
extern "C" void CaseInterrupt();
int main()
{
printf("InterruptEntry Addr:%p\n", (ULONG64)InterruptEntry);
// 笔者InterruptEntry为0x0000000140001200
if ((ULONG64)InterruptEntry != 0x0000000140001200)
{
printf("InterruptEntry地址与中断门构建的地址不一样\n");
system("pause");
return 0;
}
system("pause");
CaseInterrupt();
system("pause");
return 0;
}
x64asm.asm
option casemap:none
.data
pStr db 'I am the DbgPrint func', 0DH, 0AH
.code
InterruptEntry proc
;切换gs
swapgs
;调用DbgPrint函数. 为方便直接用windbg查了之后将函数地址写成硬编码了
mov rax,0fffff8047e7647b0h
sub rsp,20h
lea rcx,[pStr]
call rax
add rsp,20h
; 恢复gs
swapgs
iretq
InterruptEntry endp
CaseInterrupt proc
int 0fh ;f号中断系统未使用
ret
CaseInterrupt endp
end
windbg配置
1.构建中断门
笔者InterruptEntry地址为0x0000000140001200,中断门属性就填3号中断的属性,地址填0x0000000140001200
windbg:
eq idtr+f0 4000ee00`00101200
eq idtr+f8 00000000`00000001
!idt f
-
2.设置Cr4寄存器中SMEP和SMAP, 把3去掉就行
运行截图
参考资料
-
bilibili博主_周壑:x64内核研究
-
Intel 第3卷 段页机制
-
如有错误, 还请大佬指正