魔鬼天使 发表于 2017-6-1 12:17:34

VT系列:VMCS表填写



一共有5章表要填 分别是:
1.Guest State Area (客户机) 填写虚拟机相关的信息
2.Host State Area(宿主机) 填写真实机的相关信息
3.VM-Execution Control Fields(虚拟机运行控制域) 定义了我们的VT能拦截什么东西 指令 异常 操作。
4.VMEntry Control Fields(VMENTRY行为控制域) 写死的 只有x86 x64的区别
5.VMExit Control Fields(VMEXIT行为控制域) 写死的 只有x86 x64的区别

在填写之前需要注意一件事 就是当我们使用VMLanuch指令的时候 在VMLanuch下面的指令是执行不到的了

比如:
VMLanuch
Mov eax,1
Ret

如果VMLanuch(VVMCALL后VMOFF)成功那么Mov eax,1 和Ret是不会执行了,因为这个时候已经跑到虚拟机里面去执行了
那么我们需要得到Mov eax,1的地址当调用VMLanuch(VMCALL后VMOFF)之后让虚拟机的EIP指向mov eax,1的地址继续执行后面的代码

在填写VMCS中有一部分是麻烦的 NewBuilePill里面将这块封装成了模块
这里我们直接使用Newbluepill里面的代码
Common.cpp 和 common.h


步骤为:
1.判断处理器是否支持虚拟化
2.申请VMXON和VMCS的内存区域
3.设置版本号信息以及开启虚拟机汇编指令
4.保存客户机原始寄存器、填写VMCS表项
5.测试
6.关闭虚拟化的代码 调用VMCall 设置返回EIP 关闭虚拟化指令(CR4) 释放申请到的内存

这些需要一个结构保存申请到的VMXON VMCS的内存地址
typedef struct _VMX_CPU
{
         PVOIDpVMXONRegion; //VMXON的虚拟地址
         PHYSICAL_ADDRESSpVMXONRegion_PA; //VMXON的物理地址
         PVOIDpVMCSRegion; //VMCS的虚拟地址
         PHYSICAL_ADDRESSpVMCSRegion_PA; //VMCS的物理地址
         PVOIDpHostEsp; //主机的Esp

         BOOLEANbVTStartSuccess; //用于记录是否开启成功
}VMX_CPU,*PVMX_CPU;

增加两个函数
开启VT和关闭VT
NTSTATUS StartVirtualTechnology();
NTSTATUS StopVirtualTechnology();
实现如下:
#include "stdafx.h"
#include "vtsystem.h"
#include "vtasm.h"
#include "exithandler.h"
#include "common.h"

VMX_CPU g_VMXCPU;

NTSTATUS AllocateVMXRegion()
{
        PVOID pVMXONRegion;
        PVOID pVMCSRegion;
        PVOID pHostEsp;

        //必须4kb
        pVMXONRegion = ExAllocatePoolWithTag(NonPagedPool,0x1000,'vmon'); //4KB
        if (!pVMXONRegion)
        {
                Log("ERROR:申请VMXON内存区域失败!",0);
                return STATUS_MEMORY_NOT_ALLOCATED;
        }
        //最好zero
        RtlZeroMemory(pVMXONRegion,0x1000);

        //4kb就够了
        pVMCSRegion = ExAllocatePoolWithTag(NonPagedPool,0x1000,'vmcs');
        if (!pVMCSRegion)
        {
                Log("ERROR:申请VMCS内存区域失败!",0);
                ExFreePoolWithTag(pVMXONRegion,0x1000);
                return STATUS_MEMORY_NOT_ALLOCATED;
        }
        //最好zero
        RtlZeroMemory(pVMCSRegion,0x1000);

        //用于给保存寄存器时当栈 0x2000后面会说
        pHostEsp = ExAllocatePoolWithTag(NonPagedPool,0x2000,'mini');
        if (!pHostEsp)
        {
                Log("ERROR:申请宿主机堆载区域失败!",0);
                ExFreePoolWithTag(pVMXONRegion,0x1000);
                ExFreePoolWithTag(pVMCSRegion,0x1000);
                return STATUS_MEMORY_NOT_ALLOCATED;
        }
        RtlZeroMemory(pHostEsp,0x2000);

        Log("TIP:VMXON内存区域地址",pVMXONRegion);
        Log("TIP:VMCS内存区域地址",pVMCSRegion);
        Log("TIP:宿主机堆载区域地址",pHostEsp);

        g_VMXCPU.pVMXONRegion = pVMXONRegion;
        g_VMXCPU.pVMXONRegion_PA = MmGetPhysicalAddress(pVMXONRegion);
        g_VMXCPU.pVMCSRegion = pVMCSRegion;
        g_VMXCPU.pVMCSRegion_PA = MmGetPhysicalAddress(pVMCSRegion);
        g_VMXCPU.pHostEsp = pHostEsp;
        return STATUS_SUCCESS;
}

