内容简介:这个故事描述了如何使用硬件虚拟化(HVM)使得自己的一些hook代码远离内核不容易被其他内核hook所影响并且较难被检测。本文的思路来源于某学校的动态linux内核更新的玩意,代码大量抄自bluepill。由于驱动牛人越来越多系统控制权的争夺愈演愈烈,内核之中几乎无一块净土。inline hook、ssdt hook等等各种hook充斥着我们幼小的内核。他们有些结构复杂,有些相互关联,有些检测监视,想要重新获得那些控制点的控制权必须花些力气研究那些hook,使得原来很简单的hook变得牵一发而动全身。
简介
这个故事描述了如何使用硬件虚拟化(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) 阿瓦隆是一个基于虚拟化的内核加载框架。其利用硬件虚拟化(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获取。
实现代码如下:
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硬件虚拟化的步骤:
1、检查vt环境
2、开启vt功能
3、填充vt,存储虚拟机状态
4、设定需要拦截项目
5、设定从虚拟机中退出时返回的地址
6、启动虚拟机,等待拦截项目触发
7、拦截项目被触发,进入相关项目的处理例程
8、恢复虚拟机继续运行
接下来是一些坑爹的代码:
/* 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指令,所以只能使用其他方法来获得控制权。
这里有三种方法:
1、在kifastcallentery的头部写入cpuid,int3等利用中断或特权指令进入vm。
2、使用调试寄存器在kifastcallentery下硬件执行中断,利用中断进入vm
3、进入VMM后直接修改guest的sysenter_eip地址,通过控制msr的读写来欺骗其他访问msr的程序。
为了不被内存检测和充分利用调试寄存器,Avalon中我选用了方案3来控制进程执行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重定向处理方法:
1、idt地址欺骗
2、idt模拟投递
第一种是指方案拦截sidt,lidt指令填充一份伪造的idt地址误导访问者(由系统投递相对稳定)。
第二种是指方案模拟idt的处理过程自己写程序投递idt。
因为方案一需要使用反汇编引擎分析具体的保存地址体积过大,所以本版的Avalon使用第二种方案即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未修复。
以上所述就是小编给大家介绍的《【硬件虚拟化】远离kernel的理想乡》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 远离服务器宕机 腾讯WeTest推出服务器深度性能测试服务
- 我们为什么要远离数据库生成的ID?- Tugberk Ugurlu
- ASLR如何保护Linux系统远离缓冲区溢出攻击? - 网络·安全技术周刊第377期
- ASLR如何保护Linux系统远离缓冲区溢出攻击? - 网络·安全技术周刊第378期
- 虚拟化生态系统及实现从虚拟化走向云端
- KVM虚拟化技术(一)虚拟化简介以及按安装
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。