VT 基本框架

VT 基本框架

1.查询CPU对VMX的支持

  • cpuid 指令,查询1号功能,返回的ECX[5]==1
  •   #define CPUID_ECX_VMX_ABILITY (1<<5)
      ULONG64 CheckCPUID() {
    
          int ctx[4] = { 0 };//顺序:eax,ebx,ecx,edx
          __cpuid(ctx, 1);
    
          return ctx[2] & CPUID_ECX_VMX_ABILITY;	//Bit-5
      }
    
  • rdmsr 指令,查询BIOS是否已开启对VMX的支持
  •   #define MSR_IA32_FEATURE_CONTROL 0x3A
      ULONG64 CheckMsr() {
    
          MSR_IA32_FEATURE_CONTROL_REGISTER msr = { 0 };
          msr.value.QuadPart = __readmsr(MSR_IA32_FEATURE_CONTROL);
    
          return msr.Bits.Lock;
      }
    

2.开启VMX

  • 修正CR0和CR4,设置CR4.VMXE=1
  •   #define	MSR_IA32_VMX_CR0_FIXED0				0x486
      #define	MSR_IA32_VMX_CR0_FIXED1				0x487
      #define	MSR_IA32_VMX_CR4_FIXED0				0x488
      #define	MSR_IA32_VMX_CR4_FIXED1				0x489
      ULONG64 FixCr0() {
    
          IA32_CR0 uCr0 = { 0 };
          uCr0.value.QuadPart = __readcr0();
    
          uCr0.value.QuadPart |= __readmsr(MSR_IA32_VMX_CR0_FIXED0);
          uCr0.value.QuadPart &= __readmsr(MSR_IA32_VMX_CR0_FIXED1);
    
          __writecr0(uCr0.value.QuadPart);
          return TRUE;
      }
      ULONG64 FixCr4() {
    
          IA32_CR4 uCr4 = { 0 };
          uCr4.value.QuadPart = __readcr4();
    
          uCr4.value.QuadPart |= __readmsr(MSR_IA32_VMX_CR4_FIXED0);
          uCr4.value.QuadPart &= __readmsr(MSR_IA32_VMX_CR4_FIXED1);
          uCr4.Bits.VMXE = 1;
    
          __writecr4(uCr4.value.QuadPart);
          return TRUE;
      }
    

3.申请内存

  • VMON区,需要页对齐,并且需要得到它的物理地址
  • VMCS区,需要页对齐,并且需要得到它的物理地址
  • VMM栈,可以的话申请几个页,避免栈溢出
  •   ULONG64 CreateVM(UCHAR CpuIndex) {
    
          psVM[CpuIndex].pOnVa = ExAllocatePool2(POOL_FLAG_NON_PAGED, PAGE_SIZE, 'VMON');
          if (!psVM[CpuIndex].pOnVa) { return FALSE; }
          RtlZeroMemory(psVM[CpuIndex].pOnVa, PAGE_SIZE);
          psVM[CpuIndex].pOnPa = MmGetPhysicalAddress(psVM[CpuIndex].pOnVa);
    
          psVM[CpuIndex].pCsVa = ExAllocatePool2(POOL_FLAG_NON_PAGED, PAGE_SIZE, 'VMCS');
          if (!psVM[CpuIndex].pCsVa) { return FALSE; }
          RtlZeroMemory(psVM[CpuIndex].pCsVa, PAGE_SIZE);
          psVM[CpuIndex].pCsPa = MmGetPhysicalAddress(psVM[CpuIndex].pCsVa);
    
          psVM[CpuIndex].pStack = ExAllocatePool2(POOL_FLAG_NON_PAGED, STACK_SIZE, 'VMSK');
          if (!psVM[CpuIndex].pStack) { return FALSE; }
          RtlZeroMemory(psVM[CpuIndex].pStack, STACK_SIZE);
    
          return TRUE;
      }
    

4.设置VMXON区,执行VMXON

  • 只需要填写一个版本号
  • 执行vmxon(VMON区的物理地址);
  •   ULONG64 InitVmxON(UCHAR CpuIndex, ULONG RevisonId) {
    
          *((PULONG)psVM[CpuIndex].pOnVa) = RevisonId;
    
          if (__vmx_on(&psVM[CpuIndex].pOnPa.QuadPart))
          {
              KdPrint(("vmx_on 失败!\n"));
              return FALSE;
          }
    
          return TRUE;
      }
    

