首页
社区
课程
招聘
[原创]VT虚拟化技术笔记(part 2)
发表于: 2022-3-14 14:47 22585

[原创]VT虚拟化技术笔记(part 2)

2022-3-14 14:47
22585

本文讲解vmcs控制区中Guest state fields和Host state fields字段的填充方法,vm-control fields的相关填充方法将会在下一篇文章中讲解。

image-20220313160323737

在intel白皮书的24.3章中,详细描述了VMCS控制区的字段。如上一篇文章所说,需要设置的vmcs字段为

vm-control fields

除这5个区域之外,vmcs还有一个区域是vm exit信息区域。这个区域是只读的,存储的是vmx指令失败后失败代码的编号。

在《处理器虚拟化技术》3.4章节中,展示了需要填充的字段以及其对应的ID。对于guest区域和host区域,需要填充的字段如下:

长度为16位的字段:

image-20220314094526560

长度为64位的字段

image-20220314094606939

长度为32位的字段

image-20220314094640848

image-20220314094648078

可以看到基本上都是进入guest区域之后的寄存器。其中对段寄存器的填充尤其麻烦,需要分别填充base、attribute、limit、selector。因此需要手动获取段寄存器并将其进行拆分然后进行相应的填充。根据保护模式的知识,段寄存器的结构大致如下

image-20220314094951938

在填充时需要将其拆分并填充,此步骤较为繁琐。

填充guest和host区域时还有四个重要的字段需要获取。分别是进入guest区域之后的rip和rsp以及从guest返回到host区域之后的rip rsp。这里我们要使得进入guest区域之后系统仍然正常进行,还是从原来的地方往下跑。因此要通过函数获取需要返回的上一层函数的返回地址以及rsp。而对于返回host区域之后的host eip,由于从虚拟机中返回一定是发生了vmexit事件,需要对该事件进行处理,因此从虚拟机中返回之后的rip一定要设置为vmexit事件的处理函数。而rsp则需要重新开辟一块内存区域供vmexit事件处理函数使用。如果还是使用guest返回之前的堆栈,则会破坏堆栈中的内容,导致无法预知的结果。

接下来开始具体字段的填充讲解。

和设置vmon区域类似,首先也是申请内存区域然后填充IA32_VMX_BASIC。而在进行基本ID的填充之后,要通过vmclear初始化内存并通过vmptrld选择vmcs区域。这两步操作对应的是前一篇文章说的拔电源以及选中机器。在完成之后,便是最复杂的vmcs字段的填充。这里对于每一块vmcs字段,分别封装一个函数进行初始化。本篇文章讨论的是guest区域和host区域的初始化,分别对应的是 VmxInitGuestVmxInitHost 函数。

对于guest相关的字段,需要传入的是guesteip和guestesp,以用来确定在进入guest虚拟机之后guest从哪里开始跑。其他的字段全部根据当前状态填入。首先需要填入的便是gdt表中各个段寄存器的base、limit、attribute、selector。在对其ID进行观察后可以发现,这些字段的id都是连在一起的,id的值相差2。而对于这些段寄存器,分离base、limit、attribute、selector的方法也非常类似。因此可以考虑将填写段寄存器的属性的方法封装成一个函数。这里将其封装成 fillGdtDataItem 函数。对于各个属性的分离,依照下图来进行。具体分离的细节不赘述,建议仔细读懂代码中切割bit的方法。

image-20220314094951938

对于tr寄存器的gdt表项,并不能像其他寄存器一样进行填充。因为在64位下,tr寄存器的gdt表项是128位的。因此需要单独设置。64位下tr寄存器的gdt表项格式在intel白皮书的7.2.3章节中有解析。其结构如下

image-20220314135406986

因此其需要用另外一套代码进行设置,具体代码如下。思路与其他gdt表项的设置思路相同,都是取出对应的位填入vmcs区域中。

