VT笔记(1)

83 篇文章 9 订阅
2 篇文章 2 订阅

概念

硬件虚拟化技术,隐藏计算机资源物理性的一面,比如能把单个或多个物理资源,转化为一个或多个逻辑资源。

intel vt-x/或者amdv8(SVM)

VT是一个驱动(.sys),由操作系统加载后,运行在每个cpu上,推翻原来的操作系统,然后os和app-r0和r3变成GUEST,建立特权层(host,vmm,hypervisor,root,-1),然后监控GUEST执行。

这样就可以简化虚拟机开发,hook,隐藏,软件调试与反调试。

简单概括就是在os和hardware中间加一层。参考下图armv8的架构

secureworld与normalworld

之间物理隔离。

我们知道r3进r0,xp以前软中断int2E,xp及以后是sysenter,x86是kifastcall,x64及其以后syscall。

内核进入VT层,使用vmexit,就是因为guest执行某条指令,vt层感兴趣,操作系统就会抛出vmexit事件,一个异常,被特权层监控到。

原理概述

特权层就是中间的VMMhost.

应用场景是1.硬件-vmware-0s,2.硬件-VTDrv-os

VT出现前,VMWare采用进程虚拟机,作为操作系统一个进程,通过软件手段模拟硬件,为了实现多个虚拟机真是物理地址隔离,需要编程实现把客户机的物理地址翻译为真实机器的物理地址。要给不同操作系统编写不同虚拟机程序。

硬件虚拟化技术则在硬件上(ept,gpt)实现内存地址和io设备映射,就是引进直接二次寻址,从虚拟机虚拟地址到虚拟机物理地址到真实机物理地址和i/o映射的特性。

VMM在使用硬件虚拟化技术时创建出的特权层,提供给虚拟机开发者,实现虚拟硬件与真实硬件的通信和一些事件(vmm抛出的事件)
进行处理。

HOST:虚拟机宿主(VMM)即VT驱动,相当于VMWare。两者互斥。

GUEST:客户机,就是操作系统以上的东西。

VT意义:简化虚拟机开发,中断HOOK,软件调试。SSDTHOOk等。

流程

VT运行时候,VTDrv驱动做初始化,执行VMXON,开启VT,然后建立了特权层,然后执行VMLAUNCH,将当前级别切换成Guest,从root-1级别,退出vt状态,然后Guest运行时候产生VMEXIT事件,被Host捕捉到(比如事件抛出调用int2e等),重新回到host调用那个抛出事件的handle函数。调用完handle函数后,调用VMRESUME,重新回到Guest。直到再次发生抛出VMEXIT,如果想退出VM,就调用VMXOFF。

VMLAUNCH和VMRESUME区别就是lanuch初始化时候调用,以后用VMRESUME。

Guest执行时候,有可能引发VMEXIT事件,host处理这些操作,即可以修改他,也可以欺骗guest,完全取决于host,guest指令分两种,无条件陷入(只要执行就会引发VMEXIT)和有条件(必须在vt驱动注册之后)。

VT指令

VMCS区域管理指令:

VMPTRLD:激活当前VMCS(关键结构体,VThost/guest对应的上下文,每个cpu核一个),并加载内存数据到VMCS指针。

VMPTRST:存储当前VMCS指针数据到内存

VMCLEAR:将launch状态的VMCS设置为clear状态(非激活状态),与VMPTRLD相反。

VMREAD:读取VMCS中的域值

VMWRITE:写入VMCS的值域

VMX管理指令:

VMLAUNCH:启动VMCS的虚拟机

VMRESUME:从host恢复VMCS的虚拟机

VMXOFF:退出VMXROOT模式

VMON:进入CMXROOT模式

VMCALL:关闭VT时让虚拟机主动发出退出事件(NBP_HYPERCALL_UNLOAD)

其他什么RDMSR,WWRMSR,异常等就是有条件指令,必须在vt驱动中注册。

虚拟机双层地址翻译

就是虚拟机虚拟到虚拟机物理到真实机物理地址。

前置知识参考

https://blog.csdn.net/youyou519/article/details/82419163

EPT(Extended Page Table,扩展页表技术),Intel Core i7全面支持EPT

CR3:指向虚拟机页表(gpt,guest page table)虚拟机虚拟地址到虚拟机物理地址映射

EPTP:指向EPT页表(用于将虚拟机物理地址到真实机物理地址映射)

将CR3指定的Gpt通过EPTP指定的EPT翻译成真实内存中的GPT,虚拟地址通过GPT翻译成虚拟机物理地址。

