今天看了下windows内核情景分析中的系统调用这章,对于系统调用的一个大概过程有了一定的了解!
就拿书上的ReadFile这个API来说,首先这个API是在用户空间的,所以能够直接调用,然后在这个函数的内部会调用NtReadFile这个中介函数(这个在ntdll.dll中也是用户空间),当然内核中也有一个同名的函数,ntdll.dll中的这个NtReadFile函数在内部实现如下:
NtReadFile(int dummy0, int dummy1, int dummy2)
{
__asm
{
mov eax, 152
mov ecx, KUSER_SHARD_SYSCALL
call [ecx]
ret 9
}
}这个KUSER_SHARD_SYSCALL是用户空间的一个地址,这个地址存放着一个函数指针,指向KiIntsystemcall()或KiFastSystemCall();
这两个函数的实现如下
lea edx,[esp+8] //edx指向函数调用的第一个参数
int 0x2e
ret
mov edx,esp
sysenter
ret
通过int 0x2e 这个中断,cpu进入了系统态,然后一些寄存器会被压到系统堆栈中如ss esp cs EFLAGS 等,然后系统通过中断好从中断向量表中找到中断处理函数的入口
这个入口就是KISystemService(),实现如下:
_KiSystemService:
SYSCAL_PROLOG kss_a,kss_t
jmp SharedCode
SYSCAL_PROLOG kss_a,kss_t
这个是一个宏,这个宏的作用主要是保存一些之前的数据,然后设置为新的内容,最主要的就是设置新的堆栈框架,做完这些之后就跳到SharedCode,这个标签下的代码的作用是判断系统调用号的大小,根据系统调用号的大小来调用基本的系统调用或扩充
的系统调用Win32k,以及检查系统调用号是否越界,然后将用户空间栈中的函数参数复制到系统空间栈中,再通过系统调用号找到系统调用表中的的函数,得到函数的地址,最后才进入真正的系统函数中!