2种切换到内核模式的方法
首先总结2种切换到内核模式方法的各自流程:
内存法(中断法):
(用户模式)WriteFile() -> ntdll!NtWriteFile() -> ntdll!KiIntSystemCall() -> int 2Eh -> 查找IDT的内存地址,偏移0x2E处 ->(内核模式)nt!KiSystemService()
-> nt!KiFastCallEntry() -> nt!NtWriteFile()
通过0x2E中断转移控制到内核模式后,系统服务分发/调度器为 nt!KiFastCallEntry(),它负责调用内核空间中的同名异前缀函数 nt!NtWriteFile(),后者有一个系统服务号;也叫做分发 ID,该 ID 需要在执行 int 2Eh 前,加载到EAX 寄存器,以便通知 nt!KiSystemService()要它分发的系统调用(本机API),但是最终还是经由 nt!KiFastCallEntry() 来分发
MSR寄存器法(快速法):
(用户模式)WriteFile() -> ntdll!NtWriteFile() -> ntdll!KiFastSystemCall() -> 分别设置 IA32_SYSENTER_CS 寄存器的值为 Ring0 权限代码段描述符对应的段选择符;设置 IA32_SYSENTER_ESP 寄存器的值为 Ring0 权限的内核模式栈地址;设置 IA32_SYSENTER_EIP 寄存器指向 nt!KiFastCallEntry() 的起始地址 ->
SYSENTER ->(内核模式)nt!KiFastCallEntry() -> nt!NtWriteFile()
通过 SYSENTER 转移控制到内核模式后,系统服务分发/调度器为 nt!KiFastCallEntry() ,它负责调用内核空间中的同名异前缀函数 nt!NtWriteFile()
SYSENTER指令隐含了6步操作:
1.从 IA32_SYSENTER_CS 取出段选择符加载到 CS 中。
2.从 IA32_SYSENTER_EIP 取出指令指针放到 EIP 中
3.将 IA32_SYSENTER_CS 的值加上8,将其结果加载到 SS 中。(也就是将Ring0权限代码段选择符+8,来计算 Ring0 权限的内核模式堆栈段地址对应的段描述符)
4.从 IA32_SYSENTER_ESP 取出堆栈指针放到 ESP 寄存器中
5. 从 EIP 指向的地址处取指令,从而真正进入内核模式
6.若 EFLAGS 中 VM 标志已被置,则清除 VM 标志。
寄存器法看似比内存法多了很多步骤,尤其是 SYSENTER 指令的前置准备工作与隐含的内部操作,但是所有这些加起来,与访问内存中的 IDT 并取回数据相比,仍然快了数十至数百个处理器时钟周期。另外,中断法在进入内核模式后还要多一次对 nt!KiSystemService() 的调用,因此增加了性能开销。
ntdll!Nt* 为 nt!Nt* 系统调用的用户模式代理,前者在其中一个叫做SytemCallStub 的变量中保存 ntdll!KiFastSystemCall() 的地址(后面会验证);
ntdll!KiFastSystemCall() 中的 SYSENTER 指令负责实际从Ring3 到 Ring0 的转移,即进入内核模式。
在 Intel Pentium II 或 Windows XP 以前,系统调用只能通过 INT 2Eh 中断切换到内核模式,并且 nt!KiSystemService() 作为实际的系统服务分发/调度器。
在这之后,无论使用 INT 2Eh 或 SYSENTER,实际的系统服务分发/调度器都是 nt!KiFastCallEntry(),如前所述,这就没有必要使用 INT 2Eh 来多执行一次nt!KiSystemService()。
下面结合用户模式调试与内核模式调试来验证上述内容,首先用 WinDbg 打开 calc.exe (Windows 计算器)或其它任意可执行 PE 文件,在底部的命令行输入
u ntdll!KiIntSystemCall,,反汇编这个函数,可以看到其 77c071c4 地址处的2字节机器指令序列,int 2Eh :
在WinDbg菜单中选择停止调试,然后退出程序,再次用 LiveKD.exe打开 WinDbg,这将直接调试内核,执行 !idt 2e 命令,获取处理int 2Eh 的 ISR,可以看到,这个8字节的门描述符最终指向的就是 nt!KiSystemService() 的地址 842447fe;注意,线性地址7FFFFFFF是用户与内核空间的分水岭,往上80000000属于内核空间:
执行 u 842447fe L25 命令,反汇编nt!KiSystemService() 的前25行,发现其最终跳转到了nt!KiFastCallEntry+0x8f 偏移处(8424495f地址处):
使用KD.EXE 也可以验证:
温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/65956.html