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

Posted 看雪学院

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了硬件虚拟化远离kernel的理想乡相关的知识,希望对你有一定的参考价值。


简介


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



第一章  (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)阿瓦隆的应用 


其实我们可以把(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获取。

实现代码如下:

NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath) 
{

...

KrnlCopy();
IDTCopy();
ReadMsrSysenter();

...

}

/*

  内核拷贝

*/


VOID KrnlCopy()
{
PVOID Buffer;
ULONG Size;
ULONG RetSize;
PSYSTEM_MODULE_INFORMATION InfoBuffer;
NTSTATUS Status;
PVOID ModuleBase;
ULONG ModuleSize;

Size=0x1000;


do {
 Buffer=ExAllocatePool(NonPagedPool,Size);
 Status=ZwQuerySystemInformation(SystemModuleInformation,Buffer,Size,&RetSize);
 if (Status == STATUS_INFO_LENGTH_MISMATCH)
       {
           ExFreePool(Buffer);
           Size = RetSize;
       }  
}while(Status == STATUS_INFO_LENGTH_MISMATCH);

InfoBuffer = (PSYSTEM_MODULE_INFORMATION)Buffer;


ModuleBase=(PVOID)(InfoBuffer->ModuleInfo[0].Base);
Orig_krnl=(ULONG)ModuleBase;

DbgPrint("AvlBoot:Orig_krnl Base=%x\n",ModuleBase);
ModuleSize=InfoBuffer->ModuleInfo[0].Size;
makeKernelCopy((ULONG)ModuleBase,ModuleSize);
DbgPrint("AvlBoot:Avl_krnl Base=%x\n",Avl_krnl);
ExFreePool(Buffer);
}


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


二、抄来的虚拟化 


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

/*
  Vmx初始化
*/

NTSTATUS NTAPI VmxInitialize (
   PCPU Cpu,
   PVOID GuestEip,
   PVOID GuestEsp
)
{
   PHYSICAL_ADDRESS AlignedVmcsPA;
   ULONG VaDelta;
   NTSTATUS Status;


   // 为 VMXON region 申请内存空间
   Cpu->Vmx.OriginaVmxonR = MmAllocateContiguousPages(
       VMX_VMXONR_SIZE_IN_PAGES,
       &Cpu->Vmx.OriginalVmxonRPA);
   if (!Cpu->Vmx.OriginaVmxonR)
   {
 DbgPrint("VmxInitialize(): Failed to allocate memory for original VMCS\n");
       return STATUS_INSUFFICIENT_RESOURCES;
   }

   DbgPrint("VmxInitialize(): OriginaVmxonR VA: 0x%x\n", Cpu->Vmx.OriginaVmxonR);
   DbgPrint("VmxInitialize(): OriginaVmxonR PA: 0x%llx\n", Cpu->Vmx.OriginalVmxonRPA.QuadPart);

   // 为 VMCS 申请内存空间
   Cpu->Vmx.OriginalVmcs = MmAllocateContiguousPages(
       VMX_VMCS_SIZE_IN_PAGES,
       &Cpu->Vmx.OriginalVmcsPA);
   if (!Cpu->Vmx.OriginalVmcs)
   {
 DbgPrint("VmxInitialize(): Failed to allocate memory for original VMCS\n");
       return STATUS_INSUFFICIENT_RESOURCES;
   }

   DbgPrint("VmxInitialize(): Vmcs VA: 0x%x\n", Cpu->Vmx.OriginalVmcs);
   DbgPrint("VmxInitialize(): Vmcs PA: 0x%llx\n", Cpu->Vmx.OriginalVmcsPA.QuadPart);

   // 开启vmx
   if (!NT_SUCCESS (VmxEnable (Cpu->Vmx.OriginaVmxonR)))
   {
       DbgPrint("VmxInitialize(): Failed to enable Vmx\n");
       return STATUS_UNSUCCESSFUL;
   }

   *((ULONG64 *)(Cpu->Vmx.OriginalVmcs)) =
       (MsrRead (MSR_IA32_VMX_BASIC) & 0xffffffff); //set up vmcs_revision_id      

   // 填充VMCS结构
Status = VmxSetupVMCS (Cpu, GuestEip, GuestEsp);
   if (!NT_SUCCESS (Status))
   {
       DbgPrint("VmxSetupVMCS() failed with status 0x%08hX\n", Status);
       VmxDisable();
       return Status;
   }

   DbgPrint("VmxInitialize(): Vmx enabled\n");

  // 保存EFER
   Cpu->Vmx.GuestEFER = MsrRead (MSR_EFER);
   DbgPrint("Guest MSR_EFER Read 0x%llx \n", Cpu->Vmx.GuestEFER);

  // 保存控制寄存器
   Cpu->Vmx.GuestCR0 = RegGetCr0 ();
   Cpu->Vmx.GuestCR3 = RegGetCr3 ();
   Cpu->Vmx.GuestCR4 = RegGetCr4 ();

   CmCli ();
   return STATUS_SUCCESS;
}



