成都创新互联网站制作重庆分公司

【硬件虚拟化】远离kernel的理想乡

【硬件虚拟化】远离kernel的理想乡

作者:三寸法师 2018-12-05 15:55:44

云计算

虚拟化 这个故事描述了如何使用硬件虚拟化(HVM)使得自己的一些hook代码远离内核不容易被其他内核hook所影响并且较难被检测。本文的思路来源于某学校的动态linux内核更新的玩意,代码大量抄自bluepill。

站在用户的角度思考问题,与客户深入沟通,找到沂水网站设计与沂水网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:网站制作、网站建设、企业官网、英文网站、手机端网站、网站推广、主机域名、网页空间、企业邮箱。业务覆盖沂水地区。

 简介

这个故事描述了如何使用硬件虚拟化(HVM)使得自己的一些hook代码远离内核不容易被其他内核hook所影响并且较难被检测。本文的思路来源于某学校的动态linux内核更新的玩意,代码大量抄自bluepill。

[[251619]]

第一章 (Avalon) 阿瓦隆的黎明

由于驱动牛人越来越多系统控制权的争夺愈演愈烈,内核之中几乎无一块净土。inline hook、ssdt hook等等各种hook充斥着我们幼小的内核。他们有些结构复杂,有些相互关联,有些检测监视,想要重新获得那些控制点的控制权必须花些力气研究那些hook,使得原来很简单的hook变得牵一发而动全身。

现在有硬件虚拟化技术,我们可以换个思路来解决那些问题了…

一、(Avalon)阿瓦隆的构成

(Avalon) 阿瓦隆的本体为以下几个部分:

1、Avlboot.sys(用于保存刚初始化后还没被hook污染的系统内核方便之后使用)

2、Avalon.sys(用于读取伪内核信息,开启硬件虚拟化加载伪内核)

3、XXX.sys(用户利用Avlboot.sys中所提供的信息进行具体hook操作的程序)

 

二、(Avalon)阿瓦隆的真相

本文所介绍的(Avalon) 阿瓦隆是一个基于虚拟化的内核加载框架。其利用硬件虚拟化(HVM)和一块没有被hook污染的内核内存,使得PC中同时运行着两个内核。并且Avalon通过控制sysenter_eip和idt中指向伪内核的相应地址来获得控制权。

简而言之,(Avalon)阿瓦隆就是利用硬件虚拟化(HVM)加载自己的内核架空原内核,使得自己能在不被检测的环境下获得内核的控制权。

实现原理如下图所示:

 

 

自己的hook程序在获得伪内核的基地址后,可以通过hookport来处理ssdt,shadow ssdt(strongod需要备份其所hook的ssdt稍微麻烦点,agp什么的只要把地址偏移一下就能用了)。

三、(Avalon)阿瓦隆的应用

其实我们可以把(Avalon)阿瓦隆看成是一种变相的sysenter hook+idt hook。

由于(Avalon)阿瓦隆基于硬件虚拟化(HVM)使得我们可以架空整个内核,使得内核可以在运行时可以实时的替换。

因为伪内核的所在内存范围不受内存监控,别的程序也无法直接访问,使得我们的伪内核就成了不用考虑其他干扰问题,可以随意hook的安全的理想乡。

这种方式的hook和传统的一些hook相比有着以下优点:

1、不在传统的内存监控的范围,较难检测。

2、不干扰系统中原有的hook代码,兼容性较高。

但是同样有一些缺点:

1、无法直接干涉object hook,irp hook等不依赖于内核内存的hook代码

2、整个系统依赖于硬件虚拟化(HVM),对硬件设备有一定要求。

第二章 空想具现化

一、纯洁的初始化

首先我们来实现(Avalon)阿瓦隆的初始化,初始化由4个部分组成:内核信息获取、伪内核构造、IDT信息保存、原始SYSENTER_EIP获取。