//填写版本号信息和开启虚拟化汇编指令
void SetupVMXRegion()
{
        VMX_BASIC_MSR Msr;
        ULONG uRevId;
        _CR4 uCr4;
        _EFLAGS uEflags;

        RtlZeroMemory(&Msr,sizeof(Msr));

        *((PULONG)&Msr) = Asm_ReadMsr(MSR_IA32_VMX_BASIC);
        uRevId = Msr.RevId;

        //将Revid写入申请的到VMXON VMCS的前4个字节
        *((PULONG)g_VMXCPU.pVMXONRegion) = uRevId;
        *((PULONG)g_VMXCPU.pVMCSRegion) = uRevId;

        Log("TIP:VMX版本号信息",uRevId);

        //开启虚拟化汇编指令
        *((PULONG)&uCr4) = Asm_GetCr4();
        uCr4.VMXE = 1;
        Asm_SetCr4(*((PULONG)&uCr4));

        //调用VmxOn指令启动虚拟化指令
        Vmx_VmxOn(g_VMXCPU.pVMXONRegion_PA.LowPart,g_VMXCPU.pVMXONRegion_PA.HighPart);

        //检测Vmxon指令是否成功 见intel手册3.15
        *((PULONG)&uEflags) = Asm_GetEflags();
        if (uEflags.CF != 0)
        {
                Log("ERROR:VMXON指令调用失败!",0);
                return;
        }
        Log("SUCCESS:VMXON指令调用成功!",0);
}

