首页
论坛
课程
招聘
[原创]Windows系统调用分析
2017-11-29 15:37 9470

[原创]Windows系统调用分析

2017-11-29 15:37
9470

前言:

近期看书学习系统调用的一些心得,分享给大家,如有错漏,希望大家多多提点。
下面主要是从Intel下Windows系统调用执行流程角度分析。
下面流程图左边的尖角正方形表示正常执行流程和相关的关键信息,右边的圆角正方形表示对应的部分伪代码和注释。
下面是以Win8 RTM为例子,注意,不同系统下的实现可能会有所不同。


系统调用主要分为三种形式:
  • int 2eh实现
  • x86上sysenter实现
  • x64上syscall实现

int 2eh

奔腾2之前的处理器上,Windows使用中断0x2e实现系统调用。


如图int2eh可知:

1.从KernelBase!CreateFileW开始,函数内部调用call Ntdll!NtCreateFile。

2.来到Ntdll!NtCreateFile,按照惯例,把系统调用号保存到eax,以便之后根据系统调用号索引SSDT上对应的系统调用例程。

值得注意的是,在所有的体系结构中,都有一个称为KUSER_SHARED_DATA的结构体,它属于进程,并且总是映射到0x7ffe0000,

对应的SystemCall字段偏移是0x300(位置会根据不同平台而有所不同)。

KUSER_SHARED_DATA.SystemCall对应的值就是ntdll!KiInitSystemCall的地址。

3.进入KiIntSystemCall,内部调用int 2eh调用IDT上对应的中断处理例程。

其中,IDT上0x2e的项为nt!KiSystemService。

4.走进nt!KiSystemService。它就是系统调用分发函数。

内部就会根据系统调用号索引SSDT上对应的系统调用例程,实现相应的功能。最后返回用户领空。


x86

Intel x86下Windows通过sysenter实现系统调用。

如图trapx86所示:

1.首先从ntdll!NtQueryInformationProcess开始,首先保存系统调用号,然后调用nt!KiFastSystemCall。

2.来到nt!KiFastSystemCall,首先保存esp,因为后面执行sysenter指令时,rsp/esp会被覆盖,最后执行sysenter。

此处补上sysenter/sysexit指令的简单说明,详情请参考intel白皮书。

sysenter和sysexit是一对配套指令,是快速在R3和R0之间转换的指令。

sysenter
{
合法性检测
RFLAGS的VM和IF复位
rsp/esp = IA32_SYSENTER_ESP/IA32_SYSENTER_ESP[31:0]
rip/eip = IA32_SYSENTER_EIP/IA32_SYSENTER_EIP[31:0]
CS.Selector = IA32_SYSENTER_CS[15:0] AND 0FFFCh
修正CS,SS,CPL
}

sysexit
{
合法性检测
rsp = rcx/ecx
rip = rdx/edx
CS.Selector = IA32_SYSENTER_CS[15:0] + 32/IA32_SYSENTER_CS[15:0] + 16
修正CS,SS,CPL
}
因为Windows会在处理器初始化进程时,使MSR寄存器IA32_SYSENTER_EIP = nt!KiFastCallEntry。一次sysenter执行后,执行位置会跳转到nt!KiFastCallEntry。 
3.进入nt!KiFastCallEntry,我们简略部分细节说一下重点。 KiFastCallEntry是x86 windows的系统调用主分发器(对应x64 KiSystemCall64)。首先调用SSDT[系统索引号]对应的系统服务例程,然后恢复调用sysexit。根据sysexit的机制,下面会跳转到nt!KiFastSystemCallRet。
4.走进nt!KiFastSystemCallRet,省略掉部分细节,我们来到内部的ret指令。
此时的堆栈应该是与ntdll!NtQueryInformationProcess中调用call nt!FastSystemCall时的堆栈相同的。因此nt!KiFastSystemCallRet中的ret指令会返回到
ntdll!NtQueryInformationProcess中的retn 14h处。最后返回到用户领空。

x64

Intel x64下Windows通过syscall实现系统调用。

如图trapx64可知:


1.从ntdll!NtCreateFile开始,首先保存ecx,保存系统调用号,然后调用syscall。值得注意的是,Windows在处理器初始化进程(参见KiInitializeBootStructures)中把MSR寄存器IA32_LSTAR设置为nt!KiSystemCall64。

下面补上syscall/sysret指令的简单说明,详情请参考intel白皮书。

与sysenter/sysexit类似,syscall和sysret是一对配套指令, 是快速在R3和R0之间转换的指令。

syscall
{
合法性检测
rcx = rip
rip = IA32_LSTAR
r11 = RFLAGS
RFLAGS = RFLAGS AND NOT(IA32_FMASK)
CS.Selector = IA32_STAR[47:32] AND 0FFFCh
修正CS,SS,CPL 
}

sysret
{
合法性检测
rip = ecx/ecx
根据R11的值对RFLAGS赋值,且RF,VM,部分reserved位复位。
CS.Selector = IA32_STAR[63:48] + 16/IA32_STAR[63:48]
修正CS,SS,CPL
}
2.来到nt!KiSystemCall64。KiSystemCall64是x64 windows中的主系统调用分发函数,其主要责任是保存用户模式上下文,建立内核栈,复制用户模式参数到内核栈,通过eax传过来的索引值确定KiServiceTable(或W32pServiceTable)中的系统调用,最后返回用户模式。







[招生]科锐逆向工程师培训46期预科班将于 2023年02月09日 正式开班

收藏
点赞1
打赏
分享
最新回复 (5)
雪    币: 329
活跃值: 活跃值 (1081)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
niuzuoquan 活跃值 2017-11-29 17:50
2
0
mark
雪    币: 8
活跃值: 活跃值 (65)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
yber 活跃值 2017-12-31 08:14
3
0
mark
雪    币: 346
活跃值: 活跃值 (496)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
编程小白 活跃值 2018-1-17 15:06
4
0
mark
雪    币: 1202
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
杨小妞 活跃值 2018-9-4 16:27
5
0
mark
雪    币: 1000
活跃值: 活跃值 (27)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ibyzero 活跃值 2020-4-25 22:50
6
0
实际上, KiServiceTable, 即ssdt, 在x64下并不是系统调用处理例程的地址, 而是一个 uint32_t 值, 是系统调用处理例程相对KiServiceTable开始地址的一个偏移?
游客
登录 | 注册 方可回帖
返回