系统调用-KiFastCallEntry逆向分析
Sysenter
在旧的CPU架构中操作系统使用中断(Windows使用:int 0x2E)实现系统调用,现在的CPU都支持快速系统调用(Fast System Call)。
快速调用使用指令sysenter进0环,下面是《Intel白皮书》对sysenter执行过程的描述:
1. Loads the segment selector from the IA32_SYSENTER_CS into the CS register.
2. Loads the instruction pointer from the IA32_SYSENTER_EIP into the EIP register.
3. Adds 8 to the value in IA32_SYSENTER_CS and loads it into the SS register.
4. Loads the stack pointer from the IA32_SYSENTER_ESP into the ESP register.
5. Switches to privilege level 0.
6. Clears the VM flag in the EFLAGS register, if the flag is set.
7. Begins executing the selected system procedure.
简单总结一下就是:从多个MSR寄存器中获取CS、EIP、SS、ESP的值并填充,其中SS是通过公式:CS+8计算出来的。
在用WinDbg可以使用命令(Read MSR):rdmsr Address 查看MSR内容。
kd> rdmsr 174
msr[174] = 00000000`00000008
kd> rdmsr 175
msr[175] = 00000000`f8ac2000
kd> rdmsr 176
msr[176] = 00000000`8053e540
中断和sysenter都可以到到进入0环的目的,sysenter由于数据通过MSR寄存器读取而不是读取内存,所以执行效率上相对的速度要高。
但执行后结果会有一定的差别:堆栈的内容不同。通过中断的方式进入0环,3环的SS、ESP、EFLAGS、CS、EIP会把CPU压入到堆栈,但sysenter则并不会压入这几个寄存器的值。 这也是为什么Windows要分别给中断和快速调用准备0环入口。
找到KiFastCallEntry
通过上面获取到的MSR[IA32_SYSENTER_EIP]可以得知Windows快速调用的0环入口函数地址为:0x8053e540。
kd> u 8053e540
nt!KiFastCallEntry:
8053e540 b923000000 mov ecx,23h
8053e545 6a30 push 30h
8053e547 0fa1 pop fs
8053e549 8ed9 mov ds,cx
8053e54b 8ec1 mov es,cx
8053e54d 8b0d40f0dfff mov ecx,dword ptr ds:[0FFDFF040h]
8053e553 8b6104 mov esp,dword ptr [ecx+4]
8053e556 6a23 push 23h
切换寄存器
一进入KiFastCallEntry会立即修改fs/ds/es/esp这几个寄存器.
比较有意思的是
- fs使用的是立即数0x30,
- ds/es则是立即数0x23.
- esp则直接切换成了TSS.Esp0.
保存现场
int2e会自动把ss/esp/eflags/cs/eip这5个寄存器压入堆栈, 但快速调用不会, 所以要手动push这5个寄存器到堆栈.
值得注意的是:KTRAP_FRAME.Eip被设置成了SystemCallReturn的地址.
最后
最后的代码和KiSystemService非常相似. 在这段代码执行完毕后, 会直接跳到SharedCode.