extern "C" void SetupVMCS()
{
        _EFLAGS uEflags;
        ULONG GdtBase,IdtBase;
        SEGMENT_SELECTOR SegmentSelector;
        ULONG uCPUBase,uExceptionBitmap;

        //需要Clear一下VMCS的物理地址
        Vmx_VmClear(g_VMXCPU.pVMCSRegion_PA.LowPart,g_VMXCPU.pVMCSRegion_PA.HighPart);
        //检测是否Clear成功
        *((PULONG)&uEflags) = Asm_GetEflags();
        if (uEflags.CF != 0 || uEflags.ZF != 0)
        {
                Log("ERROR:VMCLEAR指令调用失败!",0);
                return;
        }
        Log("SUCCESS:VMCLEAR指令调用成功!",0);
        //设置VMCS的物理地址???
        Vmx_VmPtrld(g_VMXCPU.pVMCSRegion_PA.LowPart,g_VMXCPU.pVMCSRegion_PA.HighPart);

        //得到GDT IDT表Base 后面填表时需要用到
        GdtBase = Asm_GetGdtBase();
        IdtBase = Asm_GetIdtBase();

        //
        // 1.Guest State Area
        //

        /*
        需要填写以下信息
        GUEST_CR0                        AsmGetCr0
        GUEST_CR3                        AsmGetCr3
        GUEST_CR4                        AsmGetCr4

        GUEST_DR7                        0x400
        GUEST_RFLAGS                Asm_GetEflags

        GdtBase,ES FS DS CS SS GS TR LDTR 这里使用了NewBluePill里面的代码FillGuestSelectorData

        GUEST_GDTR_BASE                GdtBast
        GUEST_GDTR_LIMIT    Asm_GetGdtLimit
        GUEST_IDTR_BASE                IdtBase
        GUEST_IDTR_LIMIT        Asm_GetIdtLimit

        GUEST_IA32_DEBUGCTL       
        GUEST_IA32_DEBUGCTL_HIGH
       
        GUEST_SYSENTER_CS
        GUEST_SYSENTER_ESP
        GUEST_SYSENTER_EIP   //这个就是KiFastCallEntry ddvp里面就是修改了这个达到修改内核入口的目的

        GUEST_RSP                在保存寄存器时保存的GuestEsp因为汇编里面的变量不能直接在CPP中使用 这里编写了一个汇编函数返回guest esp
        Asm_GetGuestESP Proc
                mov eax,GuestESP
                ret
        Asm_GetGuestESP Endp

        GUEST_RIP      在调用VmLanuch后要执行的EIP这里必须让其返回到驱动加载时的EIP 否则BIOS或卡死
        Asm_GetGuestReturn Proc
                mov eax,GuestReturn
                ret
        Asm_GetGuestReturn Endp


        //必须要加上的 不知道干什么的 不加蓝屏或卡死
        GUEST_INTERRUPTIBILITY_INFO0
        GUEST_ACTIVITY_STATE         0
        VMCS_LINK_POINTER                        0xffffffff
        VMCS_LINK_POINTER_HIGH                0xffffffff
        */
        Vmx_VmWrite(GUEST_CR0,Asm_GetCr0());
        Vmx_VmWrite(GUEST_CR3,Asm_GetCr3());
        Vmx_VmWrite(GUEST_CR4,Asm_GetCr4());

        Vmx_VmWrite(GUEST_DR7,0x400);
        Vmx_VmWrite(GUEST_RFLAGS,Asm_GetEflags());

        FillGuestSelectorData(GdtBase,ES,Asm_GetEs());
        FillGuestSelectorData(GdtBase,FS,Asm_GetFs());
        FillGuestSelectorData(GdtBase,DS,Asm_GetDs());
        FillGuestSelectorData(GdtBase,CS,Asm_GetCs());
        FillGuestSelectorData(GdtBase,SS,Asm_GetSs());
        FillGuestSelectorData(GdtBase,GS,Asm_GetGs());
        FillGuestSelectorData(GdtBase,TR,Asm_GetTr());
        FillGuestSelectorData(GdtBase,LDTR,Asm_GetLdtr());

        Vmx_VmWrite(GUEST_GDTR_BASE,GdtBase);
        Vmx_VmWrite(GUEST_GDTR_LIMIT,Asm_GetGdtLimit());
        Vmx_VmWrite(GUEST_IDTR_BASE,IdtBase);
        Vmx_VmWrite(GUEST_IDTR_LIMIT,Asm_GetIdtLimit());

        Vmx_VmWrite(GUEST_IA32_DEBUGCTL,Asm_ReadMsr(MSR_IA32_DEBUGCTL)&0xFFFFFFFF);
        Vmx_VmWrite(GUEST_IA32_DEBUGCTL_HIGH,Asm_ReadMsr(MSR_IA32_DEBUGCTL)>>32);

        Vmx_VmWrite(GUEST_SYSENTER_CS,Asm_ReadMsr(MSR_IA32_SYSENTER_CS)&0xFFFFFFFF);
        Vmx_VmWrite(GUEST_SYSENTER_ESP,Asm_ReadMsr(MSR_IA32_SYSENTER_ESP)&0xFFFFFFFF);
        Vmx_VmWrite(GUEST_SYSENTER_EIP,Asm_ReadMsr(MSR_IA32_SYSENTER_EIP)&0xFFFFFFFF); // KiFastCallEntry

        Vmx_VmWrite(GUEST_RSP,Asm_GetGuestESP());
        Vmx_VmWrite(GUEST_RIP,Asm_GetGuestReturn());// 指定vmlaunch客户机的入口点 这里我们让客户机继续执行加载驱动的代码

        Vmx_VmWrite(GUEST_INTERRUPTIBILITY_INFO, 0);
        Vmx_VmWrite(GUEST_ACTIVITY_STATE, 0);
        Vmx_VmWrite(VMCS_LINK_POINTER, 0xffffffff);
        Vmx_VmWrite(VMCS_LINK_POINTER_HIGH, 0xffffffff);

        //
        // 2.Host State Area
        //
        /*
        大部分同上
        需要关注2点
        一个是HOST_RSP这个是给保存寄存器时用的栈 就是我们申请的hostEsp 这里它是反着用的 所以要加到尾部
        另一个是Host_RIP 这个就是定义我们的VMM处理程序的入口 当发生退出事件时 会调用这个地方
        */
        Vmx_VmWrite(HOST_CR0,Asm_GetCr0());
        Vmx_VmWrite(HOST_CR3,Asm_GetCr3());
        Vmx_VmWrite(HOST_CR4,Asm_GetCr4());

        Vmx_VmWrite(HOST_ES_SELECTOR,Asm_GetEs() & 0xFFF8);
        Vmx_VmWrite(HOST_CS_SELECTOR,Asm_GetCs() & 0xFFF8);
        Vmx_VmWrite(HOST_DS_SELECTOR,Asm_GetDs() & 0xFFF8);
        Vmx_VmWrite(HOST_FS_SELECTOR,Asm_GetFs() & 0xFFF8);
        Vmx_VmWrite(HOST_GS_SELECTOR,Asm_GetGs() & 0xFFF8);
        Vmx_VmWrite(HOST_SS_SELECTOR,Asm_GetSs() & 0xFFF8);
        Vmx_VmWrite(HOST_TR_SELECTOR,Asm_GetTr() & 0xFFF8);

        InitializeSegmentSelector(&SegmentSelector,Asm_GetFs(),GdtBase);
        Vmx_VmWrite(HOST_FS_BASE,SegmentSelector.base);
        InitializeSegmentSelector(&SegmentSelector,Asm_GetGs(),GdtBase);
        Vmx_VmWrite(HOST_GS_BASE,SegmentSelector.base);
        InitializeSegmentSelector(&SegmentSelector,Asm_GetTr(),GdtBase);
        Vmx_VmWrite(HOST_TR_BASE,SegmentSelector.base);

        Vmx_VmWrite(HOST_GDTR_BASE,GdtBase);
        Vmx_VmWrite(HOST_IDTR_BASE,IdtBase);

        Vmx_VmWrite(HOST_IA32_SYSENTER_CS,Asm_ReadMsr(MSR_IA32_SYSENTER_CS)&0xFFFFFFFF);
        Vmx_VmWrite(HOST_IA32_SYSENTER_ESP,Asm_ReadMsr(MSR_IA32_SYSENTER_ESP)&0xFFFFFFFF);
        Vmx_VmWrite(HOST_IA32_SYSENTER_EIP,Asm_ReadMsr(MSR_IA32_SYSENTER_EIP)&0xFFFFFFFF); // KiFastCallEntry

        Vmx_VmWrite(HOST_RSP,((ULONG)g_VMXCPU.pHostEsp) + 0x1FFF);//8KB 0x2000
        Vmx_VmWrite(HOST_RIP,(ULONG)&Asm_VMMEntryPoint);//这里定义我们的VMM处理程序入口

        //
        // 3.虚拟机运行控制域
        //

        /*
        最重要的地方
        这个控制域决定了这个VT能干什么
        前4个是和最后5个CR3必须的

        要添加拦截什么操作
        需要从MSR_IA32_VMX_PROCBASED_CTLS中得到一个数
        跟这个数做与运算即可
        完事后 使用VMwrite写到CPU_BASED_VM_EXEC_CONTROL即可
        */
        Vmx_VmWrite(PIN_BASED_VM_EXEC_CONTROL,VmxAdjustControls(0,MSR_IA32_VMX_PINBASED_CTLS));

        Vmx_VmWrite(PAGE_FAULT_ERROR_CODE_MASK,0);
        Vmx_VmWrite(PAGE_FAULT_ERROR_CODE_MATCH,0);
        Vmx_VmWrite(TSC_OFFSET,0);
        Vmx_VmWrite(TSC_OFFSET_HIGH,0);

        uCPUBase = VmxAdjustControls(0,MSR_IA32_VMX_PROCBASED_CTLS);

        //uCPUBase |= CPU_BASED_MOV_DR_EXITING; // 拦截调试寄存器操作
        //uCPUBase |= CPU_BASED_USE_IO_BITMAPS; // 拦截键盘鼠标消息
        //uCPUBase |= CPU_BASED_ACTIVATE_MSR_BITMAP; // 拦截MSR操作
        //................
       
        Vmx_VmWrite(CPU_BASED_VM_EXEC_CONTROL,uCPUBase);

        /*
        Vmx_VmWrite(IO_BITMAP_A,0);
        Vmx_VmWrite(IO_BITMAP_A_HIGH,0);
        Vmx_VmWrite(IO_BITMAP_B,0);
        Vmx_VmWrite(IO_BITMAP_B_HIGH,0);
        */

        Vmx_VmWrite(CR3_TARGET_COUNT,0);
        Vmx_VmWrite(CR3_TARGET_VALUE0,0);
        Vmx_VmWrite(CR3_TARGET_VALUE1,0);
        Vmx_VmWrite(CR3_TARGET_VALUE2,0);
        Vmx_VmWrite(CR3_TARGET_VALUE3,0);

        //
        // 4.VMEntry运行控制域
        //

        /*
        4-5 是固定不变的 只有x86和x64的区别
        */
        Vmx_VmWrite(VM_ENTRY_CONTROLS,VmxAdjustControls(0,MSR_IA32_VMX_ENTRY_CTLS));
        Vmx_VmWrite(VM_ENTRY_MSR_LOAD_COUNT,0);
        Vmx_VmWrite(VM_ENTRY_INTR_INFO_FIELD,0);


        //
        // 5.VMExit运行控制域
        //
        Vmx_VmWrite(VM_EXIT_CONTROLS,VmxAdjustControls(VM_EXIT_ACK_INTR_ON_EXIT,MSR_IA32_VMX_EXIT_CTLS));
        Vmx_VmWrite(VM_EXIT_MSR_LOAD_COUNT,0);
        Vmx_VmWrite(VM_EXIT_MSR_STORE_COUNT,0);


        //填写完后调用VmLaunch启动虚拟机
        Vmx_VmLaunch();

        //如果成功 这里永远不会被执行
        g_VMXCPU.bVTStartSuccess = FALSE;

        Log("ERROR:VmLaunch指令调用失败!",Vmx_VmRead(VM_INSTRUCTION_ERROR));
}

