快速系统调用

来源:互联网 发布:java 动态仪表盘 编辑:程序博客网 时间:2024/05/23 13:24

转载自:http://mzf2008.blog.163.com/blog/static/35599786201121011337777/


用户层:

首先Windows还是调用R3层的函数NtReadFile(),不过此函数相对以前直接用int 0x2e进入内核有了较大的变化.
我们来看一下,函数如下:
__declspec(naked)__stdcall
{
 __asm{
      mov eax,152 //eax中还是跟以前一样存放NtReadFile的系统调用号
      mov ecx,KUSER_SHARED_SYSCALL
      call [ecx]
      ret 9
 }
}

用OD下断NtReadFile()可以看到(操作系统,汇编器不一样,可能造成汇编结果不同,但是大致思路是一样的):

2011年03月10日 - Fly - 从C开始

上述代码中的KUSER_SHARED_SYSCALL实际上是一个用户空间的地址,实际上是0x7FFE0300,这里存储着一个函数指针,指向KiIntSystemCall()或KiFastSystemCall()两个函数之一.系统在初始化的时候,根据CPU是否支持快速系统调用而使该指针指向KiIntSystemCall()或KiFastSystemCall().

下面我们先来看下KiIntSystemCall():
_KiIntSystemCall@0:
 lea edx,[esp]+8
 int 0x2e
 ret
我们可以发现,这个函数和改变后的NtReadFile()代码合起来就是原来的NtReadFile()函数中的代码.

2011年03月10日 - Fly - 从C开始

 

我们再来看下KiFastSystemCall(),采用KiFastSystemCall()要和另一段代码KiFastSystemCallRet()相互配合使用:
_KiFastSystemCall@0:
 mov edx,esp
 sysenter  //进入内核

2011年03月10日 - Fly - 从C开始

 
_KiFastSystemCallRet@0:
 ret    //在内核中通过sysexit指令返回 

2011年03月10日 - Fly - 从C开始

 

内核层:

前面我们讲到,Intel在其奔腾2开始加入了3个MSR寄存器和两天新的指令,用来执行快速系统调用.
这三个寄存器分别是SYSTEM_CS_MSR,SYSTEM_EIP_MSR,SYSTEM_ESP_MSR,指令是sysenter和sysexit.


当一个支持快速系统调用的系统在启动的时候,内核在初始化的时候会调用如下这个函数:
ULONG_PTR NTAPI
KiLoadFastSyscallMachineSpecificRegisters(IN ULONG_PTR Context)
{
 Ke386Wrmsr(0x174, KGDT_R0_CODE, 0);
 Ke386Wrmsr(0x175, (ULONG)KeGutCurrentPrcb()->DpcStack, 0);
 Ke386Wrmsr(0x176, (ULONG)KiFastCallEntry, 0);
 return 0;
}

这里调用了Ke386Wrmsr()函数三次,分别对三个MSR寄存器进行初始化,Ke386Wrmsr()实际上就是特权指令wrmsr的包装,其第一个参数目标MSR寄存器的编号.因此,SYSTEM_CS_MSR的编号为0x174,它被设置成KGDT_R0_CODE既0x8,这是零环代码段的选择项.SYSTEM_ESP_MSR的编号为0x175,这里把它设置成PRCB结构中的指针DpcStack的内容.这个指针指向一个中立的,不属于任何一个线程的堆栈,这本来是供内核执行DPC函数时使用的,现在用来作为暂时的,过度的系统空间堆栈.最后是SYSTEM_EIP_MSR,其编号为0x176,将它设置成了KiFastCallEntry(),这就是系统空间中快速系统调用的总入口.

当CPU执行sysenter指令进入系统态时
1.把寄存器SYSTEM_CS_MSR的内容复制到段寄存器CS中.
2.把寄存器SYSTEM_EIP_MSR的内容复制到寄存器EIP中
3.把寄存器SYSTEM_CS_MSR+8的内容写入堆栈段寄存器SS
3.把寄存器SYSTEM_ESP_MSR的内容复制到堆栈指针ESP中
这样,CPU在进入内核之后,就会执行EIP中的指令,既进入KiFastCallEntry()中.

下面我们来看下KiFastCallEntry()函数:
_KiFastCallEntry:
 FASTCALL_PROLOG FastCallDrSave, FastCallDrReturn
SharedCode:
 ....

FASTCALL_PROLOG是个宏,主要作用是模仿int 0x2e进入内核时的系统调用框架
SharedCode则和前面int 0x2e一样,主要是通过系统调用号调用具体的Nt函数.