/*
  开启vmx
*/

NTSTATUS NTAPI VmxEnable (
   PVOID VmxonVA
)
{
   ULONG cr4;
   ULONG64 vmxmsr;
   ULONG flags;
   PHYSICAL_ADDRESS VmxonPA;

// 设置cr4位,为启用VM模式做准备
   set_in_cr4 (X86_CR4_VMXE);
   cr4 = get_cr4 ();
   DbgPrint("VmxEnable(): CR4 after VmxEnable: 0x%llx\n", cr4);
   if (!(cr4 & X86_CR4_VMXE))
       return STATUS_NOT_SUPPORTED;

// 检测是否支持vmx
   vmxmsr = MsrRead (MSR_IA32_FEATURE_CONTROL);
   if (!(vmxmsr & 4))
   {
       DbgPrint("VmxEnable(): VMX is not supported: IA32_FEATURE_CONTROL is 0x%llx\n", vmxmsr);
       return STATUS_NOT_SUPPORTED;
   }

//bochs的bug,要改IA32_FEATURE_CONTROL的Lock为1
#if bochsdebug
MsrWrite(MSR_IA32_FEATURE_CONTROL,5);
   #endif

   vmxmsr = MsrRead (MSR_IA32_VMX_BASIC);
   *((ULONG64 *) VmxonVA) = (vmxmsr & 0xffffffff);       //set up vmcs_revision_id
   VmxonPA = MmGetPhysicalAddress (VmxonVA);

   DbgPrint("VmxEnable(): VmxonPA:  0x%llx\n", VmxonPA.QuadPart);

//开启VMX
VmxTurnOn(VmxonPA);
   flags = RegGetEflags ();
   DbgPrint("VmxEnable(): vmcs_revision_id: 0x%x  Eflags: 0x%x \n", vmxmsr, flags);

   return STATUS_SUCCESS;
}



/*
  进入虚拟机
*/

NTSTATUS NTAPI VmxVirtualize (
 PCPU Cpu
)
{

   ULONG esp;
   if (!Cpu)
       return STATUS_INVALID_PARAMETER;

*((PULONG) (g_HostStackBaseAddress + 0x0C00)) = (ULONG) Cpu;
         
   VmxLaunch ();

   // never returns

   return STATUS_UNSUCCESSFUL;
}



三、蛋疼的拦截处理

sysenter的处理方法:

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

这里有三种方法:

static BOOLEAN NTAPI VmxDispatchMsrRead (
 PCPU Cpu,
 PGUEST_REGS GuestRegs,
 PNBP_TRAP Trap,
 BOOLEAN WillBeAlsoHandledByGuestHv
)
{

...

switch (ecx) {
 case MSR_IA32_SYSENTER_CS:
   MsrValue.QuadPart = VmxRead (GUEST_SYSENTER_CS);
   break;

 case MSR_IA32_SYSENTER_ESP:
   MsrValue.QuadPart = VmxRead (GUEST_SYSENTER_ESP);
   break;
 case MSR_IA32_SYSENTER_EIP:
   MsrValue.QuadPart = Avlkrnlinfo->SysenterAddr;

...

}

 

idt重定向处理方法:

static BOOLEAN NTAPI VmxDispatchException (
 PCPU Cpu,
 PGUEST_REGS GuestRegs,
 PNBP_TRAP Trap,
 BOOLEAN WillBeAlsoHandledByGuestHv
)

{

...

 //SETP 7. SET EIP

 if((uIntrInfo & 0xff) == 1){
  ComPrint("VmxDispatchException():#BD hit  /n");
  VmxWrite(GUEST_RIP,Avlkrnlinfo->Fake_IDTMap[0]);
 }
 else if ((uIntrInfo & 0xff) == 3){
  ComPrint("VmxDispatchException():#BP hit /n");
  VmxWrite(GUEST_RIP,Avlkrnlinfo->Fake_IDTMap[1]);}

...

}



第三章     理想乡的黄昏



一、(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未修复。


附件的内容(bin+source+bin=饼+豆沙+饼,点击文末阅读原文即可获取附件):


  • avlboot.sys

  • avalonreader(读取avlboot信息的源码)

  • avalon_vt.sys


- End -



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

看雪ID:三寸法师                                     

https://bbs.pediy.com/user-135566.htm



本文由看雪论坛 三寸法师 原创

转载请注明来自看雪社区


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


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

好书推荐:

以上是关于硬件虚拟化远离kernel的理想乡的主要内容,如果未能解决你的问题,请参考以下文章

virtualbox和kvm的区别

理想乡题解 (线段树优化dp)

kvm -- Kernel-based Virtual Machine

kernel 目录

在 C(++) 中优化远离函数表

一文告诉你Linux如何配置KVM虚拟化--安装篇