NTSTATUS StartVirtualTechnology()
{
        NTSTATUS status = STATUS_SUCCESS;
        //检测是否支持虚拟化
        if (!IsVTEnabled())
                return STATUS_NOT_SUPPORTED;
        //申请VMXON VMCS内存区域
        status = AllocateVMXRegion();
        if (!NT_SUCCESS(status))
        {
                Log("ERROR:VMX内存区域申请失败",0);
                return STATUS_UNSUCCESSFUL;
        }
        Log("SUCCESS:VMX内存区域申请成功!",0);

        //填写版本号信息和开启虚拟化汇编指令VMXON
        SetupVMXRegion();
        g_VMXCPU.bVTStartSuccess = TRUE;

        //保存客户机寄存器、得到开启虚拟化后要执行的EIP(也就是前面说的Vmlanuch问题)、填写VMCS表
        //在保存好客户机寄存器 和返回地址后会调用SetupVMCS
        Asm_SetupVMCS();

        if (g_VMXCPU.bVTStartSuccess)
        {
                Log("SUCCESS:开启VT成功!",0);
                Log("SUCCESS:现在这个CPU进入了VMX模式.",0);
                return STATUS_SUCCESS;
        }
        else Log("ERROR:开启VT失败!",0);
        return STATUS_UNSUCCESSFUL;
}