虚拟机物理地址通过EPT翻译成真实地址。

两者都是4级地址翻译。

步骤:因为虚拟机虚拟地址到虚拟机物理地址要先得到gpt真实内容,所以首先把gpt表,映射成真实地址(因为他本身就是虚拟机的物理地址,并不是真的)。然后2层各4级寻址。

利用EPT页表将CR3所指向的GPT顶级地址翻译成真实物理地址,找到GPT的真实内容。即首先映射GPT到真实地址。

找到cr3的真实物理地址之后,以后再guest中拿到的虚拟机线性地址通过GPT表得到虚拟机物理地址。然后再通过EPTP找到真实地址。

X64调用约定

X64上相比x86,新增了r8-r15寄存器。统一使用fastcall,其中rbx,r12,r13,r14,r15寄存器是乱写急促安全,系统随时改写寄存器,程序自己使用需要通过栈备份。

rax,rcx,rdx,r8,r9,r10,r11是易挥发的,不用特别保护,其他寄存器比如rbx,需要保护,x86下只有eax,ecx,edx是易挥发的。

r8,r8d,r8w,r8b/低4,2,1字节。

x64不能内嵌汇编,只能专门写个asm,关于x64汇编编写注意要对汇编asm设置

接着下面一项修改成

另外X64和x86汇编的区别:

1.比ecx(rcx),edx(rdx)寄存器多了两个r8,r9

2.参数入栈对齐到8字节

3.前四个参数放到了rcx,rdx,r8,r9四个寄存器中,但栈上也会预留4个空间

4.调用者来负责堆栈平衡

5.栈整体要被16整除

6.形参不菲空间分配和初始化由调用者来完成,(为了变参)

7.局部变量空间由被调用者来分配。

VT初始化与启动流程

1.检查是否支持VT以及是否开启VT

2.分配VMX内存区域,VMXON,VMCS(来回切换的上下文环境),HOST-vMM栈区域(从guest有栈,到host无栈,需要自己分配栈区,在堆上)

3.设置Cr4.vmxe,防止别人使用vt

4.初始化VMXON(就是填一个版本号,在msr寄存器里,传给vmxon),VMXON开启VT,创建特权区

5.初始化VMCS区域:guest host虚拟机运行控制域,guest、host寄存器,rsp/rip/rflag/exithandler(exithandler是捕获exit事件时host运行代码)

6调用VMLAUNCH,返回guest,以后再进入就用resume。

检查是否支持VT和开启VT

CPUID返回的ecx的第五位,为1支持VT,

GetCpuIdInfo(0x1,&eax,&ebx,&ecx,&edx)//汇编实现的函数,调用cpuid
return((ecx&1<<5))_

CR4的第13位,vmxe是否为1,表示已经开启vt

MSR_IA32_FEATURE_CONTROL寄存器第0位Lock位是否为1,表示bios是否设置1,开启vt

MSR_IA32_FEATURE_CONTROL寄存器第2位Lock(Enable VMX outside SMX位)为1,VMxon不能执行。

分配VMX内存

1VMXON,HOST-VMM栈空间,VMCS放在一个结构体里

typedef struct _CPU
{
    PVOID OriginalVmcs;
    PVOID OriginalVmxonR;
    PVOID VMM_Stack;

}CPU,*PCPU;
CPU g_VMXCPU[128]={0}//128核

这三个每个流程如下

1.VMXON内存

申请连续4K对齐内存空间

pCpu->OriginaVmxonR=MmAllocateContiguousMemory(2*PAGE_SIZE,PhyAddr);

PVOID MmAllocateContiguousMemory(
  SIZE_T           NumberOfBytes,
  PHYSICAL_ADDRESS HighestAcceptableAddress//调用者可以使用的最高有效物理地址。
);

VMCS的版本号表示符写入VMXON区

*(ULONG64)pCpu->OriginaVmxonR=(__readmsr(MSR_IA32_VMX_BASIC)&0xffffffff);

VMXON指令进入虚拟机模式

PhyAddr=(MmGetphysicalAddress(pCpu->OriginaVmxonR));//拿到物理地址
__vmx_on(&PhyAddr)//开启虚拟机模式,以后不再访问这里

VMM Stack

因为host没有栈,所以要给他在堆上分配一个栈

