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]