NTSTATUS StopVirtualTechnology()
{
        _CR4 uCr4;
        if(g_VMXCPU.bVTStartSuccess)
        {
                //会进入我们的处理函数
                Vmx_VmCall('SVT');
               
                *((PULONG)&uCr4) = Asm_GetCr4();
                uCr4.VMXE = 0;
                Asm_SetCr4(*((PULONG)&uCr4));

                ExFreePoolWithTag(g_VMXCPU.pVMXONRegion,'vmon');
                ExFreePoolWithTag(g_VMXCPU.pVMCSRegion,'vmcs');
                ExFreePoolWithTag(g_VMXCPU.pHostEsp,'mini');

                Log("SUCCESS:关闭VT成功!",0);
                Log("SUCCESS:现在这个CPU退出了VMX模式.",0);
        }

        return STATUS_SUCCESS;
}

BOOLEAN IsVTEnabled()
{
        ULONG uRet_EAX,uRet_ECX,uRet_EDX,uRet_EBX;
        _CPUID_ECX uCPUID;
        _CR0 uCr0;
        _CR4 uCr4;
        IA32_FEATURE_CONTROL_MSR msr;
        //1. CPUID
        Asm_CPUID(1,&uRet_EAX,&uRet_EBX,&uRet_ECX,&uRet_EDX);
        *((PULONG)&uCPUID) = uRet_ECX;

        if (uCPUID.VMX != 1)
        {
                Log("ERROR:这个CPU不支持VT!",0);
                return FALSE;
        }

        // 2. CR0 CR4
        *((PULONG)&uCr0) = Asm_GetCr0();
        *((PULONG)&uCr4) = Asm_GetCr4();

        if (uCr0.PE != 1||uCr0.PG!=1||uCr0.NE!=1)
        {
                Log("ERROR:这个CPU没有开启VT!",0);
                return FALSE;
        }

        if (uCr4.VMXE == 1)
        {
                Log("ERROR:这个CPU已经开启了VT!",0);
                Log("可能是别的驱动已经占用了VT,你必须关闭它后才能开启。",0);
                return FALSE;
        }

        // 3. MSR
        *((PULONG)&msr) = Asm_ReadMsr(MSR_IA32_FEATURE_CONTROL);
        if (msr.Lock!=1)
        {
                Log("ERROR:VT指令未被锁定!",0);
                return FALSE;
        }
        Log("SUCCESS:这个CPU支持VT!",0);
        return TRUE;
}

