内容简介:这个故事描述了如何使用硬件虚拟化(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的理想乡》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Beginning Apache Struts
Arnold Doray / Apress / 2006-02-20 / USD 44.99
Beginning Apache Struts will provide you a working knowledge of Apache Struts 1.2. This book is ideal for you Java programmers who have some JSP familiarity, but little or no prior experience with Ser......一起来看看 《Beginning Apache Struts》 这本书的介绍吧!