Intel VT学习笔记(九)—— EPT应用示例
内存保护
描述:尝试使用EPT将一块特定的物理内存保护起来。
先来选择一块物理地址,那么这里就跟周壑老师一样,选择「Page Fault」函数所在的物理地址。
首先使用PCHunter看一下虚拟地址是多少:
然后用WinDbg直接查看对应的物理地址:
可以看到「Page Fault」函数的物理地址是0x541450,即所属的物理页为0x541000。
那么,能不能让Guest能够执行它,但是不能够对它读写呢?
这个是不能随便设置的,需要看系统是否支持。(通过检测IA32_VMX_EPT_VPID_CAP MSR,具体参考Intel手册卷3附录A.10)
只有当这个IA32_VMX_EPT_VPID_CAP MSR的第0位为1时,才支持将EPT物理页面设置为可执行,但不可读写的权限(在笔者的环境中是支持的)。
代码实现:
if (0x541000 == *ept_PT & 0xfffff000) //寻找Page Fault函数所在的物理页
{
*ept_PT &= 0xfffffff4; //去除可读、可写的权限
}
运行结果:
此时能够成功开启VT,并且系统能够正常运行。
那么来查看「Page Fault」函数的反汇编试试:
产生了一个VM-Exit事件,退出码为0x30,对应的含义为「EPT violation」,表示EPT不允许Guest访问该页面。
EPT violation
描述:当EPT模式下访问物理内存(读、写、执行)失败时,会导致VM-Exit,退出码为0x30,即EPT violation。
可以在Host中获取具体的信息,然后进行相应的处理。
EPT violation的具体信息包含的含义如下(参考Intel开发手册卷3表27-7):
可以通过该退出码检测Guest的行为,如果是读/写的时候,返回一个假页面给Guest;如果是执行时,返回一个真页面。
代码实现
//ept.c
...
ULONG64 fake_page_PA; //假页面
ULONG64* hook_ept_PT; //用于替换页面
static void AllocateFakePage()
{
PVOID page = AllocateOnePage();
fake_page_PA = MmGetPhysicalAddress(page).QuadPart;
}
...
if (0x541000 == (*ept_PT & 0xfffff000)) //寻找Page Fault函数所在的物理页
{
AllocateFakePage(); //分配一个假页面
*ept_PT &= 0xfffffff4; //真页面去除可读、可写的权限
hook_ept_PT = ept_PT;
}
...
//exithandler.c
...
extern ULONG64 fake_page_PA;
extern ULONG64* hook_ept_PT;
void HandleEptViolation()
{
ULONG ExitQualification;
ExitQualification = Vmx_VmRead(EXIT_QUALIFICATION);
//判断访问权限,如果是读/写,就给一个假页面,如果是执行,就给一个真页面
if (ExitQualification & 3) // 读/写
{
*hook_ept_PT = fake_page_PA | 0x33; //给假页面,权限为读/写
}
else // 执行
{
*hook_ept_PT = 0x541000 | 0x34; //给真页面,权限为执行
}
}
...
case EXIT_REASON_EPT_VIOLATION:
ExitInstructionLength = 0; //将指令长度改为0,表示不跳过指令
HandleEptViolation(); //处理
break;
...
运行结果:
可以看到,此时读取「Page Fault」函数的具体代码时,返回的内容全是0,因为在读/写时,Host挂入了一个假页面。
并且PCHunter将这个页面的所有函数都认定为被Inline Hook了:
那么我们的目的也就达到了。
关于VT,笔者暂时先学习到这里,不得不说收获了很多知识,感觉VT能做的事情远不止如此,等有时间再深入研究一番。
参考资料
- VT虚拟化架构编写视频教程①~⑥课
- 周壑《VT技术入门》系列视频教程
- github项目:VT_Learn
- github项目: HyperPlatform
- Intel开发手册 卷3:Chapter 23 ~ Chapter 33
- x86内部函数列表
- 科普下VT EPT