int VmxInitVmOn()
{
    PVMXCPUPCB pVcpu = VmxGetCurrentCPUPCB();
 
    PHYSICAL_ADDRESS lowphys,heiPhy;
 
    lowphys.QuadPart = 0;
    heiPhy.QuadPart = -1;
 
    pVcpu->VmxOnAddr = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
 
    if (!pVcpu->VmxOnAddr)
    {
        //申请内存失败
        return -1;
    }
 
    memset(pVcpu->VmxOnAddr, 0, PAGE_SIZE);
 
    pVcpu->VmxOnAddrPhys = MmGetPhysicalAddress(pVcpu->VmxOnAddr);
 
    //填充ID
    ULONG64 vmxBasic = __readmsr(IA32_VMX_BASIC);
 
    *(PULONG)pVcpu->VmxOnAddr = (ULONG)vmxBasic;
 
    //CR0,CR4
 
    ULONG64 vcr00 = __readmsr(IA32_VMX_CR0_FIXED0);
    ULONG64 vcr01 = __readmsr(IA32_VMX_CR0_FIXED1);
 
    ULONG64 vcr04 = __readmsr(IA32_VMX_CR4_FIXED0);
    ULONG64 vcr14 = __readmsr(IA32_VMX_CR4_FIXED1);
 
    ULONG64 mcr4 = __readcr4();
    ULONG64 mcr0 = __readcr0();
 
    mcr4 |= vcr04;
    mcr4 &= vcr14;
 
    mcr0 |= vcr00;
    mcr0 &= vcr01;
 
    //
    __writecr0(mcr0);
 
    __writecr4(mcr4);
 
    int error = __vmx_on(&pVcpu->VmxOnAddrPhys.QuadPart);
 
    if (error)
    {
        //释放内存,重置CR4
        mcr4 &= ~vcr04;
        __writecr4(mcr4);
        MmFreeContiguousMemorySpecifyCache(pVcpu->VmxOnAddr, PAGE_SIZE, MmCached);
        pVcpu->VmxOnAddr = NULL;
        pVcpu->VmxOnAddrPhys.QuadPart = 0;
    }
 
    return error;
}
int VmxInitVmOn()
{
    PVMXCPUPCB pVcpu = VmxGetCurrentCPUPCB();
 
    PHYSICAL_ADDRESS lowphys,heiPhy;
 
    lowphys.QuadPart = 0;
    heiPhy.QuadPart = -1;
 
    pVcpu->VmxOnAddr = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
 
    if (!pVcpu->VmxOnAddr)
    {
        //申请内存失败
        return -1;
    }
 
    memset(pVcpu->VmxOnAddr, 0, PAGE_SIZE);
 
    pVcpu->VmxOnAddrPhys = MmGetPhysicalAddress(pVcpu->VmxOnAddr);
 
    //填充ID
    ULONG64 vmxBasic = __readmsr(IA32_VMX_BASIC);
 
    *(PULONG)pVcpu->VmxOnAddr = (ULONG)vmxBasic;
 
    //CR0,CR4
 
    ULONG64 vcr00 = __readmsr(IA32_VMX_CR0_FIXED0);
    ULONG64 vcr01 = __readmsr(IA32_VMX_CR0_FIXED1);
 
    ULONG64 vcr04 = __readmsr(IA32_VMX_CR4_FIXED0);
    ULONG64 vcr14 = __readmsr(IA32_VMX_CR4_FIXED1);
 
    ULONG64 mcr4 = __readcr4();
    ULONG64 mcr0 = __readcr0();
 
    mcr4 |= vcr04;
    mcr4 &= vcr14;
 
    mcr0 |= vcr00;
    mcr0 &= vcr01;
 
    //
    __writecr0(mcr0);
 
    __writecr4(mcr4);
 
    int error = __vmx_on(&pVcpu->VmxOnAddrPhys.QuadPart);
 
    if (error)
    {
        //释放内存,重置CR4
        mcr4 &= ~vcr04;
        __writecr4(mcr4);
        MmFreeContiguousMemorySpecifyCache(pVcpu->VmxOnAddr, PAGE_SIZE, MmCached);
        pVcpu->VmxOnAddr = NULL;
        pVcpu->VmxOnAddrPhys.QuadPart = 0;
    }
 
    return error;
}
PULONG64 retAddr = (PULONG64)_AddressOfReturnAddress();
ULONG64 guestEsp = retAddr + 1;
ULONG64 guestEip = *retAddr;
PULONG64 retAddr = (PULONG64)_AddressOfReturnAddress();
ULONG64 guestEsp = retAddr + 1;
ULONG64 guestEip = *retAddr;
int VmxInit(ULONG64 hostEip)
{
 
    PVMXCPUPCB pVcpu = VmxGetCurrentCPUPCB();
 
    pVcpu->cpuNumber = KeGetCurrentProcessorNumberEx(NULL);
 
    PULONG64 retAddr = (PULONG64)_AddressOfReturnAddress();
    ULONG64 guestEsp = retAddr + 1;
    ULONG64 guestEip = *retAddr;
 
    int error = VmxInitVmOn();
 
    if (error)
    {
        DbgPrintEx(77, 0, "[db]:vmon 初始化失败 error = %d,cpunumber %d\r\n", error, pVcpu->cpuNumber);
 
        return error;
    }
 
    error = VmxInitVmcs(guestEip, guestEsp, hostEip);
 
    if (error)
    {
        DbgPrintEx(77, 0, "[db]:vmcs 初始化失败 error = %d,cpunumber %d\r\n", error, pVcpu->cpuNumber);
 
 
        VmxDestory();
        return error;
    }
 
    return 0;
}
int VmxInit(ULONG64 hostEip)
{
 
    PVMXCPUPCB pVcpu = VmxGetCurrentCPUPCB();
 
    pVcpu->cpuNumber = KeGetCurrentProcessorNumberEx(NULL);
 
    PULONG64 retAddr = (PULONG64)_AddressOfReturnAddress();
    ULONG64 guestEsp = retAddr + 1;
    ULONG64 guestEip = *retAddr;
 
    int error = VmxInitVmOn();
 
    if (error)
    {
        DbgPrintEx(77, 0, "[db]:vmon 初始化失败 error = %d,cpunumber %d\r\n", error, pVcpu->cpuNumber);
 
        return error;
    }
 
    error = VmxInitVmcs(guestEip, guestEsp, hostEip);
 
    if (error)
    {
        DbgPrintEx(77, 0, "[db]:vmcs 初始化失败 error = %d,cpunumber %d\r\n", error, pVcpu->cpuNumber);
 
 
        VmxDestory();
        return error;
    }
 
    return 0;
}
int VmxInitVmcs(ULONG64 GuestEip,ULONG64 GuestEsp, ULONG64 hostEip)
{
    PVMXCPUPCB pVcpu = VmxGetCurrentCPUPCB();
 
    PHYSICAL_ADDRESS lowphys, heiPhy;
 
    lowphys.QuadPart = 0;
    heiPhy.QuadPart = -1;
 
    pVcpu->VmxcsAddr = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
 
    if (!pVcpu->VmxcsAddr)
    {
        //申请内存失败
        return -1;
    }
 
    memset(pVcpu->VmxcsAddr, 0, PAGE_SIZE);
 
    pVcpu->VmxcsAddrPhys = MmGetPhysicalAddress(pVcpu->VmxcsAddr);
 
 
    pVcpu->VmxHostStackTop = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE * 36, lowphys, heiPhy, lowphys, MmCached);
 
    if (!pVcpu->VmxHostStackTop)
    {
        //申请内存失败
 
        return -1;
    }
 
    memset(pVcpu->VmxHostStackTop, 0, PAGE_SIZE * 36);
 
    pVcpu->VmxHostStackBase = (ULONG64)pVcpu->VmxHostStackTop + PAGE_SIZE * 36 - 0x200;
 
    //填充ID
    ULONG64 vmxBasic = __readmsr(IA32_VMX_BASIC);
 
    *(PULONG)pVcpu->VmxcsAddr = (ULONG)vmxBasic;
 
    //加载VMCS
    __vmx_vmclear(&pVcpu->VmxcsAddrPhys.QuadPart);
 
    __vmx_vmptrld(&pVcpu->VmxcsAddrPhys.QuadPart);
 
    VmxInitGuest(GuestEip, GuestEsp);
 
    VmxInitHost(hostEip);
}
int VmxInitVmcs(ULONG64 GuestEip,ULONG64 GuestEsp, ULONG64 hostEip)
{
    PVMXCPUPCB pVcpu = VmxGetCurrentCPUPCB();
 
    PHYSICAL_ADDRESS lowphys, heiPhy;
 
    lowphys.QuadPart = 0;
    heiPhy.QuadPart = -1;
 
    pVcpu->VmxcsAddr = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, lowphys, heiPhy, lowphys, MmCached);
 
    if (!pVcpu->VmxcsAddr)
    {
        //申请内存失败
        return -1;
    }
 
    memset(pVcpu->VmxcsAddr, 0, PAGE_SIZE);
 
    pVcpu->VmxcsAddrPhys = MmGetPhysicalAddress(pVcpu->VmxcsAddr);
 
 
    pVcpu->VmxHostStackTop = MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE * 36, lowphys, heiPhy, lowphys, MmCached);
 
    if (!pVcpu->VmxHostStackTop)
    {
        //申请内存失败
 
        return -1;
    }
 
    memset(pVcpu->VmxHostStackTop, 0, PAGE_SIZE * 36);
 
    pVcpu->VmxHostStackBase = (ULONG64)pVcpu->VmxHostStackTop + PAGE_SIZE * 36 - 0x200;
 
    //填充ID
    ULONG64 vmxBasic = __readmsr(IA32_VMX_BASIC);
 
    *(PULONG)pVcpu->VmxcsAddr = (ULONG)vmxBasic;
 
    //加载VMCS
    __vmx_vmclear(&pVcpu->VmxcsAddrPhys.QuadPart);
 
    __vmx_vmptrld(&pVcpu->VmxcsAddrPhys.QuadPart);
 
    VmxInitGuest(GuestEip, GuestEsp);
 
    VmxInitHost(hostEip);
}
void fillGdtDataItem(int index,short selector)
{
    GdtTable gdtTable = {0};
    AsmGetGdtTable(&gdtTable);
 
    selector &= 0xFFF8;
 
    ULONG limit = __segmentlimit(selector);
    PULONG item = (PULONG)(gdtTable.Base + selector);
 
    LARGE_INTEGER itemBase = {0};
    itemBase.LowPart = (*item & 0xFFFF0000) >> 16;
    item += 1;
    itemBase.LowPart |= (*item & 0xFF000000) | ((*item & 0xFF) << 16);
 
    //属性
    ULONG attr = (*item & 0x00F0FF00) >> 8;
 
    if (selector == 0)
    {
        attr |= 1 << 16;
    }
 
    __vmx_vmwrite(GUEST_ES_BASE + index * 2, itemBase.QuadPart);
    __vmx_vmwrite(GUEST_ES_LIMIT + index * 2, limit);
    __vmx_vmwrite(GUEST_ES_AR_BYTES + index * 2, attr);
    __vmx_vmwrite(GUEST_ES_SELECTOR + index * 2, selector);
}
void fillGdtDataItem(int index,short selector)
{
    GdtTable gdtTable = {0};
    AsmGetGdtTable(&gdtTable);
 
    selector &= 0xFFF8;
 
    ULONG limit = __segmentlimit(selector);
    PULONG item = (PULONG)(gdtTable.Base + selector);
 
    LARGE_INTEGER itemBase = {0};
    itemBase.LowPart = (*item & 0xFFFF0000) >> 16;
    item += 1;
    itemBase.LowPart |= (*item & 0xFF000000) | ((*item & 0xFF) << 16);
 
    //属性
    ULONG attr = (*item & 0x00F0FF00) >> 8;
 
    if (selector == 0)
    {
        attr |= 1 << 16;
    }
 
    __vmx_vmwrite(GUEST_ES_BASE + index * 2, itemBase.QuadPart);
    __vmx_vmwrite(GUEST_ES_LIMIT + index * 2, limit);
    __vmx_vmwrite(GUEST_ES_AR_BYTES + index * 2, attr);
    __vmx_vmwrite(GUEST_ES_SELECTOR + index * 2, selector);
}
GdtTable gdtTable = { 0 };
AsmGetGdtTable(&gdtTable);
 
