VT虚拟化技术笔记

VT技术笔记(part 1)

最近在学习VT技术,想把学习过程中记录的笔记分享出来。技术不精有不对的地方还望指正。代码会发到https://github.com/smallzhong/myvt这个仓库中,目前还在施工中,还没写完。欢迎star,预计这个月内完工。

VT概述

VT技术的大致含义如上。一个CPU上有vm monitor和guest。在guest中感受不到自己跑在虚拟环境中。执行普通的操作时在guest中的操作和未开启vt时的操作并没有什么不同,但是在执行一些特殊的指令或者进行一些特殊的操作,如执行cpuid指令、切换cr3指令、读写msr寄存器指令、触发异常时,会将控制权交还给vm monitor,通过vm monitor确定应该如何让该指令或者操作得到执行。

在云服务器的搭建中可以使用该技术让一台实体机上可以跑多台虚拟机。而该技术也可以做到无痕对一些指令进行高权限的hook。甚至可以通过ept技术进行对指定地址的无痕hook。

VT执行的大体流程

该流程的描述出自B站周壑的VT教学视频中,讲得非常通俗易懂。VT的开启与关闭大致分为开锁、开柜门、拔电源、选中机器、装机、开机、拔电源、关柜门、关锁9个步骤。这9个步骤的具体描述如下。此处为大体的描述,在后面会对每一个步骤的具体细节进行具体的说明。

① 开锁:检测是否支持VT。

② 开柜门:vmxon。需要申请一块内存然后把这块内存的lowpart和highpart作为参数调用vmxon。注意这块内存的开头要填入一个特定的msr寄存器中的值。注意这里vmxon修改cr4寄存器之后,cr0寄存器的页保护和段保护位都不能再置位,如果试图修改cr0的页保护或者段保护位,会触发异常蓝屏。

③ 拔电源:vmclear。

④ 选中机器:vmptrload,相当于一个指针。需要通过vmptrload来指向某个机器。如果当前有多个机器的话,在各个机器之间的切换操作也需要用到vmptrload。

⑤ 装机:设置vmcs。也是要申请一块内存,然后在这块内存里面填入开启虚拟机时虚拟机使用的一些寄存器。要注意这里不能直接操作这块内存区域,要用vmwrite来操作。因为随着版本的更新,原来在这块内存区域的东西可能后来就不在了。vmwrite保证兼容性。

⑥ 开机vmlaunch。

⑦ 拔电源vmclear。

⑧ 关柜门vmoff。

检测是否支持VT(开锁)

在intel手册31.5章中详细描述了vmm的开启过程。

① 通过cpuid指令查询CPU是否支持VT。

② 通过一些特定的MSR寄存器(在31.5.1中有介绍)来确认是否支持开启VT。

③ 申请一块非分页内存,用来存放vmxon区域。其大小在IA32_VMX_BASIC这个寄存器中指定。该内存要4kb对齐(后12位全为0)。实际申请时申请一个页的大小的非分页内存即可。

④ 初始化vmxon区域的版本编号(前4个字节)。版本编号通过IA32_VMX_BASIC这个寄存器的低4字节获取。实际实验时发现这个数为1,理论上把上一步申请到的内存前四字节置1即可,但为了兼容最好还是从寄存器中取值出来。

⑤ 给cr0和cr4寄存器的特定位置置位,使得其满足如下要求:

IA32_VMX_CR0_FIXED0 寄存器中为1的位,在cr0中必须为1;

IA32_VMX_CR0_FIXED1 寄存器中为0的位,在cr0中必须为0;

IA32_VMX_CR4_FIXED0 寄存器中为1的位,在cr4中必须为1;

IA32_VMX_CR4_FIXED1 寄存器中为0的位,在cr4中必须为0。

⑥ 确认IA32_FEATURE_CONTROL寄存器被正确置位。该寄存器的具体细节可以通过《处理器虚拟化技术》2.3.2.1找到。

也就是说,该寄存器的bit0是lock为,bit2是outside位。必须保证这两位均为1才可以开启VT。可以大致通过读取该寄存器后将其&5,判断是否两个位均为1。

⑦ 执行vmxon指令,传进去的参数是一个指向存放vmxon区域的物理地址的指针。如果该指令成功,那么rflags.cf=0,否则rflags.cf=1。

由上可写出检测是否支持VT的代码如下:

//检测Bios是否开启VTBOOLEAN VmxIsCheckSupportVTBIOS(){
      ULONG64 value = __readmsr(IA32_FEATURE_CONTROL);
    return (value & 0x5) == 0x5;}

//检测CPU是否支持VTBOOLEAN VmxIsCheckSupportVTCPUID(){
      int cpuidinfo[4];    __cpuidex(cpuidinfo, 1, 0);    //CPUID 是否支持VT ecx.vmx第6位 如果为1,支持VT,否则不支持    return (cpuidinfo[2] >> 5) & 1;}

//检测CR4VT是否开启,如果为1 代表已经开启过了,否则没有开启BOOLEAN VmxIsCheckSupportVTCr4(){
      ULONG64 mcr4 = __readcr4();    //检测CR4 VT是否开启,cr4.vmxe如果第14位为1,那么VT已经被开启,否则可以开启    return ((mcr4 >> 13) & 1) == 0;}
void checkVT(){
      if (VmxIsCheckSupportVTCPUID())    {
          DbgPrintEx(77, 0, "[db]:VmxIsCheckSupportVTCPUID  number = %d\r\n", KeGetCurrentProcessorNumber());    }
    if (VmxIsCheckSupportVTBIOS())    {
          DbgPrintEx(77, 0, "[db]:VmxIsCheckSupportVTBIOS  number = %d\r\n", KeGetCurrentProcessorNumber());    }
    if (VmxIsCheckSupportVTCr4())    {
          DbgPrintEx(77, 0, "[db]:VmxIsCheckSupportVTCr4  number = %d\r\n", KeGetCurrentProcessorNumber());    }}

vmxon(开柜门)

申请一块4KB对齐的非分页内存,作为vmxon的参数。

​​​​​​​

PHYSICAL_ADDRESS lowphys,heiPhy;lowphys.QuadPart = 0;heiPhy.QuadPart = -1;pVcpu->VmxOnAddr = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);

设置vmxon区域。这一块的说明在intel白皮书24.11.5中。这一块内存是操作系统使用的。在申请区域之后要rtlzeromemory一下,防止没有挂页。其他位都置零即可,之后操作系统进行虚拟机的管理的时候会使用到这块内存。

我们需要设置的只有头四个字节。要把msr中 IA32_VMX_BASIC 这个寄存器的低4字节填入vmxon的头4字节。否则vmxon的执行会出错。基本上这个寄存器的低4字节会是1。之后随着版本的更新可能会有变化。

ULONG64 vmxBasic = __readmsr(IA32_VMX_BASIC);*(PULONG)pVcpu->VmxOnAddr = (ULONG)vmxBasic;

在设置完vmxon区域之后,还需最后一步即可vmxon。需要根据msr寄存器中的指示进行cr0和cr4寄存器的设置。

<
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值