API函数的调用过程(ring3)
Windows API
-
Application Programing Interface (应用程序接口) 简称API函数
-
Windows 有多少个API?
主要是存放在C:\WINDOWS\system32下面所有的dll
-
几个重要的dll
Kernel32.dll:最核心的功能模块,比如管理内存,进程和线程相关的函数等.
User32.dll:是Windows用户界面相关的应用程序接口,如创建窗口和发送消息等.
GDI32.dll全称是Graphical Device Interface(图形设备接口),包含用于画图和显示
Ntdll.dll:大多数API都会通过这个dll进入内核(0环)
绝大部分核心功能是在0环实现的,3环只是使用了0环提供给他的一个接口.
Kernel32.dll ->ReadProcessMemory
在C:\WINDOWS\system32\目录下找到kernel32.dll模块,放到IDA中 查看ReadProcessMemory的代码
我们看到在用户层的核心功能模块中的ReadProcessMemory函数再将参数压栈后,又调用了另一个模块的函数NtReadVirtualMemory
ReadProcessMemory:
ring3所做的事情:
接下来看看NtReadVirtualMemory函数 (ring 0)所做的事情
这时候再打开另一个ntdll.dll模块 查看其中的NtReadVirtualMemory函数:
这个函数传进去一个调用号,以及一个地址,再去call这个地址中的值,这个值就是存在于内核的一个函数.
//这个函数的功能:
提供编号
找到函数
通过函数进入ring 0
也就是说ntdll也只是做了个引荐人的功能,引荐进ring 0
真正对内存读写是在ring 0实现的
那就再继续跟跟看7FFE0300处的值是什么?
7c92e4f0
再继续跟7c92e4f0:
这就跟到了sysenter 汇编指令:
该汇编指令的作用是?:
通过寄存器,切换EIP,ESP
1.将IA32_SYSENTER_CS 和IA32_SYSENTER_EIP分别装到cs和eip寄存器中
2.将IA32_SYSENTER_CS+8 和IA32_SYSENTER_ESP 分别装载到ss和esp寄存器中,切换特权级0;
之后程序在ring 0环境下跳转到指定eip去执行,执行结束后使用sysexit 汇编指令返回,这条指令功能与sysenter相反:
1.将IA32_SYSENTER_CS+16装载到cs寄存器,将edx寄存器中的指令的指针装载到eip;
2.将IA32_SYSENTER_CS+24装载到ss寄存器中;
3.cx寄存器中的指针装载到esp中,切换到特权3
IA32_SYSENTER_CS之类的属于MSR寄存器
KiFastSystemCall,KiIntSystemCall
操作系统会检测CPU支不支持快速调用,如果支持,就会调用KiFastSystemCall 进ring 0,如果CPU比较老旧不支持快速调用 就会调用 KiIntSystemCall去进入ring 0
在ntdll.dll模块中找到该函数
发现他的本质其实还是调用了int 2e 号中断进入的ring 0
我们现在尝试在ntoskrnl.exe模块中查看一下int 2e是怎么进ring 0的?
SEP位
那CPU到底是怎么验证支不支持KiFastSystemCall的呢?
CPUID 指令
//该指令需要传入一个参数
//当eax = 1 来执行cpuid指令时,处理器的特征信息被放在ecx,和edx寄存器中,其中edx包含了一个SEP位(11位),该位指明了当前处理器是否支持
当SEP位 == 1 时,说明是支持的
1 1 1 1 1 0 0 0 1 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1
15 11 0
这两种的区别
KiIntSystemCall:
这个主要是使用INT 2E 中断门去做,中断门中CS,EIP都已经在IDT表中存储好了,SS与ESP需要TSS提供,所以使用一次int 2e 会读取IDT中的CS,和查找TSS
KiFastSystemCall:
sysenter : 系统会提前将CS/SS/ESP/EIP的值存储在MSR寄存器中,sysenter指令执行时,会将MSR寄存器值直接写入相关寄存器,直接就是寄存器的读,没有读内存的过程,这是快速调用的本质。
_KUSER_SHARED_DATA
-
这个结构体是在User层和Kernel层分别定义了一个_KUSER_SHARED_DATA结构体 用于User和Kernel层共享某些数据
-
他们使用固定的地址值映射,_KUSER_SHARED_DATA结构区域在User和Kernel层地址分别为:
User 层地址为:0x7ffe0000
Kernel层地址为:0xffdf0000
注意:虽然指向同一个物理页,但在User层是只读的,在Kernel层是可读写的
回到上面的内容:
也就是说,call这个地址里的内容,我们并不能武断的就说这里面一定是什么,CPU新旧不同,可能会导致调用的函数是不同的.有可能是KiFastSystemCall,也有可能是KiIntSystemCall
附件:
MSR |
地址 |
IA32_SYSENTER_CS |
174H |
IA32_SYSENTER_ESP |
175H |
IA32_SYSENTER_EIP |
176H |
SS = CS + 8
可以通过RDMSR/WRMST 来进行读写(操作系统使用WRMST写该寄存器)
内核模块:
ntoskrnl.exe(CPU以10-10-12分页模式启动)
ntkrnlpa.exe(CPU以2-9-9-12分页模式启动)