ULONG trSelector = AsmReadTR();
 
trSelector &= 0xFFF8;
ULONG trlimit = __segmentlimit(trSelector);
 
LARGE_INTEGER trBase = {0};
 
PULONG trItem = (PULONG)(gdtTable.Base + trSelector);
 
//读TR
trBase.LowPart = ((trItem[0] >> 16) & 0xFFFF) | ((trItem[1] & 0xFF) << 16) | ((trItem[1] & 0xFF000000));
trBase.HighPart = trItem[2];
 
//属性
ULONG attr = (trItem[1] & 0x00F0FF00) >> 8;
__vmx_vmwrite(GUEST_TR_BASE, trBase.QuadPart);
__vmx_vmwrite(GUEST_TR_LIMIT, trlimit);
__vmx_vmwrite(GUEST_TR_AR_BYTES, attr);
__vmx_vmwrite(GUEST_TR_SELECTOR, trSelector);
GdtTable gdtTable = { 0 };
AsmGetGdtTable(&gdtTable);
 
ULONG trSelector = AsmReadTR();
 
trSelector &= 0xFFF8;
ULONG trlimit = __segmentlimit(trSelector);
 
LARGE_INTEGER trBase = {0};
 
PULONG trItem = (PULONG)(gdtTable.Base + trSelector);
 