实现代码如下:

  
 
 
 
  1. NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)  
  2. {  
  3.  
  4. ... 
  5.  
  6. KrnlCopy(); 
  7. IDTCopy(); 
  8. ReadMsrSysenter(); 
  9.  
  10. ... 
  11.  
  12. } 
  13.  
  14. /* 
  15.  
  16.   内核拷贝 
  17.  
  18. */ 
  19.  
  20. VOID KrnlCopy() 
  21. { 
  22. PVOID Buffer; 
  23. ULONG Size; 
  24. ULONG RetSize; 
  25. PSYSTEM_MODULE_INFORMATION InfoBuffer; 
  26. NTSTATUS Status; 
  27. PVOID ModuleBase; 
  28. ULONG ModuleSize; 
  29.  
  30. Size=0x1000; 
  31.  
  32.  
  33. do { 
  34.  Buffer=ExAllocatePool(NonPagedPool,Size); 
  35.  Status=ZwQuerySystemInformation(SystemModuleInformation,Buffer,Size,&RetSize); 
  36.  if (Status == STATUS_INFO_LENGTH_MISMATCH) 
  37.        { 
  38.            ExFreePool(Buffer); 
  39.            Size = RetSize; 
  40.        }   
  41. }while(Status == STATUS_INFO_LENGTH_MISMATCH); 
  42.  
  43. InfoBuffer = (PSYSTEM_MODULE_INFORMATION)Buffer; 
  44.  
  45.  
  46. ModuleBase=(PVOID)(InfoBuffer->ModuleInfo[0].Base); 
  47. Orig_krnl=(ULONG)ModuleBase; 
  48.  
  49. DbgPrint("AvlBoot:Orig_krnl Base=%x\n",ModuleBase); 
  50. ModuleSize=InfoBuffer->ModuleInfo[0].Size; 
  51. makeKernelCopy((ULONG)ModuleBase,ModuleSize); 
  52. DbgPrint("AvlBoot:Avl_krnl Base=%x\n",Avl_krnl); 
  53. ExFreePool(Buffer); 
  54. } 

等初始化之后,其他部件就可以通过访问Avlboot.sys来获得伪内核的信息和内核原始信息。

二、抄来的虚拟化

这里先简单的向大家介绍下,Intel vt硬件虚拟化的步骤:

1、检查vt环境

2、开启vt功能

3、填充vt,存储虚拟机状态

4、设定需要拦截项目

5、设定从虚拟机中退出时返回的地址

6、启动虚拟机,等待拦截项目触发

7、拦截项目被触发,进入相关项目的处理例程

8、恢复虚拟机继续运行

