1、檢測你的CPU是否支持VMX
MOV EAX,1
CPUID
檢測ECX的第5位,如果爲1則支持VMX
2、初始化VMXON region
pVMXONRegion = MmAllocateNonCachedMemory( 4096 );
RtlZeroMemory( pVMXONRegion, 4096 );
PHYSICAL_ADDRESS PhysicalVMXONRegionPtr=MmGetPhysicalAddress( pVMXONRegion );
__asm
{
MOV ECX, IA32_VMX_BASIC_MSR_CODE //0x480
RDMSR
LEA EBX, vmxBasicMsr
MOV [EBX+4], EDX
MOV [EBX], EAX
}
*(pVMXONRegion) = vmxBasicMsr.RevId;
3、設置VMXE位(CR4)
CR4_REG cr4_reg = {0};
__asm
{
PUSH EAX
_emit 0x0F // MOV EAX, CR4
_emit 0x20
_emit 0xE0
MOV cr4_reg, EAX
POP EAX
}
cr4_reg.VMXE = 1;
__asm
{
PUSH EAX
MOV EAX, cr4_reg
_emit 0x0F // MOV CR4, EAX
_emit 0x22
_emit 0xE0
POP EAX
}
4、執行VMXON指令
__asm
{
PUSH DWORD PTR PhysicalVMXONRegionPtr.HighPart
PUSH DWORD PTR PhysicalVMXONRegionPtr.LowPart
_emit 0xF3 // VMXON [ESP]
_emit 0x0F
_emit 0xC7
_emit 0x34
_emit 0x24
PUSHFD
POP eFlags
ADD ESP, 8
}
if( eFlags.CF == 1 )
{
Log( "ERROR : VMXON operation failed." , 0 );
return 0;
}
//到這裏說明已經進入了VMX模式,並且工作在ROOT下了
5、初始化VMCS region
//這是個4K大小的內存塊
ULONG *pVMCSRegion = MmAllocateNonCachedMemory( 4096 );
RtlZeroMemory( pVMCSRegion, 4096 );
PHYSICAL_ADDRESS PhysicalVMCSRegionPtr = MmGetPhysicalAddress( pVMCSRegion );
*(pVMCSRegion) = vmxBasicMsr.RevId;
__asm
{
PUSH DWORD PTR PhysicalVMCSRegionPtr.HighPart
PUSH DWORD PTR PhysicalVMCSRegionPtr.LowPart
_emit 0x66 // VMCLEAR [ESP]
_emit 0x0F
_emit 0xc7
_emit 0x34
_emit 0x24
ADD ESP, 8
PUSHFD
POP eFlags
}
if( eFlags.CF != 0 || eFlags.ZF != 0 )
{
Log( "ERROR : VMCLEAR operation failed." , 0 );
return 0;
}
//到這裏,說明VMCS region初始化完成了
6、裝載VMCS region
__asm
{
PUSH DWORD PTR PhysicalVMCSRegionPtr.HighPart
PUSH DWORD PTR PhysicalVMCSRegionPtr.LowPart
_emit 0x0F // VMPTRLD [ESP]
_emit 0xC7
_emit 0x34
_emit 0x24
ADD ESP, 8
}
7、設置VMCS region中的相關域
這些域包括:
A、Guest的各個段寄存器的selector,base,limit,access right
B、Host的各個段寄存器的selector,base,limit,access right
C、Guest的CR0,CR3,CR4,IDTR,GDTR,LDTR,Rflag,SYSENTER_CS,SYSENTER_EIP,SYSENTER_ESP
D、Host的CR0,CR3,CR4,IDTR,GDTR,LDTR,Rflag,SYSENTER_CS,SYSENTER_EIP,SYSENTER_ESP
如:
USHORT seg_selector = 0;
__asm MOV seg_selector, ES
Log( "Setting Guest ES Selector" , seg_selector );
WriteVMCS( 0x00000800, seg_selector );
//Guest CS selector 00000802H
__asm MOV seg_selector, CS
Log( "Setting Guest CS Selector" , seg_selector );
WriteVMCS( 0x00000802, seg_selector );
E、Exception bitmap
作爲調試器,我們需要攔截INT1和INT3中斷,因此,Exception bitmap起碼要這樣設置:
ULONG temp32 = 0x00000000;
SetBit( &temp32, 1 ); // Single Step (INT 1) int1
SetBit( &temp32, 3 ); // Software Interrupt (INT 3)
WriteVMCS( 0x00004004, temp32 );
F、VMX Abort Error Code域
這個域需要清成0
RtlZeroMemory( (pVMCSRegion + 4), 4 );
G、Guest的ESP和EIP
WriteVMCS( 0x0000681C, (ULONG)GuestStack );
WriteVMCS( 0x0000681E, (ULONG)GuestReturn );
H、Host的ESP和EIP
PVOID HostStack = ExAllocatePoolWithTag( NonPagedPool , 0x8000, 'kSkF' );
If(HostStack == NULL)
{
//…出錯…
}
WriteVMCS( 0x00006C14, ((ULONG)HostStack + 0x7FFF) );
WriteVMCS( 0x00006C16, (ULONG)VMExitProc );// VMExitProc就是VMM的程序入口
8、執行VMLAUNCH
__asm
{
_emit 0x0F // VMLAUNCH
_emit 0x01
_emit 0xC2
}
// 如果成功,就不會到這裏了!
__asm
{
PUSHFD
POP eFlags
}
Log( "VMLAUNCH Failure" , 0xDEADDEAD );
if( eFlags.CF != 0 || eFlags.ZF != 0 || TRUE )
{
ULONG ErrorCode= VMRead_ULONG(0x00004400);
Log( "VM Instruction Error" , ErrorCode );
}
VMMExitProc
extern "C"
__declspec( naked ) VOID VMMExitProc( )
{
__asm CLI
__asm PUSHAD
//保存通用寄存器
__asm MOV GuestEAX, EAX
__asm MOV GuestEBX, EBX
__asm MOV GuestECX, ECX
__asm MOV GuestEDX, EDX
__asm MOV GuestEDI, EDI
__asm MOV GuestESI, ESI
__asm MOV GuestEBP, EBP
//保存調試寄存器
__asm
{
push eax
mov eax,dr0
mov GuestDr0,eax
mov eax,dr1
mov GuestDr1,eax
mov eax,dr2
mov GuestDr2,eax
mov eax,dr3
mov GuestDr3,eax
mov eax,dr6
mov GuestDr6,eax
pop eax
}
GuestDr7 = VMRead_ULONG(VMX_VMCS_GUEST_DR7);//0x681A
GuestEIP = VMRead_ULONG(VMX_VMCS_GUEST_RIP);//0x681E
GuestEFlags = VMRead_ULONG(VMX_VMCS_GUEST_RFLAGS);//0x6820
//獲取本次Exit的詳細信息
ULONG ExitReason = VMRead_ULONG(VMX_VMCS_RO_EXIT_REASON);//0x4402
ULONG ExitInterruptionInformation = VMRead_ULONG(VMX_VMCS_RO_EXIT_INTERRUPTION_INFO);//0x4404
ULONG ExitInstructionLength = VMRead_ULONG(VMX_VMCS_RO_EXIT_INSTR_LENGTH);//0x440C
ULONG ExitQualification = VMRead_ULONG(VMX_VMCS_RO_EXIT_QUALIFICATION);//0x6400
if(ExitReason==0)//是個Exception
{
int IntrNo = ExitInterruptionInformation & 0xFF;
if(IntrNo==1 || IntrNo==3)//是INT1或者INT3
{
if(IsMyBreakPoint())
{//是我們自己設置的斷點
DoSomeThing();
}
else
{//我們該向Guest注入一個異常,讓Guest的中斷處理程序能夠繼續處理,彷彿一切都沒發生
ULONG InjectIrqInfo = 0x80000300 | IntrNo;
// 設置 VM-Entry Exception域
WriteVMCS( VMX_VMCS_CTRL_ENTRY_INSTR_LENGTH, ExitInstructionLength);
WriteVMCS( VMX_VMCS_CTRL_ENTRY_IRQ_INFO, InjectIrqInfo);
}
}
}
if(ExitReason==0x12)//VMMCALL
{//我們用一個VMMCALL指令來通知VMM結束VMX
if(GuestEAX == 0x12345678)//我們自己約定的規則
{
GuestEIP += ExitInstructionLength;
goto Exit;
}
}
goto Resume;
Exit:
// 關閉VMX.
DbgPrint("Terminating VMX Mode.");
__asm
{
_emit 0x0F // VMXOFF
_emit 0x01
_emit 0xC4
POPAD
MOV ESP, GuestESP
STI
JMP GuestEIP
}
Resume:
// Need to execute the VMRESUME without having changed
// the state of the GPR and ESP et cetera.
__asm
{
push eax
mov eax,GuestDr0
mov DR0,eax
mov eax,GuestDr1
mov DR1,eax
mov eax,GuestDr2
mov DR2,eax
mov eax,GuestDr3
mov DR3,eax
mov eax,GuestDr6
mov DR6,eax
pop eax
POPAD
MOV EAX, GuestEAX
MOV EBX, GuestEBX
MOV ECX, GuestECX
MOV EDX, GuestEDX
MOV ESI, GuestESI
MOV EDI, GuestEDI
MOV EBP, GuestEBP
STI
_emit 0x0F // VMRESUME
_emit 0x01
_emit 0xC3
//永遠不會到這裏
}