要点回顾
在上一篇中,我们主要学习了如何填写Guest state fields的各项字段,以及如何对错误码进行排查,并最终成功执行了Guest代码。
在GuestEntry中,我们通过执行VMCALL指令触发VM-Exit,而没有用INT 3指令直接中断,其中是有原因的。
在本篇,我们将解释为什么没有在GuestEntry中使用INT 3中断,以及相关的调试技巧。
INT 3失效
将GuestEntry修改为以下代码:
void __declspec(naked) GuestEntry()
{
__asm {
mov ax, es
mov es, ax
mov ax, ds
mov ds, ax
mov ax, fs
mov fs, ax
mov ax, gs
mov gs, ax
mov ax, ss
mov ss, ax
int 3
}
}
执行结果:
不难发现,CPU并没有在Guest断下来,而是直接来到了Host,说明Guest在执行某行代码时抛出了VM-Exit信号。
此时,错误码为0x1C,查Intel手册卷3附录C能够得知含义是「Control-register accesses」,即控制寄存器访问。
那么在什么地方访问了控制寄存器呢?我们看下EIP所处的位置:
可以看到,是在执行INT 3中断处理程序的途中访问了Cr3寄存器,导致Guest抛出了VM-Exit信号,因此先触发了Host中的INT 3断点。
值得注意的是,当从Guest切换到Host时,权限也由VMX non-root切换到了VMX root,因此在Host中是能够正常执行INT 3的。
调试技巧
那么,如果想要在Guest中触发INT 3中断,应该怎么做呢?
本篇先记录一种最简单的方法。
步骤:
- Guest执行INT 3指令,触发VM-Exit,进入Host
- 立刻保存Guest的各项状态信息。
- 收集错误码等重要参数
- 将CPU还原成Guest触发VM-Exit时的状态,继续执行INT 3后半部分
- 由于此时只是改变了EIP,权限不变,因此继续执行INT 3时能够成功。
当然,本篇这种方法只是饮鸩止渴,并不是一劳永逸的,但其中的思想值得参考,更加成熟的方法将在后续给出。
代码实现:
static void VMMEntryPointEbd(void)
{
ULONG ExitReason;
ExitReason = Vmx_VmRead(VM_EXIT_REASON);
Log("ExitReason", ExitReason);
g_GuestRegs.esp = Vmx_VmRead(GUEST_RSP);
g_GuestRegs.eip = Vmx_VmRead(GUEST_RIP);
Log("g_GuestRegs.esp", g_GuestRegs.esp);
Log("g_GuestRegs.eip", g_GuestRegs.eip);
__asm
{
// 还原Guest状态,继续执行INT 3后半部分
mov eax, g_GuestRegs.eax
mov ecx, g_GuestRegs.ecx
mov edx, g_GuestRegs.edx
mov ebx, g_GuestRegs.ebx
mov esp, g_GuestRegs.esp
mov ebp, g_GuestRegs.ebp
mov esi, g_GuestRegs.esi
mov edi, g_GuestRegs.edi
push g_GuestRegs.eflags
popfd
//此时处于VMX root权限,因此不会再次触发VM-Exit
jmp g_GuestRegs.eip
}
}
void __declspec(naked) VMMEntryPoint(void)
{
__asm
{
//通过INT 3进来后,立刻保存Guest的状态
mov g_GuestRegs.eax, eax
mov g_GuestRegs.ecx, ecx
mov g_GuestRegs.edx, edx
mov g_GuestRegs.ebx, ebx
mov g_GuestRegs.esp, esp
mov g_GuestRegs.ebp, ebp
mov g_GuestRegs.esi, esi
mov g_GuestRegs.edi, edi
pushfd
pop eax
mov g_GuestRegs.eflags, eax
//需要设置fs和gs,否则无法正常运行
mov ax, fs
mov fs, ax
mov ax, gs
mov gs, ax
}
Log("VM Exit", 0);
//尽量不要在裸函数中定义局部变量,或实现太多功能,最好封装成函数
VMMEntryPointEbd();
}
执行结果:
可以看到,此时,已经成功在Guest中执行INT 3并中断。
参考资料
- VT虚拟化架构编写视频教程①~⑥课
- 周壑《VT技术入门》系列视频教程
- github项目:VT_Learn
- github项目: HyperPlatform
- Intel开发手册 卷3:Chapter 23 ~ Chapter 33
- x86内部函数列表