汇编代码实现:
.686p
.model flat, stdcall
option casemap:none

GetGuestRegsAddress Proto
VMMEntryPoint Proto
SetupVMCS Proto

.data
GuestESP dword ?
GuestReturn dword ?

EntryEAX dword ?
EntryECX dword ?
EntryEDX dword ?
EntryEBX dword ?
EntryESP dword ?
EntryEBP dword ?
EntryESI dword ?
EntryEDI dword ?
EntryEflags dword ?

.code

Asm_CPUID        Proc        uses ebx esi edi fn:dword, ret_eax:dword,ret_ebx:dword,ret_ecx:dword, ret_edx:dword
      mov        eax, fn
      cpuid
      mov        esi, ret_eax
      mov        dword ptr , eax
      mov        esi, ret_ebx
      mov        dword ptr , ebx
      mov        esi, ret_ecx
      mov        dword ptr , ecx
      mov        esi, ret_edx
      mov        dword ptr , edx
      ret
Asm_CPUID         Endp


Asm_ReadMsr                Proc        Index:dword
        mov        ecx,Index
        rdmsr
        ret
Asm_ReadMsr                Endp

Asm_WriteMsr        Proc        Index:dword,LowPart,HighPart       
        mov        ecx, Index
        mov        eax, LowPart
        mov        edx, HighPart
        wrmsr
        ret
Asm_WriteMsr         Endp

Asm_ReadMsrEx                Proc        Index:dword,pMsr:dword
        pushad
        mov        ecx,Index
        rdmsr
        mov ebx,pMsr
        mov dword ptr ,eax
        add ebx,4
        mov dword ptr ,edx
        popad
        ret
Asm_ReadMsrEx                Endp

Asm_Invd Proc
        invd
        ret
Asm_Invd Endp

Asm_GetCs PROC
        mov                eax, cs
        ret
Asm_GetCs ENDP

Asm_GetDs PROC
        mov                eax, ds
        ret
Asm_GetDs ENDP

Asm_GetEs PROC
        mov                eax, es
        ret
Asm_GetEs ENDP

Asm_GetSs PROC
        mov                eax, ss
        ret
Asm_GetSs ENDP

Asm_GetFs PROC
        mov                eax, fs
        ret
Asm_GetFs ENDP

Asm_GetGs PROC
        mov                eax, gs
        ret
Asm_GetGs ENDP


Asm_GetCr0                Proc
       
        mov         eax, cr0
        ret
       
Asm_GetCr0                 Endp

Asm_GetCr3                Proc
       
        mov         eax, cr3
        ret
Asm_GetCr3                 Endp

Asm_GetCr4                Proc
        mov         eax, cr4
        ret
Asm_GetCr4                 Endp

Asm_SetCr0                Proc         NewCr0:dword
       
        mov         eax, NewCr0
        mov        cr0, eax
        ret
Asm_SetCr0                 Endp

Asm_SetCr2                Proc         NewCr2:dword
       
        mov         eax, NewCr2
        mov        cr2, eax
        ret
Asm_SetCr2                 Endp

Asm_SetCr3                Proc         NewCr3:dword
       
        mov         eax, NewCr3
        mov        cr3, eax
        ret
Asm_SetCr3                 Endp

Asm_SetCr4                Proc        NewCr4:dword
       
        mov         eax,NewCr4
        mov         cr4, eax
        ret
       
Asm_SetCr4                 Endp

Asm_GetDr0 PROC
        mov                eax, dr0
        ret
Asm_GetDr0 ENDP

Asm_GetDr1 PROC
        mov                eax, dr1
        ret
Asm_GetDr1 ENDP

Asm_GetDr2 PROC
        mov                eax, dr2
        ret
Asm_GetDr2 ENDP

Asm_GetDr3 PROC
        mov                eax, dr3
        ret
Asm_GetDr3 ENDP