//读TR
trBase.LowPart = ((trItem[0] >> 16) & 0xFFFF) | ((trItem[1] & 0xFF) << 16) | ((trItem[1] & 0xFF000000));
trBase.HighPart = trItem[2];
 
//属性
ULONG attr = (trItem[1] & 0x00F0FF00) >> 8;
__vmx_vmwrite(GUEST_TR_BASE, trBase.QuadPart);
__vmx_vmwrite(GUEST_TR_LIMIT, trlimit);
__vmx_vmwrite(GUEST_TR_AR_BYTES, attr);
__vmx_vmwrite(GUEST_TR_SELECTOR, trSelector);
__vmx_vmwrite(GUEST_CR0, __readcr0());
__vmx_vmwrite(GUEST_CR4, __readcr4());
__vmx_vmwrite(GUEST_CR3, __readcr3());
__vmx_vmwrite(GUEST_DR7, __readdr(7));
__vmx_vmwrite(GUEST_RFLAGS, __readeflags());
__vmx_vmwrite(GUEST_RSP, GuestEsp);
__vmx_vmwrite(GUEST_RIP, GuestEip);
 
__vmx_vmwrite(VMCS_LINK_POINTER, -1);
 
__vmx_vmwrite(GUEST_IA32_DEBUGCTL, __readmsr(IA32_MSR_DEBUGCTL));
__vmx_vmwrite(GUEST_IA32_PAT, __readmsr(IA32_MSR_PAT));
__vmx_vmwrite(GUEST_IA32_EFER, __readmsr(IA32_MSR_EFER));
 