5.设置VMXCS区

  • 5.1.初始化VMCS
  •       ULONG64 InitVmxCS(UCHAR CpuIndex, ULONG RevisonId) {
    
              *((PULONG)psVM[CpuIndex].pCsVa) = RevisonId;
    
              if (__vmx_vmclear(&psVM[CpuIndex].pCsPa.QuadPart))
                  return FALSE;
              if (__vmx_vmptrld(&psVM[CpuIndex].pCsPa.QuadPart))
                  return FALSE;
    
              return TRUE;
          }
    
  • 2.设置GUEST运行环境
    • 控制寄存器:CR0,CR3,CR4
    • 调试寄存器:DR7
    • 段寄存器:ES,FS,DS,CS,SS,GS,TR,LDTR
    • GS基址,FS基址
    • GDT基址,IDT基址
    • GDT段限,IDT段限
    • 状态寄存器:RFLAGS
    • SYSENTER_CS,SYSENTER_ESP,SYSENTER_EIP
    • RIP,RSP
    • 需要保存GUEST当前的RSP和GUEST当前下一条指令的RIP,以便操作完成后回到GUEST的代码流程,就像没有发生过什么事一样
    •   ULONG64 SetGuestState(ULONG64 GuestRip, ULONG64 GuestRsp) {
      
            ULONG64 ret = 0;
      
            ULONG64 GdtBase = _readBaseGDT();
            ULONG64 IdtBase = _readBaseIDT();
      
            ret += __vmx_vmwrite(GUEST_CR0, __readcr0());
            ret += __vmx_vmwrite(GUEST_CR3, __readcr3());
            ret += __vmx_vmwrite(GUEST_CR4, __readcr4());
            ret += __vmx_vmwrite(GUEST_DR7, __readdr(7));
      
            ret += SetGuestSeg(ES, _readES(), GdtBase);
            ret += SetGuestSeg(FS, _readFS(), GdtBase);
            ret += SetGuestSeg(DS, _readDS(), GdtBase);
            ret += SetGuestSeg(CS, _readCS(), GdtBase);
            ret += SetGuestSeg(SS, _readSS(), GdtBase);
            ret += SetGuestSeg(GS, _readGS(), GdtBase);
            ret += SetGuestSeg(TR, _readTR(), GdtBase);
            ret += SetGuestSeg(LDTR, _readLDTR(), GdtBase);
      
            ret += __vmx_vmwrite(GUEST_FS_BASE, __readmsr(MSR_IA32_FS_BASE));
            ret += __vmx_vmwrite(GUEST_GS_BASE, __readmsr(MSR_IA32_GS_BASE));
            ret += __vmx_vmwrite(GUEST_GDTR_BASE, GdtBase);
            ret += __vmx_vmwrite(GUEST_GDTR_LIMIT, _readLimitGDT());
            ret += __vmx_vmwrite(GUEST_IDTR_BASE, IdtBase);
            ret += __vmx_vmwrite(GUEST_IDTR_LIMIT, _readLimitIDT());
      
            ret += __vmx_vmwrite(GUEST_RFLAGS, __readeflags());
            ret += __vmx_vmwrite(GUEST_RIP, GuestRip);
            ret += __vmx_vmwrite(GUEST_RSP, GuestRsp);
      
            ret += __vmx_vmwrite(GUEST_IA32_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS));
            ret += __vmx_vmwrite(GUEST_IA32_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP));
            ret += __vmx_vmwrite(GUEST_IA32_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP));
      
            return ret;
        }
      
  • 3.设置HOST运行环境
    • 控制寄存器:CR0,CR3,CR4
    • 段寄存器:ES,FS,DS,CS,SS,GS,TR
    • GS基址,FS基址,TR基址
    • GDT基址,IDT基址
    • SYSENTER_CS,SYSENTER_ESP,SYSENTER_EIP
    • RIP,RSP
    • HOST的RIP就是VM退出后跳转到VMM的入口地址,所以我们要写一个VMM入口函数,保存VM的现场状态,回到VM后才能继续像什么也没发生过一样继续执行
    •     ULONG64 SetHostState(ULONG64 HostRip, ULONG64 HostRsp) {
      
              SEG seg = { 0 };
              ULONG64 ret = 0;
              ULONG64 GdtBase = _readBaseGDT();
              ULONG64 IdtBase = _readBaseIDT();
              ULONG64 TR = GetSegInfo(&seg, _readTR(), GdtBase);
      
              ret += __vmx_vmwrite(HOST_CR0, __readcr0());
              ret += __vmx_vmwrite(HOST_CR3, __readcr3());
              ret += __vmx_vmwrite(HOST_CR4, __readcr4());
      
              ret += __vmx_vmwrite(HOST_RIP, HostRip);
              ret += __vmx_vmwrite(HOST_RSP, HostRsp);
      
              ret += __vmx_vmwrite(HOST_CS_SELECTOR, _readCS() & 0xF8);
              ret += __vmx_vmwrite(HOST_SS_SELECTOR, _readSS() & 0xF8);
              ret += __vmx_vmwrite(HOST_DS_SELECTOR, _readDS() & 0xF8);
              ret += __vmx_vmwrite(HOST_ES_SELECTOR, _readES() & 0xF8);
              ret += __vmx_vmwrite(HOST_FS_SELECTOR, _readFS() & 0xF8);
              ret += __vmx_vmwrite(HOST_GS_SELECTOR, _readGS() & 0xF8);
              ret += __vmx_vmwrite(HOST_TR_SELECTOR, _readTR() & 0xF8);
      
              ret += __vmx_vmwrite(HOST_FS_BASE, __readmsr(MSR_IA32_FS_BASE));
              ret += __vmx_vmwrite(HOST_GS_BASE, __readmsr(MSR_IA32_GS_BASE));
              ret += __vmx_vmwrite(HOST_TR_BASE, TR ? seg.Base : 0);
              ret += __vmx_vmwrite(HOST_GDTR_BASE, GdtBase);
              ret += __vmx_vmwrite(HOST_IDTR_BASE, IdtBase);
      
              ret += __vmx_vmwrite(HOST_IA32_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS));
              ret += __vmx_vmwrite(HOST_IA32_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP));
              ret += __vmx_vmwrite(HOST_IA32_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP));
      
              return ret;
          }
      
  • 4.设置执行控制
    • 先设置最基本的的条件
    •     ULONG64 SetVmxExecutionCTLS(UCHAR CpuIndex, ULONG64 vmxctrl) {
      
              UCHAR ret = 0;
              ULONG64 value = 0;
      
              MSR_IA32_VMX_PINBASED_CTLS_REGISTER PinBased = { 0 }, SetPinBase = { 0 };
              MSR_IA32_VMX_PROCBASED_CTLS_REGISTER Primary = { 0 }, SetPrimary = { 0 };
              MSR_IA32_VMX_PROCBASED_CTLS2_REGISTER Secondry = { 0 }, SetSecondry = { 0 };
              MSR_IA32_VMX_PROCBASED_CTLS3_REGISTER Tertiary = { 0 };
      
              PinBased.value.QuadPart = vmxctrl ? __readmsr(MSR_IA32_VMX_TRUE_PINBASED_CTLS) : __readmsr(MSR_IA32_VMX_PINBASED_CTLS);
              /*在这里设置《PinBased》*/
      
              value = SetControls(SetPinBase.value.QuadPart, PinBased.value);
              ret += __vmx_vmwrite(PIN_BASED_VM_EXECUTION_CONTROLS, value);
      
              Primary.value.QuadPart = vmxctrl ? __readmsr(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) : __readmsr(MSR_IA32_VMX_PROCBASED_CTLS);
              /*在这里设置《Primary》*/
              SetPrimary.Bits->MSRmap = TRUE;
              SetPrimary.Bits->RDTSC = TRUE;
              SetPrimary.Bits->Secondary = TRUE;
      
              value = SetControls(SetPrimary.value.QuadPart, Primary.value);
              ret += __vmx_vmwrite(PRIMARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, value);
      
              if (SetPrimary.Bits->Secondary)
              {
                  Secondry.value.QuadPart = __readmsr(MSR_IA32_VMX_PROCBASED_CTLS2);
      
                  /*在这里设置《Secondry》*/
                  SetSecondry.Bits->XSAVES_XRSTORS = TRUE;
                  SetSecondry.Bits->INVPCID = TRUE;
                  SetSecondry.Bits->RDTSCP = TRUE;
      
                  value = SetControls(SetSecondry.value.QuadPart, Secondry.value);
                  ret += __vmx_vmwrite(SECONDARY_PROCESSOR_BASED_VM_EXECUTION_CONTROLS, value);
      
              }
              
              ret += __vmx_vmwrite(VMCS_LINK_POINTER_FULL, ~0Ull);
              ret += __vmx_vmwrite(VIRTUAL_PROCESSOR_IDENTIFIER, CpuIndex);
      
              return ret;
          }
      
  • 5.设置VM退出控制
    • 先设置最基本的的条件
    •     ULONG64 SetVmxExitCTLS(UCHAR CpuIndex, ULONG64 vmxctrl) {
      
              ULONG64 ret = 0;            
              ULONG64 value = 0;
              MSR_IA32_VMX_EXIT_CTLS_REGISTER VmExit = { 0 }, SetExit = { 0 };
              VmExit.value.QuadPart = vmxctrl ? __readmsr(MSR_IA32_VMX_TRUE_EXIT_CTLS) : __readmsr(MSR_IA32_VMX_EXIT_CTLS);
              /*在这里设置《VmExit》*/
              SetExit.Bits->HOST_addr_size = TRUE;
      
              value = SetControls(SetExit.value.QuadPart, VmExit.value);
              ret += __vmx_vmwrite(PRIMARY_VM_EXIT_CONTROLS, value);
      
              return ret;
          }
      
  • 6.设置VM进入控制
    • 先设置最基本的的条件
    •     ULONG64 SetVmxEntryCTLS(UCHAR CpuIndex, ULONG64 vmxctrl) {
      
              ULONG64 ret = 0;
              ULONG64 value = 0;
              MSR_IA32_VMX_ENTRY_CTLS_REGISTER VmEntry = { 0 }, SetEntry = { 0 };
              VmEntry.value.QuadPart = vmxctrl ? __readmsr(MSR_IA32_VMX_TRUE_ENTRY_CTLS) : __readmsr(MSR_IA32_VMX_ENTRY_CTLS);
              /*在这里设置《VmEntry》*/
              SetEntry.Bits->Guest_IA32e = TRUE;
      
              value = SetControls(SetEntry.value.QuadPart, VmEntry.value);
              ret += __vmx_vmwrite(VM_ENTRY_CONTROLS, value);
      
              return ret;
          }
      
  • 7.编写VMM处理函数
    • 此函数只做一些准备工作。
    •     ULONG64 HostEntry(PGUESTREG pGuestRegs)
          {
              __vmx_vmread(VIRTUAL_PROCESSOR_IDENTIFIER, &pGuestRegs->CpuIndex);
              __vmx_vmread(EXIT_REASON,  (size_t*) & pGuestRegs->exitReason);
      
              __vmx_vmread(GUEST_RIP, &pGuestRegs->GuestRip);
              __vmx_vmread(GUEST_RSP, &pGuestRegs->GuestRsp);
              __vmx_vmread(GUEST_RFLAGS, &pGuestRegs->GuestFlag);
              __vmx_vmread(VM_EXIT_INSTRUCTION_LENGTH, &pGuestRegs->InstrLen);
      
              pGuestRegs->eProcess = PsGetCurrentProcess();
              pGuestRegs->eThread = PsGetCurrentThread();
      
              return DispatchHandler(pGuestRegs);
          }
      
    • 调用 DispatchHandler 来处理事件。以下只是处理一些必须要处理的指令,CPU不同可能会有差异。处理方法就是查看CPU手册,模仿指令行为。这里就省略了,以免读者看得不清晰
    •     ULONG64 DispatchHandler(PGUESTREG pGuestRegs)
          {
              EXIT_REASON_FIELDS* exit = (EXIT_REASON_FIELDS*)&pGuestRegs->exitReason;
              ULONG64 CpuIndex = pGuestRegs->CpuIndex;
              ULONG64 GuestRip = pGuestRegs->GuestRip;
              ULONG64 GuestRsp = pGuestRegs->GuestRsp;
              ULONG64 InstrLen = pGuestRegs->InstrLen;
      
              switch (exit->Bits.Basic)
              {
              case EXIT_REASON_CPUID: {
                 ...
                  break;
              }
              case EXIT_REASON_RDMSR: {
                 ...
                  break;
              }
              case EXIT_REASON_WRMSR: {
                 ...
                  break;
              }
              case EXIT_REASON_RDTSC: {
                  ...
                  break;
              }
              case EXIT_REASON_RDTSCP: {
                 ...
                  break;
              }                           
              default:
                  DbgBreakPoint();
                  break;
              }
              return TRUE;
          }
      

6.执行VM

  • 执行vmlaunch();
  • 如果上述设置没有出现问题。代码会接着在我们设置好的GUEST.RIP处继续运行。直到触发VM退出条件,即可进入我们设置好的VMM.RIP(HostEntry)处开始执行

<源码下载>

<源码>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小烟囱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值