2.1内核与系统调用
上节讲到进入内核五种方式 其中一种就是 系统调用 syscall/sysenter或者 int 2e( 在 64 位环境里统一使用 syscall/sysret 指令,在 32 位环境里统一使用 sysenter/sysexit 在 compatibility 模式下必须切换到 64 位模式,然后使用 syscall/sysret 指令 注释 :32位 cpu 是 x86 模式 也叫 legacy 模式 再说清楚点 就是包含了实模式 : 可以执行以前的 16 位程序 也包含了保护模式 : 可以执行 32 位的程序 64 位 cpu 是 long 模式 : 分为两种 64 位模式 : 只执行 64 位的程序和 compatibility 模式 :可以执行 x86 模式的程序 老式的 cpu不支持 不提供 sysenter 指令 , 只能由 int 2e 模拟中断方式进入内核 , 调用系统服务 )这两者什么区别呢 ?
1,Int 2e速度慢 首先从 TSS 中加载内核堆栈的 ss esp-> 保存 5 个寄存器的现场 (ss esp eip eflags cs)-> 然后还要去 IDT 中查找 isr, 这个过程消耗的时间太多
2,sysenter 提供了三个 MSR 寄存器 分别是 SYSENTER_CS_MSR SYSENTER_EIP_MSR SYSENTER_ESP_MSR 分别指示了内核对应处理例程的 cs 选择子 ( 函数所在段的选择子 通过选择子找到 GDT 从逻辑地址转换为线性地址 最后访问线性地址 会根据四级表转换为物理地址 ) 和内核函数地址和内核堆栈地址 这样就省去了查找 idt 表 TSS 段 获得内核函数地址和内核堆栈地址 节省了大量时间
++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++ ++以下是 X64 的 syscall 讲解 参考总结 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++
syscall 的内核入口点是 KiSystemCall64() ,系统在 KiInitializeBootStructures() 里对 syscall/sysret 执行环境进行了设置:
MSR_STAR 寄存器里的值被设为 23001000000000h ,它意味着:
SYSCALL_EIP 为 0
SYSCALL_CS 为 0x10
SYSRET_CS 为 0x23
在 SYSRET_CS 中, SYSRET_CS.RPL = 3 返回的权限级别是 3 级(用户代码)。
MSR_CSTAR 寄存器被设为 nt!KiSystemCall32 (fffff800`03cc4c00) 地址值,这是为了 compaitibility 模式代码调用而设置的。
MSR_LSTAR 寄存器被设为 nt!KiSystemCall64 (fffff800`03cc4ec0) 地址值,是为 64-bit 模式而准备的。 CSTAR 寄存器为 compatibility 模式下的代码提供 rip 值,当 processor 在 comatibility 模式下运行时,执行了 syscall 指令,此时 rip 值将从 MSR_STAR 寄存器中加载。 请记住:只能在 AMD 的 processor 使用 compaitibility 模式下的调用。照顾通用性,为了在 Intel 和 AMD 的 processor 上都能够使用 fast call 功能,操作系统的设计者应该要避免在 comaptibility 模式下使用 syscall 指令。前面提到过,建议在 compatibility 模式下先切换到 64-bit 模式后,再执行 syscall 指令
MSR_SFMASK 寄存器设为 4700h ,意味着:
NT DF IF TF
这些 rflags 寄存器中的标志位在进入 KiSystemCall64() 后会被清 0
在 long mode(win7 x64 ) 下,当执行 syscall 指令时,当前的 rflags 寄存器值被保存在 r11 寄存器, processor 在执行 syscall 时,准备的目标执行环境中, rflags 将会根据 SFMASK 寄存器的值进行设置:如果 SFMASK 寄存器的某一位置为 1 ,那么 rflags 寄存器中相应的位将会被清 0 ,置为 0 时, rflags 寄存器中相应位不变
它的逻辑 C 描述为:
rflags = rflags & (~sfmask);
你应该在系统服务例程先保存 r11 值(原来的 rflags 寄存器值),以便 sysret 执行返回时可以恢复原来的 rflags 值
那么,对于要进入系统调用的代码来说:
执行 syscall 后, rcx 会保存返回值,因此应该要保存 rcx 原来的值 。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++以下是 X86 的 sysenter 讲解 参考总结 +++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++ ++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++以下是 int 2e 讲解 参考总结 ++++++++++++++++++++++++++++++++++ +++++++++++++++++++ ++++++++++++++++
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
上传的附件: