1. 程式人生 > >VT系列:VMCS表填寫

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