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_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: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[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: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,[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