接下来是一些坑爹的代码:

  
 
 
 
  1. /* 
  2.   Vmx初始化 
  3. */ 
  4. NTSTATUS NTAPI VmxInitialize ( 
  5.    PCPU Cpu, 
  6.    PVOID GuestEip, 
  7.    PVOID GuestEsp 
  8. ) 
  9. { 
  10.    PHYSICAL_ADDRESS AlignedVmcsPA; 
  11.    ULONG VaDelta; 
  12.    NTSTATUS Status; 
  13.  
  14.  
  15.    // 为 VMXON region 申请内存空间 
  16.    Cpu->Vmx.OriginaVmxonR = MmAllocateContiguousPages( 
  17.        VMX_VMXONR_SIZE_IN_PAGES,  
  18.        &Cpu->Vmx.OriginalVmxonRPA); 
  19.    if (!Cpu->Vmx.OriginaVmxonR)  
  20.    { 
  21.  DbgPrint("VmxInitialize(): Failed to allocate memory for original VMCS\n"); 
  22.        return STATUS_INSUFFICIENT_RESOURCES; 
  23.    } 
  24.  
  25.    DbgPrint("VmxInitialize(): OriginaVmxonR VA: 0x%x\n", Cpu->Vmx.OriginaVmxonR); 
  26.    DbgPrint("VmxInitialize(): OriginaVmxonR PA: 0x%llx\n", Cpu->Vmx.OriginalVmxonRPA.QuadPart); 
  27.  
  28.    // 为 VMCS 申请内存空间  
  29.    Cpu->Vmx.OriginalVmcs = MmAllocateContiguousPages( 
  30.        VMX_VMCS_SIZE_IN_PAGES,  
  31.        &Cpu->Vmx.OriginalVmcsPA); 
  32.    if (!Cpu->Vmx.OriginalVmcs)  
  33.    { 
  34.  DbgPrint("VmxInitialize(): Failed to allocate memory for original VMCS\n"); 
  35.        return STATUS_INSUFFICIENT_RESOURCES; 
  36.    } 
  37.  
  38.    DbgPrint("VmxInitialize(): Vmcs VA: 0x%x\n", Cpu->Vmx.OriginalVmcs); 
  39.    DbgPrint("VmxInitialize(): Vmcs PA: 0x%llx\n", Cpu->Vmx.OriginalVmcsPA.QuadPart); 
  40.  
  41.    // 开启vmx 
  42.    if (!NT_SUCCESS (VmxEnable (Cpu->Vmx.OriginaVmxonR))) 
  43.    { 
  44.        DbgPrint("VmxInitialize(): Failed to enable Vmx\n"); 
  45.        return STATUS_UNSUCCESSFUL; 
  46.    } 
  47.  
  48.    *((ULONG64 *)(Cpu->Vmx.OriginalVmcs)) =  
  49.        (MsrRead (MSR_IA32_VMX_BASIC) & 0xffffffff); //set up vmcs_revision_id       
  50.  
  51.    // 填充VMCS结构 
  52. Status = VmxSetupVMCS (Cpu, GuestEip, GuestEsp); 
  53.    if (!NT_SUCCESS (Status))  
  54.    { 
  55.        DbgPrint("VmxSetupVMCS() failed with status 0x%08hX\n", Status); 
  56.        VmxDisable(); 
  57.        return Status; 
  58.    } 
  59.  
  60.    DbgPrint("VmxInitialize(): Vmx enabled\n"); 
  61.  
  62.   // 保存EFER 
  63.    Cpu->Vmx.GuestEFER = MsrRead (MSR_EFER); 
  64.    DbgPrint("Guest MSR_EFER Read 0x%llx \n", Cpu->Vmx.GuestEFER); 
  65.  
  66.   // 保存控制寄存器 
  67.    Cpu->Vmx.GuestCR0 = RegGetCr0 (); 
  68.    Cpu->Vmx.GuestCR3 = RegGetCr3 (); 
  69.    Cpu->Vmx.GuestCR4 = RegGetCr4 (); 
  70.  
  71.    CmCli (); 
  72.    return STATUS_SUCCESS; 
  73. } 
  74.  
  75.  
  76.  
  77. /* 
  78.   开启vmx 
  79. */ 
  80. NTSTATUS NTAPI VmxEnable ( 
  81.    PVOID VmxonVA 
  82. ) 
  83. { 
  84.    ULONG cr4; 
  85.    ULONG64 vmxmsr; 
  86.    ULONG flags; 
  87.    PHYSICAL_ADDRESS VmxonPA; 
  88.  
  89. // 设置cr4位,为启用VM模式做准备 
  90.    set_in_cr4 (X86_CR4_VMXE); 
  91.    cr4 = get_cr4 (); 
  92.    DbgPrint("VmxEnable(): CR4 after VmxEnable: 0x%llx\n", cr4); 
  93.    if (!(cr4 & X86_CR4_VMXE)) 
  94.        return STATUS_NOT_SUPPORTED; 
  95.  
  96. // 检测是否支持vmx 
  97.    vmxmsr = MsrRead (MSR_IA32_FEATURE_CONTROL); 
  98.    if (!(vmxmsr & 4))  
  99.    { 
  100.        DbgPrint("VmxEnable(): VMX is not supported: IA32_FEATURE_CONTROL is 0x%llx\n", vmxmsr); 
  101.        return STATUS_NOT_SUPPORTED; 
  102.    } 
  103.  
  104. //bochs的bug,要改IA32_FEATURE_CONTROL的Lock为1 
  105. #if bochsdebug 
  106. MsrWrite(MSR_IA32_FEATURE_CONTROL,5); 
  107.    #endif 
  108.  
  109.    vmxmsr = MsrRead (MSR_IA32_VMX_BASIC); 
  110.    *((ULONG64 *) VmxonVA) = (vmxmsr & 0xffffffff);       //set up vmcs_revision_id 
  111.    VmxonPA = MmGetPhysicalAddress (VmxonVA); 
  112.  
  113.    DbgPrint("VmxEnable(): VmxonPA:  0x%llx\n", VmxonPA.QuadPart); 
  114.  
  115. //开启VMX 
  116. VmxTurnOn(VmxonPA); 
  117.    flags = RegGetEflags (); 
  118.    DbgPrint("VmxEnable(): vmcs_revision_id: 0x%x  Eflags: 0x%x \n", vmxmsr, flags); 
  119.  
  120.    return STATUS_SUCCESS; 
  121. } 
  122.  
  123.  
  124.  
  125. /* 
  126.   进入虚拟机 
  127. */ 
  128. NTSTATUS NTAPI VmxVirtualize ( 
  129.  PCPU Cpu 
  130. ) 
  131. { 
  132.  
  133.    ULONG esp; 
  134.    if (!Cpu) 
  135.        return STATUS_INVALID_PARAMETER; 
  136.  
  137. *((PULONG) (g_HostStackBaseAddress + 0x0C00)) = (ULONG) Cpu; 
  138.            
  139.    VmxLaunch (); 
  140.  
  141.    // never returns 
  142.  
  143.    return STATUS_UNSUCCESSFUL; 
  144. } 