Asm_GetDr6 PROC
        mov                eax, dr6
        ret
Asm_GetDr6 ENDP

Asm_GetDr7 PROC
        mov                eax, dr7
        ret
Asm_GetDr7 ENDP

Asm_SetDr0 PROC
        mov                dr0, ecx
        ret
Asm_SetDr0 ENDP

Asm_SetDr1 PROC
        mov                dr1, ecx
        ret
Asm_SetDr1 ENDP

Asm_SetDr2 PROC
        mov                dr2, ecx
        ret
Asm_SetDr2 ENDP

Asm_SetDr3 PROC
        mov                dr3, ecx
        ret
Asm_SetDr3 ENDP

Asm_SetDr6 PROC nNewDr6:DWORD
        mov eax,nNewDr6
        mov                dr6, eax
        ret
Asm_SetDr6 ENDP

Asm_SetDr7 PROC        nNewDr7:DWORD
        mov eax,nNewDr7
        mov                dr7, eax
        ret
Asm_SetDr7 ENDP

Asm_GetEflags PROC
        pushfd
        pop                eax
        ret
Asm_GetEflags ENDP

Asm_GetIdtBase PROC
        LOCAL        idtr:BYTE
       
        sidt        idtr
        mov                eax, dword PTR idtr
        ret
Asm_GetIdtBase ENDP

Asm_GetIdtLimit PROC
        LOCAL        idtr:BYTE
       
        sidt        idtr
        mov                ax, WORD PTR idtr
        ret
Asm_GetIdtLimit ENDP

Asm_GetGdtBase PROC
        LOCAL        gdtr:BYTE

        sgdt        gdtr
        mov                eax, dword PTR gdtr
        ret
Asm_GetGdtBase ENDP

Asm_GetGdtLimit PROC
        LOCAL        gdtr:BYTE

        sgdt        gdtr
        mov                ax, WORD PTR gdtr
        ret
Asm_GetGdtLimit ENDP

Asm_GetLdtr PROC
        sldt        eax
        ret
Asm_GetLdtr ENDP

Asm_GetTr PROC
        str        eax
        ret
Asm_GetTr ENDP

Asm_SetGdtr                Proc
       
        push        ecx
        shl        edx, 16
        push        edx
       
        lgdt        fword ptr
        pop        eax
        pop        eax
        ret
Asm_SetGdtr        Endp

Asm_SetIdtr                Proc
       
        push        ecx
        shl        edx, 16
        push        edx
        lidt        fword ptr
        pop        eax
        pop        eax
        ret
Asm_SetIdtr        Endp


Vmx_VmxOn Proc LowPart:dword,HighPart:dword
        push HighPart
        push LowPart
        Vmxon qword ptr
        add esp,8
        ret
Vmx_VmxOn Endp

Vmx_VmxOff Proc
        Vmxoff
        ret
Vmx_VmxOff Endp

Vmx_VmPtrld Proc LowPart:dword,HighPart:dword
;!!!!!!!!!!!!!!!VMCS!!!!!!!!!!!!!!!!!!!
        push HighPart
        push LowPart
        vmptrld qword ptr
        add esp,8
        ret
Vmx_VmPtrld endp

Vmx_VmClear Proc LowPart:dword,HighPart:dword
;!!!!!!!!!!!!!!!VMCS!!!!!!!!!!!!!!!!!!!
        push HighPart
        push LowPart
        vmclear qword ptr
        add esp,8
        ret
Vmx_VmClear endp

Vmx_VmRead Proc uses ecx Field:dword
        mov eax,Field
        vmread ecx,eax
        mov eax,ecx
        ret
Vmx_VmRead endp

Vmx_VmWrite Proc uses ecx Field:dword,Value:dword
        mov eax,Field
        mov ecx,Value
        vmwrite eax,ecx
        ret
Vmx_VmWrite endp

Vmx_VmCall Proc HyperCallNumber:DWORD
        pushad
        pushfd
        mov eax,HyperCallNumber
        vmcall
       
        popfd
        popad
        ret
Vmx_VmCall endp

Vmx_VmLaunch Proc
        vmlaunch
        ret
Vmx_VmLaunch endp

Vmx_VmResume Proc
        vmresume
        ret
Vmx_VmResume endp

Asm_GetVMXBasic Proc
        push 480h        ;        MSR_IA32_VMX_BASIC
        call Asm_ReadMsr
        ret
