想要查看内容赶紧注册登陆吧!
您需要 登录 才可以下载或查看,没有账号?立即注册
x
一共有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_INFO 0
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 [esi], eax
mov esi, ret_ebx
mov dword ptr [esi], ebx
mov esi, ret_ecx
mov dword ptr [esi], ecx
mov esi, ret_edx
mov dword ptr [esi], 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 [ebx],eax
add ebx,4
mov dword ptr [ebx],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 WORD
mov eax,nNewDr6
mov dr6, eax
ret
Asm_SetDr6 ENDP
Asm_SetDr7 PROC nNewDr7 WORD
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[10]:BYTE
sidt idtr
mov eax, dword PTR idtr[2]
ret
Asm_GetIdtBase ENDP
Asm_GetIdtLimit PROC
LOCAL idtr[10]:BYTE
sidt idtr
mov ax, WORD PTR idtr[0]
ret
Asm_GetIdtLimit ENDP
Asm_GetGdtBase PROC
LOCAL gdtr[10]:BYTE
sgdt gdtr
mov eax, dword PTR gdtr[2]
ret
Asm_GetGdtBase ENDP
Asm_GetGdtLimit PROC
LOCAL gdtr[10]:BYTE
sgdt gdtr
mov ax, WORD PTR gdtr[0]
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 [esp+2]
pop eax
pop eax
ret
Asm_SetGdtr Endp
Asm_SetIdtr Proc
push ecx
shl edx, 16
push edx
lidt fword ptr [esp+2]
pop eax
pop eax
ret
Asm_SetIdtr Endp
Vmx_VmxOn Proc LowPart:dword,HighPart:dword
push HighPart
push LowPart
Vmxon qword ptr [esp]
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 [esp]
add esp,8
ret
Vmx_VmPtrld endp
Vmx_VmClear Proc LowPart:dword,HighPart:dword
;!!!!!!!!!!!!!!!VMCS!!!!!!!!!!!!!!!!!!!
push HighPart
push LowPart
vmclear qword ptr [esp]
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 WORD
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 WORD
mov eax,nNewCr0
mov cr0,eax
ret
Asm_SetCr0Ex endp
Asm_SetCr4Ex Proc nNewCr4 WORD
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,[esp]
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 [esp-1280h],eax
mov [esp-1284h],ebx
call GetGuestRegsAddress
mov [eax+4h],ecx
mov [eax+8h],edx
mov [eax+0Ch],ebx
mov [eax+10h],esp
mov [eax+14h],ebp
mov [eax+18h],esi
mov [eax+1Ch],edi
mov ebx,[esp-1280h]
mov [eax],ebx
mov eax,[esp-1280h]
mov ebx,[esp-1284h]
call VMMEntryPoint
pop esi
pop edi
pop ebp
pop esp
pop ebx
pop edx
pop ecx
pop eax
call GetGuestRegsAddress
mov ecx,[eax+4h]
mov edx,[eax+8h]
mov ebx,[eax+0Ch]
mov esp,[eax+10h]
mov ebp,[eax+14h]
mov esi,[eax+18h]
mov edi,[eax+1Ch]
mov eax,[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 
|