以下参考水滴教程,做出的笔记
Windows API主要存放在C:\WINDOWS\system32下
内核模块为C:\WINDOWS\system32下的ntoskrnl.exe(10-10-12分页)和ntkrnlpa.exe(2-9-9-12分页)
Kennel32.dll:最核心功能模块,如管理内存、进程和线程相关的函数
User32.dll:Windows用户界面相关应用程序接口,如创建窗口和发送消息等
GDI32.dll:全称Graphical Device Interface(图形设备接口)包含用于画图和显示文本的函数,比如要显示一个程序窗口,就调用其中的函数来画该窗口。
Ntdll.dll:大多数API都会通过这个DLL进入内核(0环)
前导
探究一下API函数如何进入内核(0环),以Kernel32.dll中函数ReadProcessMemory查看,如下:
可以发现在7C8021E5处调用NtReadVirtualMemory函数,其来自于Ntdll.dll,打开Ntdll.dll查看,如下:
可以看到.text:7C92D9E0处,向eax传入0BAh,这里传入的是内核函数编号,.text7C92D9E5处向edx传入一个线性地址7FFE0300h,并调用该线性地址中的内容,可以看出其调用了edx处的函数。
_KUSER_SHARED_DATA
在探究7FFE0300h地址时,需要提前了解一个数据结构,即_KUSER_SHARED_DATA。其主要作用如下:
1)在User层和KerNel层分别定义了一个_KUSER_SHARED_DATA结构区域,用于User层和Kernel层共享某些数据。
2)它们使用同一段页,只是映射位置不同。虽然同一页,但User只读,Kernnel层可写。
3)它们使用固定的地址值映射,_KUSER_SHARED_DATA结构在User为:0x7ffe0000,在Kernel层为:0xffdf0000。
注意: 0x7ffe0000与0xffdf0000线性地址指向同一个物理页,但是在User层只读,Kernel层可读可写。
使用windbg查看_KUSER_SHARED_DATA的结构体,如下
可以看到0x7FFE0300h处,是SystemCall。
SystemCall
SystemCall处存储的是进入内核(0环)的函数,其分两种情况:
(1)若当前CPU支持sysenter/sysexit指令,0x7FFE0300处(SystemCall)存储的是ntdll.dll!KiFastSystemCall() 函数
(2)若当前CPU不支持sysenter/sysexit指令,0x7FFE0300处(SystemCall)存储的是ntdll.dll!KiIntSystemCall() 函数
KiIntSystemCall()查看如下:可以看到KiIntSystemCall()使用中断门(0x2E)进入0环。而在KiFastSystemCall()通过systementer进入0环。
快速调用
3环进入0环,需要改变CS、EIP、SS、ESP寄存器的值,利用中断门进入0环时,需要的CS、EIP存在IDT表中,SS、ESP存在TSS中,故需要查找内存得到SS、ESP,而快速调用(systementer,使用KiFastSystemCall)在使用systementer进入0环时,操作系统提前将CS、EIP、SS、ESP存在MSR寄存器中,没有读取内存的过程(实际上从MSR得到的是一个临时的0环栈,代码执行后仍要从TSS.ESP0得到当前线程0环堆栈),过称为快速调用。
利用中断门进入0环
利用中断门进入0环步骤
(1)在IDT表中查找0x2E处的中段描述符(中断门)
(2)从中段描述符中获取CS、EIP,其中SS、ESP通过查内存TSS得到
(3)执行EIP处程序:nt!KiSystemService
现在分析该流程,查看0x2E处中段描述符,如下
利用systementer进入0环
MSR寄存器中存储了CS、EIP、ESP值, MSR寄存器如下
MSR | 地址 |
---|---|
IA32_SYSENTER_CS | 174h |
IA32_SYSENTER_ESP | 175h |
IA32_SYSENTER_EIP | 176h |
利用systementer进入0环时的流程:
(1)CPU读取MSR中CS、EIP、ESP值,计算SS=CS+8(这是约定,别管)
(2)执行EIP处指令:nt!KiFastCallEntry