/*
Virtual Machine Monitor
Copyright (C) 2007 Shawn Embleton
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ntddk.h>
#pragma warning(disable: 4133 4102)
#define IA32_VMX_BASIC_MSR_CODE 0x480
#define IA32_FEATURE_CONTROL_CODE 0x03A
VOID
KeSetSystemAffinityThread (
IN KAFFINITY Affinity
);
//
// //
// PROTOTYPES //
// //
//
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath );
VOID DriverUnload( IN PDRIVER_OBJECT DriverObject );
VOID StartVMX( );
VOID VMMEntryPoint( );
typedef struct _VMX_FEATURES
{
unsigned SSE3 :1; // SSE3 Extensions
unsigned RES1 :2;
unsigned MONITOR :1; // MONITOR/WAIT
unsigned DS_CPL :1; // CPL qualified Debug Store
unsigned VMX :1; // Virtual Machine Technology
unsigned RES2 :1;
unsigned EST :1; // Enhanced Intel?Speedstep Technology
unsigned TM2 :1; // Thermal monitor 2
unsigned SSSE3 :1; // SSSE3 extensions
unsigned CID :1; // L1 context ID
unsigned RES3 :2;
unsigned CX16 :1; // CMPXCHG16B
unsigned xTPR :1; // Update control
unsigned PDCM :1; // Performance/Debug capability MSR
unsigned RES4 :2;
unsigned DCA :1;
unsigned RES5 :13;
} VMX_FEATURES;
//
// //
// EFLAGS //
// //
//
typedef struct _EFLAGS
{
unsigned Reserved1 :10;
unsigned ID :1; // Identification flag
unsigned VIP :1; // Virtual interrupt pending
unsigned VIF :1; // Virtual interrupt flag
unsigned AC :1; // Alignment check
unsigned VM :1; // Virtual 8086 mode
unsigned RF :1; // Resume flag
unsigned Reserved2 :1;
unsigned NT :1; // Nested task flag
unsigned IOPL :2; // I/O privilege level
unsigned OF :1;
unsigned DF :1;
unsigned IF :1; // Interrupt flag
unsigned TF :1; // Task flag
unsigned SF :1; // Sign flag
unsigned ZF :1; // Zero flag
unsigned Reserved3 :1;
unsigned AF :1; // Borrow flag
unsigned Reserved4 :1;
unsigned PF :1; // Parity flag
unsigned Reserved5 :1;
unsigned CF :1; // Carry flag [Bit 0]
} EFLAGS;
///
// //
// MSR //
// //
///
typedef struct _MSR
{
ULONG Hi;
ULONG Lo;
} MSR;
typedef struct _IA32_VMX_BASIC_MSR
{
unsigned RevId :32; // Bits 31...0 contain the VMCS revision identifier
unsigned szVmxOnRegion :12; // Bits 43...32 report # of bytes for VMXON region
unsigned RegionClear :1; // Bit 44 set only if bits 32-43 are clear
unsigned Reserved1 :3; // Undefined
unsigned PhyAddrWidth :1; // Physical address width for referencing VMXON, VMCS, etc.
unsigned DualMon :1; // Reports whether the processor supports dual-monitor
// treatment of SMI and SMM
unsigned MemType :4; // Memory type that the processor uses to access the VMCS
unsigned VmExitReport :1; // Reports weather the procesor reports info in the VM-exit
// instruction information field on VM exits due to execution
// of the INS and OUTS instructions
unsigned Reserved2 :9; // Undefined
} IA32_VMX_BASIC_MSR;
typedef struct _IA32_FEATURE_CONTROL_MSR
{
unsigned Lock :1; // Bit 0 is the lock bit - cannot be modified once lock is set
unsigned Reserved1 :1; // Undefined
unsigned EnableVmxon :1; // Bit 2. If this bit is clear, VMXON causes a general protection exception
unsigned Reserved2 :29; // Undefined
unsigned Reserved3 :32; // Undefined
} IA32_FEATURE_CONTROL_MSR;
/
// //
// REGISTERS //
// //
/
typedef struct _CR0_REG
{
unsigned PE :1; // Protected Mode Enabled [Bit 0]
unsigned MP :1; // Monitor Coprocessor FLAG
unsigned EM :1; // Emulate FLAG
unsigned TS :1; // Task Switched FLAG
unsigned ET :1; // Extension Type FLAG
unsigned NE :1; // Numeric Error
unsigned Reserved1 :10; //
unsigned WP :1; // Write Protect
unsigned Reserved2 :1; //
unsigned AM :1; // Alignment Mask
unsigned Reserved3 :10; //
unsigned NW :1; // Not Write-Through
unsigned CD :1; // Cache Disable
unsigned PG :1; // Paging Enabled
} CR0_REG;
typedef struct _CR4_REG
{
unsigned VME :1; // Virtual Mode Extensions
unsigned PVI :1; // Protected-Mode Virtual Interrupts
unsigned TSD :1; // Time Stamp Disable
unsigned DE :1; // Debugging Extensions
unsigned PSE :1; // Page Size Extensions
unsigned PAE :1; // Physical Address Extension
unsigned MCE :1; // Machine-Check Enable
unsigned PGE :1; // Page Global Enable
unsigned PCE :1; // Performance-Monitoring Counter Enable
unsigned OSFXSR :1; // OS Support for FXSAVE/FXRSTOR
unsigned OSXMMEXCPT :1; // OS Support for Unmasked SIMD Floating-Point Exceptions
unsigned Reserved1 :2; //
unsigned VMXE :1; // Virtual Machine Extensions Enabled
unsigned Reserved2 :18; //
} CR4_REG;
typedef struct _MISC_DATA
{
unsigned Reserved1 :6; // [0-5]
unsigned ActivityStates :3; // [6-8]
unsigned Reserved2 :7; // [9-15]
unsigned CR3Targets :9; // [16-24]
// 512*(N+1) is the recommended maximum number of MSRs
unsigned MaxMSRs :3; // [25-27]
unsigned Reserved3 :4; // [28-31]
unsigned MSEGRevID :32; // [32-63]
} MISC_DATA;
/
// //
// SELECTORS //
// //
/
typedef struct _GDTR
{
unsigned Limit :16;
unsigned BaseLo :16;
unsigned BaseHi :16;
} GDTR;
typedef struct _IDTR
{
unsigned Limit :16;
unsigned BaseLo :16;
unsigned BaseHi :16;
} IDTR;
typedef struct _SEG_DESCRIPTOR
{
unsigned LimitLo :16;
unsigned BaseLo :16;
unsigned BaseMid :8;
unsigned Type :4;
unsigned System :1;
unsigned DPL :2;
unsigned Present :1;
unsigned LimitHi :4;
unsigned AVL :1;
unsigned L :1;
unsigned DB :1;
unsigned Gran :1; // Granularity
unsigned BaseHi :8;
} SEG_DESCRIPTOR;
///
// //
// Log //
// //
///
#define Log( message, value ) { DbgPrint("[vmm] %-40s [%08X]\n", message, value ); }
///
// //
// SET BIT //
// //
///
VOID SetBit( ULONG * dword, ULONG bit )
{
ULONG mask = ( 1 << bit );
*dword = *dword | mask;
}
/
// //
// CLEAR BIT //
// //
/
VOID ClearBit( ULONG * dword, ULONG bit )
{
ULONG mask = 0xFFFFFFFF;
ULONG sub = ( 1 << bit );
mask = mask - sub;
*dword = *dword & mask;
}
///
// //
// Globals //
// //
///
ULONG *pVMXONRegion = NULL; // Memory address of VMXON region.
ULONG *pVMCSRegion = NULL;
ULONG VMXONRegionSize = 0;
ULONG VMCSRegionSize = 0;
ULONG ErrorCode = 0;
EFLAGS eFlags = {0};
MSR msr = {0};
PVOID FakeStack = NULL;
ULONG HandlerLogging = 0;
ULONG ScrubTheLaunch = 0;
// Writes the contents of registers EDX:EAX into the 64-bit model specific
// register (MSR) specified in the ECX register. The contents of the EDX
// register are copied to high-order 32 bits of the selected MSR and the
// contents of the EAX register are copied to low-order 32 bits of the MSR.
//
VOID WriteVMCS( ULONG encoding, ULONG value )
{
__asm
{
PUSHAD
PUSH value
MOV EAX, encoding
_emit 0x0F // VMWRITE EAX, [ESP]
_emit 0x79
_emit 0x04
_emit 0x24
POP EAX
POPAD
}
}
// Loads the contents of a 64-bit model specific register (MSR) specified
// in the ECX register into registers EDX:EAX. The EDX register is loaded
// with the high-order 32 bits of the MSR and the EAX register is loaded
// with the low-order 32 bits.
// msr.Hi --> EDX
// msr.Lo --> EAX
//
VOID ReadMSR( ULONG msrEncoding )
{
__asm
{
PUSHAD
MOV ECX, msrEncoding
RDMSR
MOV msr.Hi, EDX
MOV msr.Lo, EAX
POPAD
}
}
// Write the msr data structure into MSR specified by msrEncoding.
// msr.Hi <-- EDX
// msr.Lo <-- EAX
//
VOID WriteMSR( ULONG msrEncoding )
{
__asm
{
PUSHAD
MOV EDX, msr.Hi
MOV EAX, msr.Lo
MOV ECX, msrEncoding
WRMSR
POPAD
}
}
ULONG GetSegmentDescriptorBase( ULONG gdt_base , USHORT seg_selector )
{
ULONG base = 0;
SEG_DESCRIPTOR segDescriptor = {0};
RtlCopyBytes( &segDescriptor, (ULONG *)(gdt_base + (seg_selector >> 3) * 8), 8 );
base = segDescriptor.BaseHi;
base <<= 8;
base |= segDescriptor.BaseMid;
base <<= 16;
base |= segDescriptor.BaseLo;
return base;
}
ULONG GetSegmentDescriptorDPL( ULONG gdt_base , USHORT seg_selector )
{
SEG_DESCRIPTOR segDescriptor = {0};
RtlCopyBytes( &segDescriptor, (ULONG *)(gdt_base + (seg_selector >> 3) * 8), 8 );
return segDescriptor.DPL;
}
ULONG GetSegmentDescriptorLimit( ULONG gdt_base , USHORT seg_selector )
{
SEG_DESCRIPTOR segDescriptor = {0};
RtlCopyBytes( &segDescriptor, (ULONG *)(gdt_base + (seg_selector >> 3) * 8), 8 );
//return segDescriptor.LimitLo;
return ( (segDescriptor.LimitHi << 16) | segDescriptor.LimitLo );
}
PHYSICAL_ADDRESS PhysicalVMXONRegionPtr;
PHYSICAL_ADDRESS PhysicalVMCSRegionPtr;
VMX_FEATURES vmxFeatures;
IA32_VMX_BASIC_MSR vmxBasicMsr ;
IA32_FEATURE_CONTROL_MSR vmxFeatureControl ;
CR0_REG cr0_reg = {0};
CR4_REG cr4_reg = {0};
ULONG temp32 = 0;
USHORT temp16 = 0;
GDTR gdt_reg = {0};
IDTR idt_reg = {0};
ULONG gdt_base = 0;
ULONG idt_base = 0;
USHORT mLDT = 0;
USHORT seg_selector = 0;
SEG_DESCRIPTOR segDescriptor = {0};
MISC_DATA misc_data = {0};
PVOID GuestReturn = NULL;
ULONG GuestStack = 0;
///
// //
// VMX //
// //
///
__declspec( naked ) VOID StartVMX( )
{
//
// Get the Guest Return EIP.
//
//
// Hi | |
// +-----------+
// | EIP |
// +-----------+ <-- ESP after the CALL
// Lo | |
//
//
__asm POP GuestReturn
Log("Guest Return EIP" , GuestReturn );
///
// //
// SET THREAD AFFINITY //
// //
///
Log( "Enabling VMX mode on CPU 0", 0 );
KeSetSystemAffinityThread( (KAFFINITY) 0x00000001 );
Log( "Running on Processor" , KeGetCurrentProcessorNumber() );
// //
// GDT Info //
// //
__asm
{
SGDT gdt_reg
}
temp32 = 0;
temp32 = gdt_reg.BaseHi;
temp32 <<= 16;
temp32 |= gdt_reg.BaseLo;
gdt_base = temp32;
Log( "GDT Base", gdt_base );
Log( "GDT Limit", gdt_reg.Limit );
// //
// IDT Segment Selector //
// //
__asm SIDT idt_reg
temp32 = 0;
temp32 = idt_reg.BaseHi;
temp32 <<= 16;
temp32 |= idt_reg.BaseLo;
idt_base = temp32;
Log( "IDT Base", idt_base );
Log( "IDT Limit", idt_reg.Limit );
// (1) Check VMX support in processor using CPUID.
__asm
{
PUSHAD
MOV EAX, 1
CPUID
// ECX contains the VMX_FEATURES FLAGS (VMX supported if bit 5 equals 1)
MOV vmxFeatures, ECX
MOV EAX, 0x80000008
CPUID
MOV temp32, EAX
POPAD
}
if( vmxFeatures.VMX == 0 )
{
Log( "VMX Support Not Present." , vmxFeatures );
goto Abort;
}
Log( "VMX Support Present." , vmxFeatures );
// (2) Determine the VMX capabilities supported by the processor through
// the VMX capability MSRs.
__asm
{
PUSHAD
MOV ECX, IA32_VMX_BASIC_MSR_CODE
RDMSR
LEA EBX, vmxBasicMsr
MOV [EBX+4], EDX
MOV [EBX], EAX
MOV ECX, IA32_FEATURE_CONTROL_CODE
RDMSR
LEA EBX, vmxFeatureControl
MOV [EBX+4], EDX
MOV [EBX], EAX
POPAD
};
// (3) Create a VMXON region in non-pageable memory of a size specified by
// IA32_VMX_BASIC_MSR and aligned to a 4-byte boundary. The VMXON region
// must be hosted in cache-coherent memory.
Log( "VMXON Region Size" , vmxBasicMsr.szVmxOnRegion ) ;
Log( "VMXON Access Width Bit" , vmxBasicMsr.PhyAddrWidth );
Log( " [ 1] --> 32-bit" , 0 );
Log( " [ 0] --> 64-bit" , 0 );
Log( "VMXON Memory Type", vmxBasicMsr.MemType );
Log( " [ 0] --> Strong Uncacheable" , 0 );
Log( " [ 1-5] --> Unused" , 0 );
Log( " [ 6] --> Write Back" , 0 );
Log( " [7-15] --> Unused" , 0 );
VMXONRegionSize = vmxBasicMsr.szVmxOnRegion;
switch( vmxBasicMsr.MemType )
{
case 0:
Log( "Unsupported memory type." , vmxBasicMsr.MemType );
goto Abort;
break;
case 6:
break;
default:
Log( "ERROR : Unknown VMXON Region memory type." , 0);
goto Abort;
break;
}
// (4) Initialize the version identifier in the VMXON region (first 32 bits)
// with the VMCS revision identifier reported by capability MSRs.
*(pVMXONRegion) = vmxBasicMsr.RevId;
Log( "vmxBasicMsr.RevId" , vmxBasicMsr.RevId );
// (5) Ensure the current processor operating mode meets the required CR0
// fixed bits (CR0.PE=1, CR0.PG=1). Other required CR0 fixed bits can
// be detected through the IA32_VMX_CR0_FIXED0 and IA32_VMX_CR0_FIXED1
// MSRs.
__asm
{
PUSH EAX
MOV EAX, CR0
MOV cr0_reg, EAX
POP EAX
}
if( cr0_reg.PE != 1 )
{
Log( "ERROR : Protected Mode not enabled." , 0 );
Log( "Value of CR0" , cr0_reg );
goto Abort;
}
Log( "Protected Mode enabled." , 0 );
if( cr0_reg.PG != 1 )
{
Log( "ERROR : Paging not enabled." , 0 );
Log( "Value of CR0" , cr0_reg );
goto Abort;
}
Log( "Paging enabled." , 0 );
cr0_reg.NE = 1;
__asm
{
PUSH EAX
MOV EAX, cr0_reg
MOV CR0, EAX
POP EAX
}
// (6) Enable VMX operation by setting CR4.VMXE=1 [bit 13]. Ensure the
// resultant CR4 value supports all the CR4 fixed bits reported in
// the IA32_VMX_CR4_FIXED0 and IA32_VMX_CR4_FIXED1 MSRs.
__asm
{
PUSH EAX
_emit 0x0F // MOV EAX, CR4
_emit 0x20
_emit 0xE0
MOV cr4_reg, EAX
POP EAX
}
Log( "CR4" , cr4_reg );
cr4_reg.VMXE = 1;
Log( "CR4" , cr4_reg );
__asm
{
PUSH EAX
MOV EAX, cr4_reg
_emit 0x0F // MOV CR4, EAX
_emit 0x22
_emit 0xE0
POP EAX
}
// (7) Ensure that the IA32_FEATURE_CONTROL_MSR (MSR index 0x3A) has been
// properly programmed and that its lock bit is set (bit 0=1). This MSR
// is generally configured by the BIOS using WRMSR.
Log( "IA32_FEATURE_CONTROL Lock Bit" , vmxFeatureControl.Lock );
if( vmxFeatureControl.Lock != 1 )
{
Log( "ERROR : Feature Control Lock Bit != 1." , 0 );
goto Abort;
}
// (8) Execute VMXON with the physical address of the VMXON region as the
// operand. Check successful execution of VMXON by checking if
// RFLAGS.CF=0.
__asm
{
PUSH DWORD PTR 0
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 );
goto Abort;
}
Log( "SUCCESS : VMXON operation completed." , 0 );
Log( "VMM is now running." , 0 );
//
// *** The processor is now in VMX root operation!
//
// (1) Create a VMCS region in non-pageable memory of size specified by
// the VMX capability MSR IA32_VMX_BASIC and aligned to 4-KBytes.
// Software should read the capability MSRs to determine width of the
// physical addresses that may be used for a VMCS region and ensure
// the entire VMCS region can be addressed by addresses with that width.
// The term "guest-VMCS address" refers to the physical address of the
// new VMCS region for the following steps.
VMCSRegionSize = vmxBasicMsr.szVmxOnRegion;
switch( vmxBasicMsr.MemType )
{
case 0:
Log( "Unsupported memory type." , vmxBasicMsr.MemType );
goto Abort;
break;
case 6:
break;
default:
Log( "ERROR : Unknown VMCS Region memory type." , 0 );
goto Abort;
break;
}
// (2) Initialize the version identifier in the VMCS (first 32 bits)
// with the VMCS revision identifier reported by the VMX
// capability MSR IA32_VMX_BASIC.
*(pVMCSRegion) = vmxBasicMsr.RevId;
// (3) Execute the VMCLEAR instruction by supplying the guest-VMCS address.
// This will initialize the new VMCS region in memory and set the launch
// state of the VMCS to "clear". This action also invalidates the
// working-VMCS pointer register to FFFFFFFF_FFFFFFFFH. Software should
// verify successful execution of VMCLEAR by checking if RFLAGS.CF = 0
// and RFLAGS.ZF = 0.
__asm
{
PUSH DWORD PTR 0
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 );
goto Abort;
}
Log( "SUCCESS : VMCLEAR operation completed." , 0 );
// (4) Execute the VMPTRLD instruction by supplying the guest-VMCS address.
// This initializes the working-VMCS pointer with the new VMCS region’s
// physical address.
__asm
{
PUSH DWORD PTR 0
PUSH DWORD PTR PhysicalVMCSRegionPtr.LowPart
_emit 0x0F // VMPTRLD [ESP]
_emit 0xC7
_emit 0x34
_emit 0x24
ADD ESP, 8
}
//
// ***************************************
// * *
// * H.1.1 16-Bit Guest-State Fields *
// * *
// ***************************************
//
// Guest ES selector 00000800H
__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 );
// Guest SS selector 00000804H
__asm MOV seg_selector, SS
Log( "Setting Guest SS Selector" , seg_selector );
WriteVMCS( 0x00000804, seg_selector );
// Guest DS selector 00000806H
__asm MOV seg_selector, DS
Log( "Setting Guest DS Selector" , seg_selector );
WriteVMCS( 0x00000806, seg_selector );
// Guest FS selector 00000808H
__asm MOV seg_selector, FS
Log( "Setting Guest FS Selector" , seg_selector );
WriteVMCS( 0x00000808, seg_selector );
// Guest GS selector 0000080AH
__asm MOV seg_selector, GS
Log( "Setting Guest GS Selector" , seg_selector );
WriteVMCS( 0x0000080A, seg_selector );
// Guest TR selector 0000080EH
__asm STR seg_selector
ClearBit( &seg_selector, 2 ); // TI Flag
Log( "Setting Guest TR Selector" , seg_selector );
WriteVMCS( 0x0000080E, seg_selector );
// **************************************
// * *
// * H.1.2 16-Bit Host-State Fields *
// * *
// **************************************
//
// Host ES selector 00000C00H
__asm MOV seg_selector, ES
seg_selector &= 0xFFFC;
Log( "Setting Host ES Selector" , seg_selector );
WriteVMCS( 0x00000C00, seg_selector );
// Host CS selector 00000C02H
__asm MOV seg_selector, CS
Log( "Setting Host CS Selector" , seg_selector );
WriteVMCS( 0x00000C02, seg_selector );
// Host SS selector 00000C04H
__asm MOV seg_selector, SS
Log( "Setting Host SS Selector" , seg_selector );
WriteVMCS( 0x00000C04, seg_selector );
// Host DS selector 00000C06H
__asm MOV seg_selector, DS
seg_selector &= 0xFFFC;
Log( "Setting Host DS Selector" , seg_selector );
WriteVMCS( 0x00000C06, seg_selector );
// Host FS selector 00000C08H
__asm MOV seg_selector, FS
Log( "Setting Host FS Selector" , seg_selector );
WriteVMCS( 0x00000C08, seg_selector );
// Host GS selector 00000C0AH
__asm MOV seg_selector, GS
seg_selector &= 0xFFFC;
Log( "Setting Host GS Selector" , seg_selector );
WriteVMCS( 0x00000C0A, seg_selector );
// Host TR selector 00000C0CH
__asm STR seg_selector
Log( "Setting Host TR Selector" , seg_selector );
WriteVMCS( 0x00000C0C, seg_selector );
// ***************************************
// * *
// * H.2.2 64-Bit Guest-State Fields *
// * *
// ***************************************
//
// VMCS Link Pointer (full) 00002800H
temp32 = 0xFFFFFFFF;
Log( "Setting VMCS Link Pointer (full)" , temp32 );
WriteVMCS( 0x00002800, temp32 );
// VMCS link pointer (high) 00002801H
temp32 = 0xFFFFFFFF;
Log( "Setting VMCS Link Pointer (high)" , temp32 );
WriteVMCS( 0x00002801, temp32 );
// Reserved Bits of IA32_DEBUGCTL MSR must be 0
// (1D9H)
ReadMSR( 0x000001D9 );
Log( "IA32_DEBUGCTL MSR" , msr.Lo );
// Guest IA32_DEBUGCTL (full) 00002802H
temp32 = msr.Lo;
Log( "Setting Guest IA32_DEBUGCTL (full)" , temp32 );
WriteVMCS( 0x00002802, temp32 );
// Guest IA32_DEBUGCTL (high) 00002803H
temp32 = msr.Hi;
Log( "Setting Guest IA32_DEBUGCTL (high)" , temp32 );
WriteVMCS( 0x00002803, temp32 );
// ***********************************
// * *
// * H.3.1 32-Bit Control Fields *
// * *
// ***********************************
//
// Pin-based VM-execution controls 00004000H
// IA32_VMX_PINBASED_CTLS MSR (index 481H)
ReadMSR( 0x481 );
Log( "Pin-based allowed-0" , msr.Lo );
Log( "Pin-based allowed-1" , msr.Hi );
temp32 = 0;
temp32 |= msr.Lo;
temp32 &= msr.Hi;
//SetBit( &temp32, 3 );
Log( "Setting Pin-Based Controls Mask" , temp32 );
WriteVMCS( 0x00004000, temp32 );
// Primary processor-based VM-execution controls 00004002H
// IA32_VMX_PROCBASED_CTLS MSR (index 482H)
ReadMSR( 0x482 );
Log( "Proc-based allowed-0" , msr.Lo );
Log( "Proc-based allowed-1" , msr.Hi );
temp32 = 0;
temp32 |= msr.Lo;
temp32 &= msr.Hi;
Log( "Setting Pri Proc-Based Controls Mask" , temp32 );
WriteVMCS( 0x00004002, temp32 );
// Get the CR3-target count, MSR store/load counts, et cetera
//
// IA32_VMX_MISC MSR (index 485H)
ReadMSR( 0x485 );
Log( "Misc Data" , msr.Lo );
//Log( "Misc Data" , msr.Hi );
RtlCopyBytes( &misc_data, &msr.Lo, 4 );
Log( " ActivityStates" , misc_data.ActivityStates );
Log( " CR3Targets" , misc_data.CR3Targets );
Log( " MaxMSRs" , misc_data.MaxMSRs );
// VM-exit controls 0000400CH
// IA32_VMX_EXIT_CTLS MSR (index 483H)
ReadMSR( 0x483 );
Log( "Exit controls allowed-0" , msr.Lo );
Log( "Exit controls allowed-1" , msr.Hi );
temp32 = 0;
temp32 |= msr.Lo;
temp32 &= msr.Hi;
SetBit( &temp32, 15 ); // Acknowledge Interrupt On Exit
Log( "Setting VM-Exit Controls Mask" , temp32 );
WriteVMCS( 0x0000400C, temp32 );
// VM-entry controls 00004012H
// IA32_VMX_ENTRY_CTLS MSR (index 484H)
ReadMSR( 0x484 );
Log( "VMX Entry allowed-0" , msr.Lo );
Log( "VMX Entry allowed-1" , msr.Hi );
temp32 = 0;
temp32 |= msr.Lo;
temp32 &= msr.Hi;
ClearBit( &temp32 , 9 ); // IA-32e Mode Guest Disable
Log( "Setting VM-Entry Controls Mask" , temp32 );
WriteVMCS( 0x00004012, temp32 );
// ***************************************
// * *
// * H.3.3 32-Bit Guest-State Fields *
// * *
// ***************************************
//
// Guest ES limit 00004800H
__asm MOV seg_selector, ES
temp32 = 0;
temp32 = GetSegmentDescriptorLimit( gdt_base, seg_selector );
Log( "Setting Guest ES limit" , 0xFFFFFFFF );
WriteVMCS( 0x00004800, 0xFFFFFFFF );
// Guest CS limit 00004802H
__asm MOV seg_selector, CS
temp32 = 0;
temp32 = GetSegmentDescriptorLimit( gdt_base, seg_selector );
Log( "Setting Guest CS limit" , 0xFFFFFFFF );
WriteVMCS( 0x00004802, 0xFFFFFFFF );
// Guest SS limit 00004804H
__asm MOV seg_selector, SS
temp32 = 0;
temp32 = GetSegmentDescriptorLimit( gdt_base, seg_selector );
Log( "Setting Guest SS limit" , 0xFFFFFFFF );
WriteVMCS( 0x00004804, 0xFFFFFFFF );
// Guest DS limit 00004806H
__asm MOV seg_selector, DS
temp32 = 0;
temp32 = GetSegmentDescriptorLimit( gdt_base, seg_selector );
Log( "Setting Guest DS limit" , 0xFFFFFFFF );
WriteVMCS( 0x00004806, 0xFFFFFFFF );
// Guest FS limit 00004808H
__asm MOV seg_selector, FS
temp32 = 0;
temp32 = GetSegmentDescriptorLimit( gdt_base, seg_selector );
Log( "Setting Guest FS limit" , 0x00001000 );
WriteVMCS( 0x00004808, 0x00001000 );
// Guest GS limit 0000480AH
__asm MOV seg_selector, GS
temp32 = 0;
temp32 = GetSegmentDescriptorLimit( gdt_base, seg_selector );
Log( "Setting Guest GS limit" , 0xFFFFFFFF );
WriteVMCS( 0x0000480A, 0xFFFFFFFF );
// Guest TR limit 0000480EH
__asm
{
PUSH EAX
STR AX
MOV mLDT, AX
POP EAX
}
temp32 = 0;
temp32 = GetSegmentDescriptorLimit( gdt_base, mLDT );
Log( "Setting Guest TR limit" , temp32 );
WriteVMCS( 0x0000480E, temp32 );
// Guest GDTR limit 00004810H
Log( "Setting Guest GDTR limit" , gdt_reg.Limit );
WriteVMCS( 0x00004810, gdt_reg.Limit );
// Guest IDTR limit 00004812H
Log( "Setting Guest IDTR limit" , idt_reg.Limit );
WriteVMCS( 0x00004812, idt_reg.Limit );
__asm MOV seg_selector, CS
temp32 = seg_selector;
temp32 >>= 3;
temp32 *= 8;
temp32 += (gdt_base + 5); // CS Segment Descriptor
__asm
{
PUSHAD
MOV EAX, temp32
MOV EBX, [EAX]
MOV temp32, EBX
POPAD
}
temp32 &= 0x0000F0FF;
Log( "Setting Guest CS access rights" , temp32 );
WriteVMCS( 0x00004816, temp32 );
__asm MOV seg_selector, DS
temp32 = seg_selector;
temp32 >>= 3;
temp32 *= 8;
temp32 += (gdt_base + 5); // DS Segment Descriptor
__asm
{
PUSHAD
MOV EAX, temp32
MOV EBX, [EAX]
MOV temp32, EBX
POPAD
}
temp32 &= 0x0000F0FF;
Log( "Setting Guest DS access rights" , temp32 );
WriteVMCS( 0x0000481A, temp32 );
__asm MOV seg_selector, ES
temp32 = seg_selector;
temp32 >>= 3;
temp32 *= 8;
temp32 += (gdt_base + 5); // ES Segment Descriptor
__asm
{
PUSHAD
MOV EAX, temp32
MOV EBX, [EAX]
MOV temp32, EBX
POPAD
}
temp32 &= 0x0000F0FF;
Log( "Setting Guest ES access rights" , temp32 );
WriteVMCS( 0x00004814, temp32 );
__asm MOV seg_selector, FS
temp32 = seg_selector;
temp32 >>= 3;
temp32 *= 8;
temp32 += (gdt_base + 5); // FS Segment Descriptor
__asm
{
PUSHAD
MOV EAX, temp32
MOV EBX, [EAX]
MOV temp32, EBX
POPAD
}
temp32 &= 0x0000F0FF;
temp32 &= 0xFFFF7FFF; // Granularity Bit = 0
Log( "Setting Guest FS access rights" , temp32 );
WriteVMCS( 0x0000481C, temp32 );
__asm MOV seg_selector, GS
temp32 = seg_selector;
temp32 >>= 3;
temp32 *= 8;
temp32 += (gdt_base + 5); // GS Segment Descriptor
__asm
{
PUSHAD
MOV EAX, temp32
MOV EBX, [EAX]
MOV temp32, EBX
POPAD
}
temp32 &= 0x0000F0FF;
SetBit( &temp32, 16 ); // Unusable
Log( "Setting Guest GS access rights" , temp32 );
WriteVMCS( 0x0000481E, temp32 );
__asm MOV seg_selector, SS
temp32 = seg_selector;
temp32 >>= 3;
temp32 *= 8;
temp32 += (gdt_base + 5); // SS Segment Descriptor
__asm
{
PUSHAD
MOV EAX, temp32
MOV EBX, [EAX]
MOV temp32, EBX
POPAD
}
temp32 &= 0x0000F0FF;
Log( "Setting Guest SS access rights" , temp32 );
WriteVMCS( 0x00004818, temp32 );
__asm STR seg_selector
temp32 = seg_selector;
temp32 >>= 3;
temp32 *= 8;
temp32 += (gdt_base + 5); // TR Segment Descriptor
__asm
{
PUSHAD
MOV EAX, temp32
MOV EBX, [EAX]
MOV temp32, EBX
POPAD
}
temp32 &= 0x0000F0FF;
Log( "Setting Guest TR access rights" , temp32 );
WriteVMCS( 0x00004822, temp32 );
// Guest LDTR access rights 00004820H
temp32 = 0;
SetBit( &temp32, 16 ); // Unusable
Log( "Setting Guest LDTR access rights" , temp32 );
WriteVMCS( 0x00004820, temp32 );
// Guest IA32_SYSENTER_CS 0000482AH
// (174H)
ReadMSR( 0x174 );
Log( "Setting Guest IA32_SYSENTER_CS" , (ULONG)msr.Lo );
WriteVMCS( 0x0000482A, msr.Lo );
// **************************************
// * *
// * H.3.4 32-Bit Host-State Fields *
// * *
// **************************************
//
// Host IA32_SYSENTER_CS 00004C00H
// (174H)
ReadMSR( 0x174 );
Log( "Setting Host IA32_SYSENTER_CS" , (ULONG)msr.Lo );
WriteVMCS( 0x00004C00, msr.Lo );
// **********************************************
// * *
// * H.4.3 Natural-Width Guest-State Fields *
// * *
// **********************************************
//
// Guest CR0 00006800H
__asm
{
PUSH EAX
MOV EAX, CR0
MOV temp32, EAX
POP EAX
}
ReadMSR( 0x486 ); // IA32_VMX_CR0_FIXED0
Log( "IA32_VMX_CR0_FIXED0" , msr.Lo );
ReadMSR( 0x487 ); // IA32_VMX_CR0_FIXED1
Log( "IA32_VMX_CR0_FIXED1" , msr.Lo );
SetBit( &temp32, 0 ); // PE
SetBit( &temp32, 5 ); // NE
SetBit( &temp32, 31 ); // PG
Log( "Setting Guest CR0" , temp32 );
WriteVMCS( 0x00006800, temp32 );
// Guest CR3 00006802H
__asm
{
PUSH EAX
_emit 0x0F // MOV EAX, CR3
_emit 0x20
_emit 0xD8
MOV temp32, EAX
POP EAX
}
Log( "Setting Guest CR3" , temp32 );
WriteVMCS( 0x00006802, temp32 );
// Guest CR4 00006804H
__asm
{
PUSH EAX
_emit 0x0F // MOV EAX, CR4
_emit 0x20
_emit 0xE0
MOV temp32, EAX
POP EAX
}
ReadMSR( 0x488 ); // IA32_VMX_CR4_FIXED0
Log( "IA32_VMX_CR4_FIXED0" , msr.Lo );
ReadMSR( 0x489 ); // IA32_VMX_CR4_FIXED1
Log( "IA32_VMX_CR4_FIXED1" , msr.Lo );
SetBit( &temp32, 13 ); // VMXE
Log( "Setting Guest CR4" , temp32 );
WriteVMCS( 0x00006804, temp32 );
// Guest ES base 00006806H
__asm MOV seg_selector, ES
temp32 = 0;
temp32 = GetSegmentDescriptorBase( gdt_base , seg_selector );
Log( "Setting Guest ES Base" , temp32 );
WriteVMCS( 0x00006806, temp32 );
// Guest CS base 00006808H
__asm MOV seg_selector, CS
temp32 = 0;
temp32 = GetSegmentDescriptorBase( gdt_base , seg_selector );
Log( "Setting Guest CS Base" , temp32 );
WriteVMCS( 0x00006808, temp32 );
// Guest SS base 0000680AH
__asm MOV seg_selector, SS
temp32 = 0;
temp32 = GetSegmentDescriptorBase( gdt_base , seg_selector );
Log( "Setting Guest SS Base" , temp32 );
WriteVMCS( 0x0000680A, temp32 );
// Guest DS base 0000680CH
__asm MOV seg_selector, DS
temp32 = 0;
temp32 = GetSegmentDescriptorBase( gdt_base , seg_selector );
Log( "Setting Guest DS Base" , temp32 );
WriteVMCS( 0x0000680C, temp32 );
// Guest FS base 0000680EH
__asm MOV seg_selector, FS
temp32 = 0;
temp32 = GetSegmentDescriptorBase( gdt_base , seg_selector );
Log( "Setting Guest FS Base" , temp32 );
WriteVMCS( 0x0000680E, temp32 );
// Guest TR base 00006814H
__asm
{
PUSH EAX
STR AX
MOV mLDT, AX
POP EAX
}
temp32 = 0;
temp32 = GetSegmentDescriptorBase( gdt_base , mLDT );
Log( "Setting Guest TR Base" , temp32 );
WriteVMCS( 0x00006814, temp32 );
// Guest GDTR base 00006816H
__asm
{
SGDT gdt_reg
}
temp32 = 0;
temp32 = gdt_reg.BaseHi;
temp32 <<= 16;
temp32 |= gdt_reg.BaseLo;
Log( "Setting Guest GDTR Base" , temp32 );
WriteVMCS( 0x00006816, temp32 );
// Guest IDTR base 00006818H
__asm
{
SIDT idt_reg
}
temp32 = 0;
temp32 = idt_reg.BaseHi;
temp32 <<= 16;
temp32 |= idt_reg.BaseLo;
Log( "Setting Guest IDTR Base" , temp32 );
WriteVMCS( 0x00006818, temp32 );
// Guest RFLAGS 00006820H
__asm
{
PUSHAD
PUSHFD
MOV EAX, 0x00006820
// VMWRITE EAX, [ESP]
_emit 0x0F
_emit 0x79
_emit 0x04
_emit 0x24
POP eFlags
POPAD
}
Log( "Guest EFLAGS" , eFlags );
// Guest IA32_SYSENTER_ESP 00006824H
// MSR (175H)
ReadMSR( 0x175 );
Log( "Setting Guest IA32_SYSENTER_ESP" , msr.Lo );
WriteVMCS( 0x00006824, msr.Lo );
// Guest IA32_SYSENTER_EIP 00006826H
// MSR (176H)
ReadMSR( 0x176 );
Log( "Setting Guest IA32_SYSENTER_EIP" , msr.Lo );
WriteVMCS( 0x00006826, msr.Lo );
// *********************************************
// * *
// * H.4.4 Natural-Width Host-State Fields *
// * *
// *********************************************
//
// Host CR0 00006C00H
__asm
{
PUSH EAX
MOV EAX, CR0
MOV temp32, EAX
POP EAX
}
SetBit( &temp32, 5 ); // Set NE Bit
Log( "Setting Host CR0" , temp32 );
WriteVMCS( 0x00006C00, temp32 );
// Host CR3 00006C02H
__asm
{
PUSH EAX
_emit 0x0F // MOV EAX, CR3
_emit 0x20
_emit 0xD8
MOV temp32, EAX
POP EAX
}
Log( "Setting Host CR3" , temp32 );
WriteVMCS( 0x00006C02, temp32 );
// Host CR4 00006C04H
__asm
{
PUSH EAX
_emit 0x0F // MOV EAX, CR4
_emit 0x20
_emit 0xE0
MOV temp32, EAX
POP EAX
}
Log( "Setting Host CR4" , temp32 );
WriteVMCS( 0x00006C04, temp32 );
// Host FS base 00006C06H
__asm MOV seg_selector, FS
temp32 = 0;
temp32 = GetSegmentDescriptorBase( gdt_base , seg_selector );
Log( "Setting Host FS Base" , temp32 );
WriteVMCS( 0x00006C06, temp32 );
// Host TR base 00006C0AH
__asm
{
PUSH EAX
STR AX
MOV mLDT, AX
POP EAX
}
temp32 = 0;
temp32 = GetSegmentDescriptorBase( gdt_base , mLDT );
Log( "Setting Host TR Base" , temp32 );
WriteVMCS( 0x00006C0A, temp32 );
// Host GDTR base 00006C0CH
__asm
{
SGDT gdt_reg
}
temp32 = 0;
temp32 = gdt_reg.BaseHi;
temp32 <<= 16;
temp32 |= gdt_reg.BaseLo;
Log( "Setting Host GDTR Base" , temp32 );
WriteVMCS( 0x00006C0C, temp32 );
// Host IDTR base 00006C0EH
__asm
{
SIDT idt_reg
}
temp32 = 0;
temp32 = idt_reg.BaseHi;
temp32 <<= 16;
temp32 |= idt_reg.BaseLo;
Log( "Setting Host IDTR Base" , temp32 );
WriteVMCS( 0x00006C0E, temp32 );
// Host IA32_SYSENTER_ESP 00006C10H
// MSR (175H)
ReadMSR( 0x175 );
Log( "Setting Host IA32_SYSENTER_ESP" , msr.Lo );
WriteVMCS( 0x00006C10, msr.Lo );
// Host IA32_SYSENTER_EIP 00006C12H
// MSR (176H)
ReadMSR( 0x176 );
Log( "Setting Host IA32_SYSENTER_EIP" , msr.Lo );
WriteVMCS( 0x00006C12, msr.Lo );
// (5) Issue a sequence of VMWRITEs to initialize various host-state area
// fields in the working VMCS. The initialization sets up the context
// and entry-points to the VMM VIRTUAL-MACHINE MONITOR PROGRAMMING
// CONSIDERATIONS upon subsequent VM exits from the guest. Host-state
// fields include control registers (CR0, CR3 and CR4), selector fields
// for the segment registers (CS, SS, DS, ES, FS, GS and TR), and base-
// address fields (for FS, GS, TR, GDTR and IDTR; RSP, RIP and the MSRs
// that control fast system calls).
//
// (6) Use VMWRITEs to set up the various VM-exit control fields, VM-entry
// control fields, and VM-execution control fields in the VMCS. Care
// should be taken to make sure the settings of individual fields match
// the allowed 0 and 1 settings for the respective controls as reported
// by the VMX capability MSRs (see Appendix G). Any settings inconsistent
// with the settings reported by the capability MSRs will cause VM
// entries to fail.
// (7) Use VMWRITE to initialize various guest-state area fields in the
// working VMCS. This sets up the context and entry-point for guest
// execution upon VM entry. Chapter 22 describes the guest-state loading
// and checking done by the processor for VM entries to protected and
// virtual-8086 guest execution.
//
// Clear the VMX Abort Error Code prior to VMLAUNCH
//
RtlZeroMemory( (pVMCSRegion + 4), 4 );
Log( "Clearing VMX Abort Error Code" , *(pVMCSRegion + 4) );
// Set EIP, ESP for the Guest right before calling VMLAUNCH
//
Log( "Setting Guest ESP" , GuestStack );
WriteVMCS( 0x0000681C, (ULONG)GuestStack );
Log( "Setting Guest EIP" , GuestReturn );
WriteVMCS( 0x0000681E, (ULONG)GuestReturn );
/*
// Allocate some stack space for the VMEntry and VMMHandler.
//
HighestAcceptableAddress.QuadPart = 0xFFFFFFFF;
FakeStack = MmAllocateContiguousMemory( 0x2000, HighestAcceptableAddress );
Log( "FakeStack" , FakeStack );
*/
// Set EIP, ESP for the Host right before calling VMLAUNCH
//
Log( "Setting Host ESP" , ((ULONG)FakeStack + 0x1FFF) );
WriteVMCS( 0x00006C14, ((ULONG)FakeStack + 0x1FFF) );
Log( "Setting Host EIP" , VMMEntryPoint );
WriteVMCS( 0x00006C16, (ULONG)VMMEntryPoint );
// //
// VMLAUNCH //
// //
__asm
{
_emit 0x0F // VMLAUNCH
_emit 0x01
_emit 0xC2
}
__asm
{
PUSHFD
POP eFlags
}
Log( "VMLAUNCH Failure" , 0xDEADF00D )
if( eFlags.CF != 0 || eFlags.ZF != 0 || TRUE )
{
//
// Get the ERROR number using VMCS field 00004400H
//
__asm
{
PUSHAD
MOV EAX, 0x00004400
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV ErrorCode, EBX
POPAD
}
Log( "VM Instruction Error" , ErrorCode );
}
Abort:
ScrubTheLaunch = 1;
__asm
{
MOV ESP, GuestStack
JMP GuestReturn
}
}
// //
// DriverUnload //
// //
VOID DriverUnload( IN PDRIVER_OBJECT DriverObject )
{
ULONG ExitEFlags = 0;
ULONG ExitEAX = 0;
ULONG ExitECX = 0;
ULONG ExitEDX = 0;
ULONG ExitEBX = 0;
ULONG ExitESP = 0;
ULONG ExitEBP = 0;
ULONG ExitESI = 0;
ULONG ExitEDI = 0;
DbgPrint( "[vmm-unload] Active Processor Bitmap [%08X]\n", (ULONG)KeQueryActiveProcessors( ) );
DbgPrint( "[vmm-unload] Disabling VMX mode on CPU 0.\n" );
KeSetSystemAffinityThread( (KAFFINITY) 0x00000001 );
__asm
{
PUSHAD
MOV EAX, 0x12345678
_emit 0x0F // VMCALL
_emit 0x01
_emit 0xC1
POPAD
}
DbgPrint( "[vmm-unload] Freeing memory regions.\n" );
MmFreeNonCachedMemory( pVMXONRegion , 4096 );
MmFreeNonCachedMemory( pVMCSRegion , 4096 );
ExFreePoolWithTag( FakeStack, 'HmmV' );
DbgPrint( "[vmm-unload] Driver Unloaded.\n");
}
// //
// Driver Entry //
// //
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
ULONG EntryEFlags = 0;
ULONG cr4 = 0;
ULONG EntryEAX = 0;
ULONG EntryECX = 0;
ULONG EntryEDX = 0;
ULONG EntryEBX = 0;
ULONG EntryESP = 0;
ULONG EntryEBP = 0;
ULONG EntryESI = 0;
ULONG EntryEDI = 0;
DriverObject->DriverUnload = DriverUnload;
Log( "Driver Routines" , 0 );
Log( "---------------" , 0 );
Log( " Driver Entry", DriverEntry );
Log( " Driver Unload", DriverUnload );
Log( " StartVMX", StartVMX );
Log( " VMMEntryPoint", VMMEntryPoint );
// Check if PAE is enabled.
//
__asm
{
PUSH EAX
_emit 0x0F // MOV EAX, CR4
_emit 0x20
_emit 0xE0
MOV cr4, EAX
POP EAX
}
/*if( cr4 & 0x00000020 )
{
Log( "******************************" , 0 );
Log( "Error : PAE must be disabled." , 0 );
Log( "Add the following to boot.ini:" , 0 );
Log( " /noexecute=alwaysoff /nopae" , 0 );
Log( "******************************" , 0 );
return STATUS_UNSUCCESSFUL;
}*/
// Allocate the VMXON region memory.
//
pVMXONRegion = MmAllocateNonCachedMemory( 4096 );
if( pVMXONRegion == NULL )
{
Log( "ERROR : Allocating VMXON Region memory." , 0 );
return STATUS_UNSUCCESSFUL;
}
Log( "VMXONRegion virtual address" , pVMXONRegion );
RtlZeroMemory( pVMXONRegion, 4096 );
PhysicalVMXONRegionPtr = MmGetPhysicalAddress( pVMXONRegion );
Log( "VMXONRegion physical address" , PhysicalVMXONRegionPtr.LowPart );
// Allocate the VMCS region memory.
//
pVMCSRegion = MmAllocateNonCachedMemory( 4096 );
if( pVMCSRegion == NULL )
{
Log( "ERROR : Allocating VMCS Region memory." , 0 );
MmFreeNonCachedMemory( pVMXONRegion , 4096 );
return STATUS_UNSUCCESSFUL;
}
Log( "VMCSRegion virtual address" , pVMCSRegion );
RtlZeroMemory( pVMCSRegion, 4096 );
PhysicalVMCSRegionPtr = MmGetPhysicalAddress( pVMCSRegion );
Log( "VMCSRegion physical address" , PhysicalVMCSRegionPtr.LowPart );
// Allocate stack for the VM Exit Handler.
//
FakeStack = ExAllocatePoolWithTag( NonPagedPool , 0x2000, 'HmmV' );
if( FakeStack == NULL )
{
Log( "ERROR : Allocating VM Exit Handler stack memory." , 0 );
MmFreeNonCachedMemory( pVMXONRegion , 4096 );
MmFreeNonCachedMemory( pVMCSRegion , 4096 );
return STATUS_UNSUCCESSFUL;
}
Log( "FakeStack" , FakeStack );
__asm
{
CLI
MOV GuestStack, ESP
}
// Save the state of the architecture.
//
__asm
{
PUSHAD
POP EntryEDI
POP EntryESI
POP EntryEBP
POP EntryESP
POP EntryEBX
POP EntryEDX
POP EntryECX
POP EntryEAX
PUSHFD
POP EntryEFlags
}
StartVMX( );
// Restore the state of the architecture.
//
__asm
{
PUSH EntryEFlags
POPFD
PUSH EntryEAX
PUSH EntryECX
PUSH EntryEDX
PUSH EntryEBX
PUSH EntryESP
PUSH EntryEBP
PUSH EntryESI
PUSH EntryEDI
POPAD
}
__asm
{
STI
MOV ESP, GuestStack
}
Log( "Running on Processor" , KeGetCurrentProcessorNumber() );
if( ScrubTheLaunch == 1 )
{
Log( "ERROR : Launch aborted." , 0 );
MmFreeNonCachedMemory( pVMXONRegion , 4096 );
MmFreeNonCachedMemory( pVMCSRegion , 4096 );
ExFreePoolWithTag( FakeStack, 'HmmV' );
return STATUS_UNSUCCESSFUL;
}
Log( "VM is now executing." , 0 );
return STATUS_SUCCESS;
}
ULONG ExitReason;
ULONG ExitQualification;
ULONG ExitInterruptionInformation;
ULONG ExitInterruptionErrorCode;
ULONG IDTVectoringInformationField;
ULONG IDTVectoringErrorCode;
ULONG ExitInstructionLength;
ULONG ExitInstructionInformation;
ULONG GuestEIP;
ULONG GuestResumeEIP;
ULONG GuestESP;
ULONG GuestCS;
ULONG GuestCR0;
ULONG GuestCR3;
ULONG GuestCR4;
ULONG GuestEFLAGS;
ULONG GuestEAX;
ULONG GuestEBX;
ULONG GuestECX;
ULONG GuestEDX;
ULONG GuestEDI;
ULONG GuestESI;
ULONG GuestEBP;
ULONG movcrControlRegister;
ULONG movcrAccessType;
ULONG movcrOperandType;
ULONG movcrGeneralPurposeRegister;
ULONG movcrLMSWSourceData;
ULONG ErrorCode;
///
// //
// VMM Entry Point //
// //
///
__declspec( naked ) VOID VMMEntryPoint( )
{
__asm CLI
__asm PUSHAD
//
// Record the General-Purpose registers.
//
__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
///
// //
// Exit Reason // 0x00004400
// //
///
__asm
{
PUSHAD
MOV EAX, 0x00004402
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV ExitReason, EBX
POPAD
}
if( ExitReason == 0x0000000A || // CPUID
ExitReason == 0x00000012 || // VMCALL
ExitReason == 0x0000001C || // MOV CR
ExitReason == 0x0000001F || // RDMSR
ExitReason == 0x00000020 || // WRMSR
( ExitReason > 0x00000012 && ExitReason < 0x0000001C ) )
{
HandlerLogging = 0;
}
else
{
HandlerLogging = 1;
}
if( HandlerLogging )
{
Log( "----- VMM Handler CPU0 -----", 0 );
Log( "Guest EAX" , GuestEAX );
Log( "Guest EBX" , GuestEBX );
Log( "Guest ECX" , GuestECX );
Log( "Guest EDX" , GuestEDX );
Log( "Guest EDI" , GuestEDI );
Log( "Guest ESI" , GuestESI );
Log( "Guest EBP" , GuestEBP );
Log( "Exit Reason" , ExitReason );
}
//
// //
// Exit Qualification // 00006400H
// //
//
__asm
{
PUSHAD
MOV EAX, 0x00006400
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV ExitQualification, EBX
POPAD
}
if( HandlerLogging ) Log( "Exit Qualification" , ExitQualification );
// //
// VM-Exit Interruption Information // 00004404H
// //
__asm
{
PUSHAD
MOV EAX, 0x00004404
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV ExitInterruptionInformation, EBX
POPAD
}
if( HandlerLogging ) Log( "Exit Interruption Information" , ExitInterruptionInformation );
///
// //
// VM-Exit Interruption Error Code // 00004406H
// //
///
__asm
{
PUSHAD
MOV EAX, 0x00004406
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV ExitInterruptionErrorCode, EBX
POPAD
}
if( HandlerLogging ) Log( "Exit Interruption Error Code" , ExitInterruptionErrorCode );
///
// //
// IDT-Vectoring Information Field // 00004408H
// //
///
__asm
{
PUSHAD
MOV EAX, 0x00004408
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV IDTVectoringInformationField, EBX
POPAD
}
if( HandlerLogging ) Log( "IDT-Vectoring Information Field" , IDTVectoringInformationField );
// //
// IDT-Vectoring Error Code // 0000440AH
// //
__asm
{
PUSHAD
MOV EAX, 0x0000440A
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV IDTVectoringErrorCode, EBX
POPAD
}
if( HandlerLogging ) Log( "IDT-Vectoring Error Code" , IDTVectoringErrorCode );
//
// //
// VM-Exit Instruction Length // 0000440CH
// //
//
__asm
{
PUSHAD
MOV EAX, 0x0000440C
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV ExitInstructionLength, EBX
POPAD
}
if( HandlerLogging ) Log( "VM-Exit Instruction Length" , ExitInstructionLength );
///
// //
// VM-Exit Instruction Information // 0000440EH
// //
///
__asm
{
PUSHAD
MOV EAX, 0x0000440E
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV ExitInstructionInformation, EBX
POPAD
}
if( HandlerLogging ) Log( "VM-Exit Instruction Information" , ExitInstructionInformation );
/
// //
// Guest EIP //
// //
/
__asm
{
PUSHAD
MOV EAX, 0x0000681E
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV GuestEIP, EBX
POPAD
}
if( HandlerLogging ) Log( "VM Exit EIP" , GuestEIP );
//
// Writing the Guest VMCS EIP uses general registers.
// Must complete this before setting general registers
// for guest return state.
//
GuestResumeEIP = GuestEIP + ExitInstructionLength;
WriteVMCS( 0x0000681E, (ULONG)GuestResumeEIP );
/
// //
// Guest ESP //
// //
/
__asm
{
PUSHAD
MOV EAX, 0x0000681C
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV GuestESP, EBX
POPAD
}
if( HandlerLogging ) Log( "VM Exit ESP" , GuestESP );
// //
// Guest CS //
// //
__asm
{
PUSHAD
MOV EAX, 0x00000802
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV GuestCS, EBX
POPAD
}
if( HandlerLogging ) Log( "VM Exit CS" , GuestCS );
/
// //
// Guest CR0 //
// //
/
__asm
{
PUSHAD
MOV EAX, 0x00006800
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV GuestCR0, EBX
POPAD
}
if( HandlerLogging ) Log( "VM Exit CR0" , GuestCR0 );
/
// //
// Guest CR3 //
// //
/
__asm
{
PUSHAD
MOV EAX, 0x00006802
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV GuestCR3, EBX
POPAD
}
if( HandlerLogging ) Log( "VM Exit CR3" , GuestCR3 );
/
// //
// Guest CR4 //
// //
/
__asm
{
PUSHAD
MOV EAX, 0x00006804
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV GuestCR4, EBX
POPAD
}
if( HandlerLogging ) Log( "VM Exit CR4" , GuestCR4 );
// //
// Guest EFLAGS //
// //
__asm
{
PUSHAD
MOV EAX, 0x00006820
_emit 0x0F // VMREAD EBX, EAX
_emit 0x78
_emit 0xC3
MOV GuestEFLAGS, EBX
POPAD
}
if( HandlerLogging ) Log( "VM Exit EFLAGS" , GuestEFLAGS );
/
// //
// *** EXIT REASON CHECKS START HERE *** //
// //
/
/
// //
// VMCLEAR, VMLAUNCH, VMPTRLD, VMPTRST, VMREAD, VMWRITE, VMRESUME, VMXOFF, VMXON //
// //
/
if( ExitReason > 0x00000012 && ExitReason < 0x0000001C )
{
Log( "Request has been denied - CPU0", ExitReason );
__asm
{
POPAD
JMP Resume
}
}
//
// //
// VMCALL //
// //
//
if( ExitReason == 0x00000012 )
{
Log( "VMCALL detected - CPU0" , 0 );
if( GuestEAX == 0x12345678 )
{
// Switch off VMX mode.
//
Log( "- Terminating VMX Mode.", 0xDEADDEAD );
__asm
{
_emit 0x0F // VMXOFF
_emit 0x01
_emit 0xC4
}
Log( "- Flow Control Return to Address" , GuestResumeEIP );
__asm
{
POPAD
MOV ESP, GuestESP
STI
JMP GuestResumeEIP
}
}
Log( "- Request has been denied." , ExitReason );
__asm
{
POPAD
JMP Resume
}
}
// //
// INVD //
// //
if( ExitReason == 0x0000000C )
{
Log( "INVD detected - CPU0" , 0 );
__asm
{
_emit 0x0F
_emit 0x08
POPAD
JMP Resume
}
}
/
// //
// RDMSR //
// //
/
if( ExitReason == 0x0000001F )
{
Log( "Read MSR - CPU0" , GuestECX );
__asm
{
POPAD
MOV ECX, GuestECX
_emit 0x0F
_emit 0x32
JMP Resume
}
}
/
// //
// WRMSR //
// //
/
if( ExitReason == 0x00000020 )
{
Log( "Write MSR - CPU0" , GuestECX );
__asm
{
POPAD
MOV ECX, GuestECX
MOV EAX, GuestEAX
MOV EDX, GuestEDX
_emit 0x0F
_emit 0x30
JMP Resume
}
}
/
// //
// CPUID //
// //
/
if( ExitReason == 0x0000000A )
{
if( HandlerLogging )
{
Log( "CPUID detected - CPU0", 0 );
Log( "- EAX", GuestEAX );
}
if( GuestEAX == 0x00000000 )
{
__asm
{
POPAD
MOV EAX, 0x00000000
CPUID
MOV EBX, 0x61656C43
MOV ECX, 0x2E636E6C
MOV EDX, 0x74614872
JMP Resume
}
}
__asm
{
POPAD
MOV EAX, GuestEAX
CPUID
JMP Resume
}
}
///
// //
// Control Register Access //
// //
///
if( ExitReason == 0x0000001C )
{
if( HandlerLogging ) Log( "Control Register Access detected.", 0 );
movcrControlRegister = ( ExitQualification & 0x0000000F );
movcrAccessType = ( ( ExitQualification & 0x00000030 ) >> 4 );
movcrOperandType = ( ( ExitQualification & 0x00000040 ) >> 6 );
movcrGeneralPurposeRegister = ( ( ExitQualification & 0x00000F00 ) >> 8 );
if( HandlerLogging )
{
Log( "- movcrControlRegister", movcrControlRegister );
Log( "- movcrAccessType", movcrAccessType );
Log( "- movcrOperandType", movcrOperandType );
Log( "- movcrGeneralPurposeRegister", movcrGeneralPurposeRegister );
}
// Control Register Access (CR3 <-- reg32)
//
if( movcrControlRegister == 3 && movcrAccessType == 0 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 0 )
{
WriteVMCS( 0x00006802, GuestEAX );
__asm POPAD
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 0 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 1 )
{
WriteVMCS( 0x00006802, GuestECX );
__asm POPAD
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 0 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 2 )
{
WriteVMCS( 0x00006802, GuestEDX );
__asm POPAD
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 0 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 3 )
{
WriteVMCS( 0x00006802, GuestEBX );
__asm POPAD
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 0 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 4 )
{
WriteVMCS( 0x00006802, GuestESP );
__asm POPAD
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 0 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 5 )
{
WriteVMCS( 0x00006802, GuestEBP );
__asm POPAD
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 0 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 6 )
{
WriteVMCS( 0x00006802, GuestESI );
__asm POPAD
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 0 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 7 )
{
WriteVMCS( 0x00006802, GuestEDI );
__asm POPAD
goto Resume;
}
// Control Register Access (reg32 <-- CR3)
//
if( movcrControlRegister == 3 && movcrAccessType == 1 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 0 )
{
__asm POPAD
__asm MOV EAX, GuestCR3
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 1 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 1 )
{
__asm POPAD
__asm MOV ECX, GuestCR3
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 1 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 2 )
{
__asm POPAD
__asm MOV EDX, GuestCR3
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 1 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 3 )
{
__asm POPAD
__asm MOV EBX, GuestCR3
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 1 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 4 )
{
__asm POPAD
__asm MOV ESP, GuestCR3
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 1 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 5 )
{
__asm POPAD
__asm MOV EBP, GuestCR3
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 1 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 6 )
{
__asm POPAD
__asm MOV ESI, GuestCR3
goto Resume;
}
if( movcrControlRegister == 3 && movcrAccessType == 1 && movcrOperandType == 0 && movcrGeneralPurposeRegister == 7 )
{
__asm POPAD
__asm MOV EDI, GuestCR3
goto Resume;
}
}
Exit:
//
// Switch off VMX mode.
//
Log( "Terminating VMX Mode.", 0xDEADDEAD );
__asm
{
_emit 0x0F // VMXOFF
_emit 0x01
_emit 0xC4
}
Log( "Flow Control Return to Address" , GuestEIP );
__asm
{
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
{
STI
_emit 0x0F // VMRESUME
_emit 0x01
_emit 0xC3
}
}
Virtual Machine Monitor source
最新推荐文章于 2024-10-04 21:19:32 发布