三、蛋疼的拦截处理

sysenter的处理方法:

由于硬件虚拟化(HVM)无法直接拦截sysenter指令,所以只能使用其他方法来获得控制权。

这里有三种方法:

1、在kifastcallentery的头部写入cpuid,int3等利用中断或特权指令进入vm。

2、使用调试寄存器在kifastcallentery下硬件执行中断,利用中断进入vm

3、进入VMM后直接修改guest的sysenter_eip地址,通过控制msr的读写来欺骗其他访问msr的程序。

为了不被内存检测和充分利用调试寄存器,Avalon中我选用了方案3来控制进程执行sysenter后的运行流向。

部分代码:

  
 
 
 
  1. static BOOLEAN NTAPI VmxDispatchMsrRead ( 
  2.  PCPU Cpu, 
  3.  PGUEST_REGS GuestRegs, 
  4.  PNBP_TRAP Trap, 
  5.  BOOLEAN WillBeAlsoHandledByGuestHv 
  6. ) 
  7. { 
  8.  
  9. ... 
  10.  
  11. switch (ecx) { 
  12.  case MSR_IA32_SYSENTER_CS: 
  13.    MsrValue.QuadPart = VmxRead (GUEST_SYSENTER_CS); 
  14.    break; 
  15.  
  16.  case MSR_IA32_SYSENTER_ESP: 
  17.    MsrValue.QuadPart = VmxRead (GUEST_SYSENTER_ESP); 
  18.    break; 
  19.  case MSR_IA32_SYSENTER_EIP: 
  20.    MsrValue.QuadPart = Avlkrnlinfo->SysenterAddr; 
  21.  
  22. ... 
  23.  
  24. } 

idt重定向处理方法:

1、idt地址欺骗

2、idt模拟投递

第一种是指方案拦截sidt,lidt指令填充一份伪造的idt地址误导访问者(由系统投递相对稳定)。

第二种是指方案模拟idt的处理过程自己写程序投递idt。

因为方案一需要使用反汇编引擎分析具体的保存地址体积过大,所以本版的Avalon使用第二种方案即idt模拟投递。

部分代码:

  
 
 
 
  1. static BOOLEAN NTAPI VmxDispatchException ( 
  2.  PCPU Cpu, 
  3.  PGUEST_REGS GuestRegs, 
  4.  PNBP_TRAP Trap, 
  5.  BOOLEAN WillBeAlsoHandledByGuestHv 
  6. ) 
  7.  
  8. { 
  9.  
  10. ... 
  11.  
  12.  //SETP 7. SET EIP 
  13.  
  14.  if((uIntrInfo & 0xff) == 1){ 
  15.   ComPrint("VmxDispatchException():#BD hit  /n"); 
  16.   VmxWrite(GUEST_RIP,Avlkrnlinfo->Fake_IDTMap[0]); 
  17.  } 
  18.  else if ((uIntrInfo & 0xff) == 3){ 
  19.   ComPrint("VmxDispatchException():#BP hit /n"); 
  20.   VmxWrite(GUEST_RIP,Avlkrnlinfo->Fake_IDTMap[1]);} 
  21.  
  22. ... 
  23.  
  24. } 

第三章 理想乡的黄昏

一、(Avalon)阿瓦隆的检测

对于基于硬件虚拟化(HVM)的程序,首先想到的方法必然就是直接检测和对抗硬件虚拟化。

对硬件虚拟化的检测主要有:efer的检测,vme的检测。

对于处理了中断的vmm还能通过计算中断前后的时间差来判断自身是否在虚拟机中。

当然针对Avalon还有其他的检测方法(此处省略xx字)

二、未来的更新

Avalon才刚刚开始功能并不完善,还有好多功能想加进去:

1、将内核移入EPT(NPT)让你完全看不到

2、 和ring3程序交互…

3、其他隐藏功能

总结

Avalon只是硬件虚拟化应用的冰山一角,还有更多的应用等待着我们去探索,小弟的水平有限以后还要向各位高手多多请教继续努力学习。

该bin测试环境如下:

bochs2.4.5

windows xp sp3

注意:这个bin只是个简单的样品,真机上运行必蓝,且只针对ring0的中断,ring3有3处bug未修复。


当前标题:【硬件虚拟化】远离kernel的理想乡
网站路径:http://cxhlcq.cn/article/djgocgh.html

其他资讯

在线咨询

微信咨询

电话咨询

028-86922220(工作日)

18980820575(7×24)

提交需求

返回顶部