VT技术入门。
0x00 VT 技术介绍
0x01 VT 环境搭建
在最新的Windows环境上,可能需要关闭Hyper-V
,才能在VMware的虚拟机配置-处理器-虚拟化引擎中选中Intel VT-x/EPT
如果开启了Virtualization Based Security(基于虚拟化的安全性),请先关闭它
1 | reg ADD HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\DeviceGuard /v EnableVirtualizationBasedSecurity /t REG_DWORD /d 0 /f |
再关闭Hyper-V
1 | bcdedit /set hypervisorlaunchtype off |
如果只关闭Hyper-V,不关闭VBS,Hyper-V是关不掉的。
如果还开启了UEFI 锁等复杂情况,就要参考微软文档了。
0x02 总体流程
0x03 前置工作
CPUID
eax = 1
执行CPUID
,ecx[5] = 1
则支持VT-x
1 | int ret[4]; |
msr
检查VT-x是否在BIOS中被开启msr[0x3a]
结果中的最低为为lock位,需要置1
1 | uint64_t msr = __readmsr(0x3a); |
CR4
CR4
中的第13位VMXE
置1打开启用VMXE
指令扩展
1 | uintptr_t cr4 = __readcr4(); |
0x04 VMXON
启用VMX扩展
1 | void inline EnableVMXE() |
分配VMXON 区域
直接分配4k即可
1 | PHYSICAL_ADDRESS HighAddress = { 0 }; |
设置VMXON区域
设置VMXON区域的四字节为msr[0x480]的低32位
1 | uintptr_t msr = __readmsr(0x480); |
执行VMXON
指令,出错则CF位置1
1 | push _high |
0x05 VMCS
VMCS是Virtual Machine Control Structure
,描述VM的一些属性。
首先需要初始化VMCS,再选中此VMCS。
1 | // 初始化虚拟机 |
Intel 弄了一个标准的汇编指令来读写VMCS,vmwrite/vmread
,不推荐直接修改内存。
VMCS域 包括以下三方面的内容:
1.宿主机执行域
2.虚拟机执行域
3.虚拟机执行控制域
3.1 VM 执行控制(#VMExit
事件设置)44
3.2 VM 退出控制(#VMExit
保存内容设置)
3.3 VM 进入控制(进入#VMEntry
加载设置)
宿主机执行域
获取段描述符基址
1
2
3
4
5
6
7
8
9
10
11PVOID GetSegmentDescriptor(uint16_t index)
{
KGDTENTRY* GdtEntry = Asm_GetGdtBase();
KGDTENTRY TargetEntry = GdtEntry[index >> 3];
uintptr_t ret = TargetEntry.HighWord.Bytes.BaseHi;
ret <<= 8;
ret |= TargetEntry.HighWord.Bytes.BaseMid;
ret <<= 16;
ret |= TargetEntry.BaseLow;
return (PVOID)ret;
}HOST_RSP
的值,需要提前申请& 0xfff8
必须清除RPL,和TI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27__vmx_vmwrite(HOST_CR0, __readcr0());
__vmx_vmwrite(HOST_CR3, __readcr3());
__vmx_vmwrite(HOST_CR4, __readcr4());
__vmx_vmwrite(HOST_ES_SELECTOR, GetES() & 0xFFF8);
__vmx_vmwrite(HOST_CS_SELECTOR, GetCS() & 0xFFF8);
__vmx_vmwrite(HOST_DS_SELECTOR, GetDS() & 0xFFF8);
__vmx_vmwrite(HOST_FS_SELECTOR, GetFS() & 0xFFF8);
__vmx_vmwrite(HOST_GS_SELECTOR, GetGS() & 0xFFF8);
__vmx_vmwrite(HOST_SS_SELECTOR, GetSS() & 0xFFF8);
__vmx_vmwrite(HOST_TR_SELECTOR, GetTR() & 0xFFF8);
// KGDTENTRY* GdtEntry = Asm_GetGdtBase();
__vmx_vmwrite(HOST_TR_BASE, 0x80042000);
__vmx_vmwrite(HOST_GDTR_BASE, Asm_GetGdtBase());
__vmx_vmwrite(HOST_IDTR_BASE, Asm_GetIdtBase());
__vmx_vmwrite(HOST_IA32_SYSENTER_CS, __readmsr(MSR_IA32_SYSENTER_CS) & 0xFFFFFFFF);
__vmx_vmwrite(HOST_IA32_SYSENTER_ESP, __readmsr(MSR_IA32_SYSENTER_ESP) & 0xFFFFFFFF);
__vmx_vmwrite(HOST_IA32_SYSENTER_EIP, __readmsr(MSR_IA32_SYSENTER_EIP) & 0xFFFFFFFF); // KiFastCallEntry
__vmx_vmwrite(HOST_RSP, ((ULONG)g_VMXCPU.pStack) + 0x2000); //Host 临时栈
__vmx_vmwrite(HOST_RIP, (ULONG)VMMEntryPoint); //这里定义我们的VMM处理程序入口很好,Intel你要提供
HOST_TR_BASE
,HOST_FS_BASE
,HOST_GS_BASE
如果要它的话需要在GDT表找,然后拼接基地址。
这里偷了一份KGDTEntry的结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70typedef struct _KGDTENTRY
{
USHORT LimitLow; //0x0
USHORT BaseLow; //0x2
union
{
struct
{
UCHAR BaseMid; //0x4
UCHAR Flags1; //0x5
UCHAR Flags2; //0x6
UCHAR BaseHi; //0x7
} Bytes; //0x4
struct
{
ULONG BaseMid : 8; //0x4
ULONG Type : 5; //0x4
ULONG Dpl : 2; //0x4
ULONG Pres : 1; //0x4
ULONG LimitHi : 4; //0x4
ULONG Sys : 1; //0x4
ULONG Reserved_0 : 1; //0x4
ULONG Default_Big : 1; //0x4
ULONG Granularity : 1; //0x4
ULONG BaseHi : 8; //0x4
} Bits; //0x4
} HighWord; //0x4
} KGDTENTRY;
//0x10 bytes (sizeof)
typedef union _KGDTENTRY
{
struct
{
USHORT LimitLow; //0x0
USHORT BaseLow; //0x2
};
struct
{
UCHAR BaseMiddle; //0x4
UCHAR Flags1; //0x5
UCHAR Flags2; //0x6
UCHAR BaseHigh; //0x7
} Bytes; //0x4
struct
{
struct
{
ULONG BaseMiddle : 8; //0x4
ULONG Type : 5; //0x4
ULONG Dpl : 2; //0x4
ULONG Present : 1; //0x4
ULONG LimitHigh : 4; //0x4
ULONG System : 1; //0x4
ULONG LongMode : 1; //0x4
ULONG DefaultBig : 1; //0x4
ULONG Granularity : 1; //0x4
ULONG BaseHigh : 8; //0x4
} Bits; //0x4
ULONG BaseUpper; //0x8
};
struct
{
ULONG MustBeZero; //0xc
LONGLONG DataLow; //0x0
};
LONGLONG DataHigh; //0x8
}KGDTENTRY;虚拟机执行控制域
1 | `GUEST_ES_AR_BYTES`为ES段的属性,设置为`0x10000`不可用。 |
GuestEntry
是GUEST VM的入口点
__vmx_vmread(VM_INSTRUCTION_ERROR)
@24.9.1
VM 控制域
通过设置
Flags
标志位来控制VM的一些行为,Intel为了防止你写玩具VMM的时候把这段全填0,贴心的设置了一些预留位需要置1的位,真好。通过查询MSR寄存器来了解哪些预留位为1,哪些为0
比如
1
2
3
4
5
6
7
8
9
10
11
12kd> rdmsr 481
msr[481] = 0000003f`00000016
kd> .formats 0000003f`00000016
Evaluate expression:
Hex: 0000003f`00000016
Decimal: 270582939670
Octal: 0000000003740000000026
Binary: 00000000 00000000 00000000 00111111 00000000 00000000 00000000 00010110
Chars: ...?....
Time: Mon Jan 1 15:30:58.293 1601 (UTC + 8:00)
Float: low 3.08286e-044 high 8.82818e-044
Double: 1.33686e-312MSR值中前32位中二进制为0的位,设置对应域时该位必须为0
MSR值中后32位中二进制为1的位,设置对应域时该位必须为1
1
2
3
4
5
6
7ULONG VTAdjustExcuteControls(ULONG Value,ULONG msr)
{
uint64_t mask = __readmsr(msr);
Value &= (mask >> 32);
Value |= (mask & 0xffffffff);
return Value;
}VM 执行控制
Pin-Based VM-Execution Controls
基于CPU针脚的VM执行控制 (Intel 3a @24.6.1)定义发生外部中断、NMI等的是否退出虚拟机。
1
__vmx_vmwrite(PIN_BASED_VM_EXEC_CONTROL, VTAdjustExcuteControls(0, MSR_IA32_VMX_PINBASED_CTLS));
Processor-Based VM-Execution Controls
基于处理器的VM执行控制(Intel 3a @24.6.2)定义执行一些CPU指令(
HLT/INVLPG/RDTSC/CR3切换/内部中断
)是否需要退出虚拟机1
__vmx_vmwrite(CPU_BASED_VM_EXEC_CONTROL, VTAdjustExcuteControls(0, MSR_IA32_VMX_PROCBASED_CTLS));
VM 退出控制
Intel 3a@24.7.1
1
__vmx_vmwrite(VM_EXIT_CONTROLS, VTAdjustExcuteControls(0,MSR_IA32_VMX_EXIT_CTLS));
VM 进入控制
Intel 3a@24.7.1
1
__vmx_vmwrite(VM_ENTRY_CONTROLS, VTAdjustExcuteControls(0, MSR_IA32_VMX_ENTRY_CTLS));
0x06 VM 启动
启动后,CPU会跳到GUEST_RIP
,ESP设置为GUEST_RSP
1 | __asm{ |
GuestEntry
的定义
1 | __declspec(naked) void GuestEntry(void) |
0x06 #VMExit
当虚拟机执行到设置的#VMExit
的指令后,会退出虚拟机,执行HOST_RIP
函数。
1 | __declspec(naked) void VMExitHandler(void) |
VMExitHandlerDispatcher
函数用于处理#VMExit
事件:
ExitReason
指定了退出原因。
ExitInstructionLength
指定了导致退出指令的长度。
g_GuestRegs.eip = __vmx_vmread(GUEST_RIP);
读取了导致退出指令的地址。
处理完指令的退出后,设置GUEST_RIP
为下一跳指令。
1 | static void VMExitHandlerDispatcher(void) |
0x07 EPT
看了一下要重建页表我就不想看了。
0x08 代码
这个代码魔改的周壑的VT_Learn
,建议不要乱魔改,坑非常多。