pCpu->VMM_Stack=ExAllocatePoolWithTag(NoPagedPool,8*PAGE_SIZE,MEM_TAG);
VMM_Stack=(ULLONG_PTR)pCpu->VMM_Stack+8*PAGE_SIZE-8;//栈由高向低增长,指向空位,空栈递减(空就是栈顶指针是指向空的,x86是满,sp指向栈顶的元素)
__vmx_vmwrite(HOST_RSP,VMM_Stack);//设置VMCS区域的host rsp域

VMCS各种控制域设置

VMCS是一个4k内存区域,分为六部分:

1,GUEST-STATE域:虚拟机从root操作模式进入non-root操作模式时,处理器的状态

2.HOST-STATE域:与guest相反

3.VM执行控制域:虚拟机在non-root操作模式运行时的时候,控制处理器non-root操作模式退出到root操作模式

4.VM退出控制域:虚拟机从no-root操作模式下退出时,需要保持的信息

5.VM进入控制域:虚拟机从root到non-root模式,需要读取的信息。

6.VM退出信息域:虚拟机从non-root操作模式退出到root模式,将退出原因保存在该区域。

guest寄存器设置:cr0,cr3,cr4,dr7,gdtr,idtr,es,cs,ss,ds,fs,gs,ldtr,tr

host寄存器:同上

guest rsp/rip/rflag设置(当前系统的rsp,rip,rflag,就相当于上下文)

host rsp、rip设置(rsp为分配的VMM_STACK,rip就是下一条要执行的指令,所以为VmxVmexitHandler,写死)

VMCS类似进程EPROCESS

VMLAUNCH

__vmx_vmlaunch();//进入特权层

root切换到non-root寄存器上下文状态切换:

在进入root模式下,会通过push(HVM_SAVE_ALL_NOSEGRESGS宏)把guest非seg的寄存器传给root的exithandler使用。

处理完root模式下的事情,进入non-root模式的时候,需要设置non-root的rip并恢复guest寄存器的值

root模式的rip和rsp固定,前面申请的内存底和栈顶,不用修改

vt多核的支持

就是前面每个cpu一个独立内存和寄存器

CPU g_VMXCPU[128];

for(i=0;i<keNumberProcessors;i++){
    KeSetSystemAffinityThread((KAFFINITY)(ULONG_PTR)1<<i));//设置亲和力,将该代码运行在指定processor
    OldIrql=KeRaiseIrqlToDpcLevel();//提升IRQL级别,防止被打断
    Status=CmSubvert(NULL);//CmSubvert就是前面一系列操作,的流程是保存所有寄存器(除了段寄存器)的内容到栈里后,盗用HvmSubverCpu
    KeLowerIrql(OldIrql);
    KeRevertToUserAffinityThread();
    if(Status)
    {
        KdPrint(("HvmSwallowBluepill(): CmSubvert() failed with status 0x%08hX\n",Status));
        break;
    }
}

VT退出

VmCall,

传一个事件NBP_HYPERCALL_UNLOAD给Vmcall,Vmcall产生一个EXIT_REASON_VMCALL,host知道这个事件,执行_vmx_off(),撤销特权层,把guest寄存器拷贝进host这里,然后Trampoline,一组生成的汇编指令,将host的寄存器状态恢复成guest完全一样,guest_rip+len进入普通模式。

启动流程

VmxIsImplemented()(判断是否支持VT)

->StartVirtualTechlology()(初始化启动VT内部封装HvmSwallowBluepill())

->CmSubvert(NULL)(汇编)

>HbmSubvertCpu()(汇编,每个处理器都会调用,检查IA32_FEATURE_CONTROL,LOCK位,Enable VMX outside SMX位,为VMXON结构分配空间,__vmx_on(&phyaddr),为VMCS结构分配空间,为Host分配栈,set_in_cr4(X86_CR4_VMXE,防止别人再用vt))

->VmxSetupVmCS()(初始化VMCS结构)(比如guest寄存器2写入vmcs,host sp,ip也写入vmcs)

->vmx_vmlaunch()

->后面guest通过vmxexithandle陷入host,然后执行完通过vmresume返回guest此时要通过_vmwrite写入guest下一条要执行的指令,通过guesteip+当前指令长度。

卸载过程

driverUnload里StopVirtualTechlology()(封装原本HvmSpitOutBluepill())

->vmxVmCall()

->VmExitHandler

->VmxShutdown

->VmxGenerateTrampolineToGuest()申请一个弹簧床函数,恢复寄存器的

->__vmx_off()

clear_in_cr4(x85_CR4_VMXE)(说明不再占用vt资源)

比如没有欺骗前


欺骗后

 

 

 

 

 

 

 

 

 

 

 

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值