Asm_GetVMXBasic endp

Asm_GetCr0Ex Proc
        mov eax,cr0
        ret
Asm_GetCr0Ex endp

Asm_GetCr4Ex Proc
        mov eax,cr4
        ret
Asm_GetCr4Ex endp

Asm_SetCr0Ex Proc nNewCr0:DWORD
        mov eax,nNewCr0
        mov cr0,eax
        ret
Asm_SetCr0Ex endp

Asm_SetCr4Ex Proc nNewCr4:DWORD
        mov eax,nNewCr4
        mov cr4,eax
        ret
Asm_SetCr4Ex endp

Asm_GetEflagsEx Proc
        pushfd
        pop eax
        ret
Asm_GetEflagsEx endp

Asm_GetGuestESP Proc
        mov eax,GuestESP
        ret
Asm_GetGuestESP Endp

Asm_GetGuestReturn Proc
        mov eax,GuestReturn
        ret
Asm_GetGuestReturn Endp

Asm_AfterVMXOff Proc JmpESP:dword,JmpEIP:dword
        mov esp,JmpESP
        jmp JmpEIP
        ret
Asm_AfterVMXOff Endp

Asm_RunToVMCS Proc
        mov eax,
        mov GuestReturn,eax ;获取返回地址,让vmlaunch后客户机继续执行驱动加载的代码
       
        call SetupVMCS
        ret
Asm_RunToVMCS Endp

Asm_SetupVMCS Proc
        cli
        mov GuestESP,esp
       
        mov EntryEAX,eax
        mov EntryECX,ecx
        mov EntryEDX,edx
        mov EntryEBX,ebx
        mov EntryESP,esp
        mov EntryEBP,ebp
        mov EntryEDI,edi
        mov EntryESI,esi
        pushfd
        pop EntryEflags
       
        call Asm_RunToVMCS
       
        push EntryEflags
        popfd
        mov eax,EntryEAX
        mov ecx,EntryECX
        mov edx,EntryEDX
        mov ebx,EntryEBX
        mov esp,EntryESP
        mov ebp,EntryEBP
        mov esi,EntryESI
        mov edi,EntryEDI
       
        mov esp,GuestESP
        sti
        ret
Asm_SetupVMCS Endp

Asm_VMMEntryPoint Proc
        cli
        push eax
        push ecx
        push edx
        push ebx
        push esp   ;HOST_RSP
        push ebp
        push edi
        push esi
       
        mov ,eax
        mov ,ebx
        call GetGuestRegsAddress
        mov ,ecx
        mov ,edx
        mov ,ebx
        mov ,esp
        mov ,ebp
        mov ,esi
        mov ,edi
        mov ebx,
        mov ,ebx
        mov eax,
        mov ebx,
       
        call VMMEntryPoint
       
        pop esi
        pop edi
        pop ebp
        pop esp
        pop ebx
        pop edx
        pop ecx
        pop eax
       
        call GetGuestRegsAddress
        mov ecx,
        mov edx,
        mov ebx,
        mov esp,
        mov ebp,
        mov esi,
        mov edi,
        mov eax,
        sti
        vmresume
Asm_VMMEntryPoint Endp


END


还记得前面VMCALL 这里我们要处理下
当需要关闭VT时 我们在虚拟机调用VMCALL 这时进入VMM
我们需要调用VMOFF关闭VT 但关闭VT后EIP是在关闭VT的这里 我们要让它会到调用VMCALL的下一条指令去
void HandleVmCall()
{
        ULONG JmpEIP;
        if (g_GuestRegs.eax == 'SVT')
        {
                //得到调用VmCall指令的下一句指令地址
                JmpEIP = g_GuestRegs.eip + Vmx_VmRead(VM_EXIT_INSTRUCTION_LEN);
                //调用VmxOff
                Vmx_VmxOff();

                //设置返回esp和eip
                Asm_AfterVMXOff(g_GuestRegs.esp,JmpEIP);
                /*
                Asm_AfterVMXOff Proc JmpESP:dword,JmpEIP:dword
                        mov esp,JmpESP
                        jmp JmpEIP
                        ret
                Asm_AfterVMXOff Endp
                */
        }
}


代码注释的很清楚不在多解释
代码来自看雪小宝的教程此文只是此教程的文字版
目前代码只能运行在单核的x86 intel cpu 的系统中
后面会补充在多核x64



   





页: [1]
查看完整版本: VT系列:VMCS表填写