__vmx_vmwrite(GUEST_FS_BASE, __readmsr(IA32_FS_BASE));
__vmx_vmwrite(GUEST_GS_BASE, __readmsr(IA32_GS_BASE));
 
__vmx_vmwrite(GUEST_SYSENTER_CS, __readmsr(0x174));
__vmx_vmwrite(GUEST_SYSENTER_ESP, __readmsr(0x175));
__vmx_vmwrite(GUEST_SYSENTER_EIP, __readmsr(0x176));
 
 
//IDT GDT
 
GdtTable idtTable;
__sidt(&idtTable);
 
__vmx_vmwrite(GUEST_GDTR_BASE, gdtTable.Base);
__vmx_vmwrite(GUEST_GDTR_LIMIT, gdtTable.limit);
__vmx_vmwrite(GUEST_IDTR_LIMIT, idtTable.limit);
__vmx_vmwrite(GUEST_IDTR_BASE, idtTable.Base);
__vmx_vmwrite(GUEST_CR0, __readcr0());
__vmx_vmwrite(GUEST_CR4, __readcr4());
__vmx_vmwrite(GUEST_CR3, __readcr3());
__vmx_vmwrite(GUEST_DR7, __readdr(7));
__vmx_vmwrite(GUEST_RFLAGS, __readeflags());
__vmx_vmwrite(GUEST_RSP, GuestEsp);
__vmx_vmwrite(GUEST_RIP, GuestEip);
 
__vmx_vmwrite(VMCS_LINK_POINTER, -1);

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2022-3-17 21:21 被smallzhong_编辑 ,原因:
收藏
免费 5
支持
分享
打赏 + 50.00雪花
打赏次数 1 雪花 + 50.00
 
赞赏  Editor   +50.00 2022/03/28 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (3)
雪    币: 9
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
2022-3-22 06:28
0
雪    币: 1435
活跃值: (4508)
能力值: ( LV9,RANK:220 )
在线值:
发帖
回帖
粉丝
3
很棒
2022-3-22 11:36
0
雪    币: 2
活跃值: (1116)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
2022-10-17 09:38
0
游客
登录 | 注册 方可回帖
返回
//