内核-为什么微软在windows2000以后用sysenter替换int 2e?

内核-为什么微软在windows2000以后用sysenter替换int 2e?

瑾兮 发布于 2017-01-29 字数 110 浏览 1319 回复 2

在Windows2000中,从用户态进入内核时微软用的INT 2e中断,但是到了xp以后都改成了sysenter指令,这是为什么?

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

想挽留 2017-10-18 2 楼

这个问题提得太好了,说说我的理解。
Windows2000使用INT 2e进中断,而XP使用sysenter指令进中断,主要是为了快速进入内核,调用内核函数。sysenter和sysexit是一对指令,又叫“快速系统调用”。
INT 2e中的INT是中断指令,而2e是中断号。操作系统中有一个中断向量表(IDT),保存则中断和异常向量。
操作系统有一个中断机制,是为了解放CPU。早期的IO操作,是靠CPU的轮询机制完成的,与IO设备之间的数据传输,每次传送间隙都是由CPU发送“开始发送”和“结束发送”命令完成的。这样使CPU的任务繁重,且关键是CPU与IO设备速度不匹配,CPU会等待慢速的IO传送完成,造成CPU资源浪费。后来出现了中断机制,即CPU发送开始传输命令后,就可以去忙其它的活了,待IO数据传送完毕,给CPU发送“传输完成”命令即可。但中断机制任然满足不了当今的大量计算、海量存储的需求,为了解决问题,出现了IO通道的设计。IO通道对每个IO设备(硬盘、鼠标键盘、游戏杆、网卡)增设一个类似于CPU功能的芯片,分担CPU繁重的任务。CPU只需与IO通道发送接收很少的数据即可完成IO任务。
而Linux从XX版本的内核开始使用中断改进以前靠CPU调度的机制,也就是epoll的实现。

回到中断向量表(IDT),中断又分为软中断和硬中断,即可有硬件引发也可由软件指令引发(也就是INT指令)。
操作系统提供很多API给应用程序调用,而很多Ring3(应用层)的API是通过调用内核的API完成的。而应用层的堆栈和系统层的是不一样的(也就是SS、ESP),而且执行入口也不一样(也就是CS、IP)。所以要通知CPU切换寄存器。这里就用中断机制实现了。

win2000的INT 2e方式是通过压入应用层堆栈(SS、ESP)、应用层返回地址(CS、IP),而后从TSS(任务状态段)取内核层的堆栈(内核API入口)设置为当前堆栈。内核层的API函数入口地址,首先从IDT从获得2E中断入口(对应KiSystemSerivce函数)并然后执行,入口会根据传入的应用层API编号(靠EAX传递)以及堆栈位置(EDX传递)调用SSDT表中的相应函数。而如果应用层需有界面则会根据API编号调用SSDT shadow表中的响应函数。调用完毕,从内核态返回应用态靠iret指令返回,然后装入TSS保存的应用层堆栈中的SS、ESP、CS、IP。
以上过程是否很繁琐且费时,操作系统有那么多API调用,不累死它才怪呢。

所以winXP以后使用sysenter来替代INT 2e的调用,但同时也兼容INT 2e。sysenter会把预设好的SYSENTER_CS_MSR、SYSENTER_EIP_MSR、SYSENTER_SS_MSR、SYSENTER_ESP_MSR四个寄存器内容传递内核态的API入口、堆栈给CS、EIP、SS、ESP。这样不像通过INT指令进入熊空间那样执行那么多操作,效率将提高很多。而执行sysexit指令回到用户态也是借助以上四个寄存器完成的,只不过偏移不相同。
sysenter实际对应KiFastSystemCall,sysexit对应KiFastSystemCallRet。KiFastSystemCall和KiFastSystemCallRet是通过PspLookupKernelUserEntryPoints()函数获取的。

清晨说ぺ晚安 2017-04-29 1 楼

转一下,别人的文章:)

INT 0x2E在系统调用的时候,需要进行栈切换的工作。由于Interrupt/Exception Handler的调用都是通过 call/trap/task这一类的gate来实现的,这种方式会进行栈切换,并且系统栈的地址等信息由TSS提供。这种方式可能会引起多次内存访问 (来获取这些切换信息),因此,从PentiumII开始,IA-32引入了新指令:SYSENTER/SYSEXIT。 有了这两条指令,
从用户级到特权级的堆栈以及指令指针的转换,可以通过这一条指令来实现,并且,需要切换到的新堆栈的地址,以及相应过程的第一条指令的位置

2K上,所有的调用都是通过int 2e来实现的,Int2e的服务处理例程指向的是KiSystemSerivce,因此挂钩KiSystemSystem就可以获得所有的调用
而XP上,int2e指向的还是KiSystemSerivce,但SysEntry指向的则是KiFastCallEntry函数

由于shadow ssdt的调用都是通过int 2e实现的,因此拦截shadow ssdt调用的话拦KiSystemService即可,但正常的SSDT调用都是用sysentry实现的,因此要HOOK KiFastCallEntry才可以~