1 Intel x86的用户模式-内核模式切换
Intel x86 提供了“门(gate)”机制允许可执行代码从R3 进入R0 模式,以及从R0返回R3模式。Intel x86 支持调用门(call gate)、陷阱门(trap gate)、中断门(interrupt gate)和任务门(task gate)四种门描述符。Windows 使用中断门来实现模式切换,其中断号为0x2e。意思就是,当应用程序在用户模式下运行时,它通过”int 2e”指令,切换到内核中,IDT(中断描述符表)的0x2e表项指定了内核系统服务的入口点;当内核模式代码完成了指定的系统服务时,它通过”iret”或“iretd”指令,返回到用户模式代码。
Int 0x2e 指令跳转流程,如图:
Intel x86 处理器在IDT中查找2e表项,IDTEntry包含了一个段选择符和中断例程的段内偏移,所以,处理器还需要再GDT中再查找一次表项,得到段选择符指定的虚拟基地址。段基地址加上中断例程偏移,最终得到中断例程的虚拟地址。
在Windows中,IDT的2e 表项中的段选择符为0x0008,中断偏移例程偏移为_KiSystemService的例程地址。
用户模式有用户栈,内核模式有内核栈。“int 2e”指令从用户模式切换到内核模式时,必须切换到内核栈。通过软件中断门进入内核模式时发生的栈切换,如图:
处理器在将控制权交给目标例程前,首先将原来的ss、esp、eflags、cs 和eip 寄存器的值存到内核栈中,并且让新的esp 指向内核栈顶位置。
处理器在用户模式下执行,是如何知道内核栈的位置,即内核栈的ss 和 esp ?
答案在于处理器的当前任务环境。
每个处理器都总是在一个任务环境中运行,处理器的任务寄存器(task register)指向当前任务环境的TSS(Task-State Segment),其中包含了每一特权级使用的栈,其中Ring0 的esp 位于TSS+4 位置。
当内核模式下的中断例程完成了中断处理之后,它通过iret/iretd 指令返回到用户模式代码。
Iret/iretd指令从内核栈中弹出eip、cs 、eflags、esp 和ss,然后将控制权交给eip所指的用户模式代码。
快速系统调用则通过指令”sysenter”实现。CPU中增加了三个MSR(Model Specific Register)寄存器:SYSENTER_CS_MSR、SYSENTER_EIP_MSR、SYSENTER_ESP_MSR。操作系统可以在内核模式下通过rdmsr/wrmsr 指令来设置这三个寄存器。
Sysenter 指令内部逻辑:将IA32_SYSENTER_CS 和IA32_SYSENTER_EIP 分别装载到cs和eip中;将IA32_SYSENTER_CS+8和IA32_SYSENTER_ESP 分别装载到ss和esp中;切换到特权级0;清除eflags 的VM标志;执行目标例程。
Sysexit指令内部逻辑:将IA32——SYSENTER_CS+16装载到cs;将edx的指针装载到eip中;将IA32_SYSENTER_CS+24装载到ss中;将ecx 中的指针装载到esp中;切换到特权级3;执行eip中指定的用户模式代码。
利用sysenter/sysexit 实现模式切换和栈切换,如图: