一、回顾
上次课我们学习了3环进0环的两种方式,分别是中断门和快速调用,如果CPU支持快速调用,那么_KUSER_SHARED_DATA 结构体的 SystemCall 属性指向的函数是 KiFastSystemCall;如果不支持,那么SystemCall 指向的函数是KiIntSystemCall。
执行 KiFastSystemCall,则使用快速调用的方式进0环;
执行 KiIntSystemCall,则使用中断门的方式进0环。
共同点是都要给0环提供新的CS,SS,EIP,ESP,只是提供的方式不同。中断门的CS,EIP从IDT表里获取,SS,ESP从TSS表里获取;快速调用的CS,EIP,ESP从MSR寄存器获取,SS是CS+8计算得到,至于MSR寄存器里的值,则是系统启动时就已经填好。
所以快速调用比中断门快就快在,快速调用不需要访问内存,而中断门需要读TSS和IDT表。
这节课,我们来详细分析 INT 2E 和 sysenter 做了哪些工作。
二、分析 INT 0x2E
KiIntSystemCall 触发了 2E 中断。用windbg看看2E号中断的中断门描述符,2E 右移3位(或者乘8)得到 0x170,加上 r idtr
获取的IDT基址,可以计算出 2E号中断对应的描述符:8053ee00`0008e481
kd> r idtr
idtr=8003f400
kd> dq 8003f400+170
8003f570 8053ee00`0008e481 80548e00`00081780
拆分中断门描述符,可以得到新的CS是0008(系统代码段),EIP是 8053e481,这个是内核模块的 KiSystemService 函数。
kd> u 8053e481
nt!KiSystemService:
8053e481 6a00 push 0
8053e483 55 push ebp
8053e484 53 push ebx
8053e485 56 push esi
8053e486 57 push edi
8053e487 0fa0 push fs
8053e489 bb30000000 mov ebx,30h
8053e48e 668ee3 mov fs,bx
接下来查看 TSS 表,首先看看tr寄存器的值,是0x28,然后看看TSS描述符:
kd> dq 8003f000
8003f000 00000000`00000000 00cf9b00`0000ffff
8003f010 00cf9300`0000ffff 00cffb00`0000ffff
8003f020 00cff300`0000ffff 80008b04`200020ab
TSS描述符是 80008b04`200020ab
所以TSS的地址就是 80042000,dd看一下:
kd> dd 80042000
80042000 0c458b24 8054acf0 8b080010 758b0855
所以ESP0 = 8054acf0, SS0 = 0010。
三、分析 sysenter
sysenter 是从 MSR 寄存器里读取 CS0,ESP0,EIP0,而SS0是通过CS0+8计算得来。我们可以用 rdmsr 指令在windbg里查看 MSR 寄存器,也可以用 wrmsr 修改MSR寄存器。
MSR | 地址 |
---|---|
IA32_SYSENTER_CS | 174H |
IA32_SYSENTER_ESP | 175H |
IA32_SYSENTER_EIP | 176H |
查看 CS,ESP,EIP
kd> rdmsr 174
msr[174] = 00000000`00000008
kd> rdmsr 175
msr[175] = 00000000`f8ac2000
kd> rdmsr 176
msr[176] = 00000000`8053e540
其中,EIP是 KiFastCallEntry 函数:
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
四、总结
API通过中断门进0环:
1) 固定中断号为0x2E
2) CS/EIP由门描述符提供 ESP/SS由TSS提供
3) 进入0环后执行的内核函数:NT!KiSystemService
API通过sysenter指令进0环:
1) CS/ESP/EIP由MSR寄存器提供(SS是算出来的)
2) 进入0环后执行的内核函数:NT!KiFastCallEntry
int 0x2e 和 sysenter 指令进0环后,分别调用了两个函数 KiSystemService 和 KiFastCallEntry。
下一篇博客,我将逆向分析 KiSystemService 和 KiFastCallEntry 这两个函数。