x86 SYSENTER HOOK

来源:互联网 发布:手机淘宝查看卖家信誉 编辑:程序博客网 时间:2024/06/05 17:49

准备资料:

  自2000以后Windows系统不再用int 2e或通过IDT来请求系统调用表中的服务,而是使用快速调用方法fast call method.在这种方法中,NTDLL向EAX寄存器中加载被请求服务的系统调用号,向EDX寄存器中加载当前堆栈指针ESP。然后发出Intel指令SYSENTER。

  SYSENTER指令将控制权传递给模型相关寄存器(Model-Specific Register, MSR)IA32_SYSENTER_EIP中指定的地址。可以读写该寄存器,但它是一条特权指令,这意味着必须从R0级别上执行该指令。

 

为什么MS抛弃了int 2e?(本菜:这个答案妥当嘛?)

  Copy From http://www.dewen.org/q/302

  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()函数获取的。

 

  SYSENTER/SYSEXIT这对指令有一组特殊寄存器来实现,这类特殊寄存器在IA-32中称为MSR(Model Specific Register)。这里牵涉到3个特殊寄存器: 
  SYSENTER_CS_MSR: New code segment selector       0x174 
  SYSENTER_ESP_MSR: New Stack Pointer                  0x175 
  SYSENTER_EIP_MSR: New Instruction Pointer          0x176 
  这里标出的3个16进制数分别对应这3个寄存器的地址,该地址用于Kernel debug时,通过rdmsr/wrmsr指令来读/写这3个寄存器。步骤如下:
  名称:  10.JPG查看次数: 4344文件大小:  8.0 KB
  1. 装载SYSENTER_CS_MSR 到CS 寄存器,设置目标代码段
  2. 装载SYSENTER_EIP_MSR到 EIP寄存器,设置目标指令 
  3. SYSENTER_CS_MSR+8 装载到SS寄存器 ,设置栈段
  4. 装载SYSENTER_ESP_MSR 到ESP寄存器,设置栈帧 
  5. 切换RING0. 
  6. 清除 EFLAGS的 VM标志 
  7. 执行RING0例程 

  名称:  11.JPG查看次数: 4336文件大小:  7.0 KB
  1. SYSENTER_CS_MSR+16装载到 CS寄存器 
  2. 将EDX的值送入EIP 
  3. SYSENTER_CS_MSR+24 装载到SS寄存器 
  4. 将ECX的值送入ESP 
  5. 切换回RING3 
  6. 执行EIP处的RING3指令 

 

下面贴出www.rootkit.com的Demo代码:

  

 1 #include "ntddk.h" 2  3 ULONG d_origKiFastCallEntry; // Original value of ntoskrnl!KiFastCallEntry 4  5 VOID OnUnload( IN PDRIVER_OBJECT DriverObject ) 6 { 7     _asm 8     { 9         mov ecx, 0x17610         xor edx,edx11         mov eax, d_origKiFastCallEntry     // Hook function address12         wrmsr                        // Write to the IA32_SYSENTER_EIP register13     }14 }15 16 // Hook function17 __declspec(naked) MyKiFastCallEntry()18 {19     __asm 20     {21         jmp [d_origKiFastCallEntry]22     }23 }24 25 NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )26 {27     theDriverObject->DriverUnload  = OnUnload; 28 29     __asm 30     {31         mov ecx, 0x17632         rdmsr                 // read the value of the IA32_SYSENTER_EIP register33         mov d_origKiFastCallEntry, eax34         mov eax, MyKiFastCallEntry     // Hook function address35         wrmsr                        // Write to the IA32_SYSENTER_EIP register36     }37 38     return STATUS_SUCCESS;39 }

PS:rdmsr指令 读MSR

  RDMSR将64位由ECX寄存器指定的MSR(model specific register,模式指定寄存器)的内容读出至寄存器EDX:EAX中(在支持intel64架构的处理器中RCX的高32位忽略)。MSR的高32位内容存放在EDX寄存器中,MSR的低32位内容存放在EAX寄存器中(在支持intel64架构的处理器中RDX和RAX的高32位忽略)。如果MSR中没有64位(有些位没有实现),则EDX:EAX中没有实现的位置则未定义。

  该指令必须在0层权限或者实地址模式下执行;否则会触发#GP(0)异常。在ECX中指定一个保留的或者未实现的MSR地址也会引发异常。

MSR控制着可测试性、执行跟踪、性能检测和机器错误检查等功能。附录B列出所有能读写的MSR以及它们的地址。注意不同的处理器族有自己不同的MSR.


我们可以在使用本指令前用CPUID指令来检查是否支持MSR。(CPUID.01H:EDX[5]=1).

  

0 0
原创粉丝点击