前言
本文只是浅浅地了解VT技术。
什么是VT技术
对于虚拟化技术,分为软件虚拟化和硬件虚拟化。而VT(Intel Virtualization Technology)技术,这种技术让可以让一个CPU工作起来就像多个CPU并行运行,允许一个平台同时运行多个操作系统。
主要流程(框架)
图片截取自周壑大佬的bilibili VT课
周壑大佬将其进行了一个类比
●开锁(检测是否支持VT)
cr4要设置成1。
●开柜门(vmxon)
需要申请一块内存(4kb),这块内存用来记录vmm/host运行的信息。这块内存由CPU来维护。然后把这块内存的lowpart和highpart作为参数调用vmxon。注意这块内存的开头要填入一个特定的msr寄存器中的值。注意这里vmxon修改cr4寄存器之后,cr0寄存器的页保护和段保护位都不能再置位,如果试图修改cr0的页保护或者段保护位,会触发异常蓝屏。
●拔电源(vmclear/vminit)
●选中机器(vmptrload)
一个host可以有多个guest。所以要先选中一台guest机器。vmptrload相当于一个指针。需要通过vmptrload来指向某个机器。如果当前有多个机器的话,在各个机器之间的切换操作也需要用到vmptrload
●装机(设置vmcs(vmwrite))
设置vmcs。也是要申请一块内存(依旧是4kb),然后在这块内存里面填入开启虚拟机时虚拟机使用的一些寄存器。
比如
设置Guest 控制区
在VT从虚拟机中退出时,处理器的状态(寄存器等)会被存储在该区域中。而进入虚拟机(开启VT)时,虚拟机中的各种处理器的状态都由进入虚拟机时该区域的对应字段的值来决定。
GuestEntry
GuestEntry是指虚拟机中的一个入口点,用于将处理器控制权从VMM转移给虚拟机,让虚拟机开始执行。
设置Host控制区
在从虚拟机中退出时,host对CPU进行接管。host接管后各种寄存器的状态存储在这个区域。也就是说当虚拟机中发生了vm-exit事件,CPU会从guest返回到host,这个区域中的值会被设置到对应的寄存器中,然后按照设置完之后的eip继续执行。
VMMEntryPoint函数
VMMEntryPoint的实现通常需要与VMCS交互,以完成虚拟机的初始化和启动以及检查并处理虚拟化陷阱(VM Exit)。个人理解是如果虚拟机发生了异常或者中断,VMMEntryPoint就会去接管并处理,并根据需要恢复虚拟机的上下文(包括寄存器状态、内存映射等),然后再次跳转到虚拟机的下一条指令处,让虚拟机继续执行。
●开机(vmlaunch)
这才真正进入guest系统
●拔电源(vmclear/vminit)
●关柜门(vmxoff)
●关锁(cr4设置成0)
西湖论剑EasyVT复现
前言
这题是出题人在周壑写的简易VT的基础上改的。
Github项目
Guest程序
先看要在VT中跑的Guest程序。
先看整体逻辑,就是通过四次循环验证,就说明输入的flag正确。
记住此处,取两次8位字节,前8位存储在esi,后8位存储在edi。
后面的十条vm指令,每次都会直接触发中断,从而使VMMEntryPoint去接管处理,跳转到VM Exit处理分支。
EasyVT驱动
寻找处理中断的分发函数
我们返回到host,去查看VMMEntryPoint,接下来从驱动入口一步步寻找。
对照周壑的VT源码,我们很容易就能找到设置VMCS处。
顺利找到VMMEntryPoint
保存了寄存器的状态,之后进入分发函数。
这些常量的宏可以在intel的白皮书里找到。(第4497页开始)
然后根据一开始的10条指令的顺序,去对应每条分发函数内部的逻辑。
理清加密逻辑
出题人故意多次赋值RC4的key,TEA的key等等,所以还是要仔细分析。我就列举一下,不详细展开了。
VMLaunch 设置rc4的key
VMREAD 设置RC4明文加密的顺序,EDI为先,ESI为后
VMCALL 没有魔改的RC4加密
VMPTRST 设置l,r
VMPRTLD 设置TEA的key,delta
VMCLEAR 设置sum
VMRESUME 魔改后的TEA
VMOFF check,注意l,r顺序
编写EXP
#include<stdio.h>
using namespace std;
void decrypt(unsigned int* v, unsigned int* key) {
unsigned int l = v[0], r = v[1], sum = 0x20000000, delta = 0xC95D6ABF;
for(int i=0;i<32;i++){
sum-=delta;
}
for (size_t i = 0; i < 32; i++) {
sum += delta;
r -= ((l << 4) + key[1]) ^ (l + sum) ^ ((l >> 5) + key[0]);
l += ((r << 4) + key[3]) ^ (r + sum) ^ ((r >> 5) + key[2]);
}
v[0] = l;
v[1] = r;
}
signed main(){
unsigned int v[]={0x5C073994, 0x0D805CB3, 0x87DDA586, 0x0317FB8E, 0x6520EF29, 0x5A4987AF, 0xEB2DC2A4, 0x38CF470E};
unsigned int key[]={0x00102030,0x8090A0B0,0xC0D0E0F0,0x40506070};
for (int i = 0; i < 8; i += 2) {
decrypt(v + i, key);
printf("%d,%d,%d,%d,",*((unsigned char*)&v[i+1]+0),*((unsigned char*)&v[i+1]+1),*((unsigned char*)&v[i+1]+2),*((unsigned char*)&v[i+1]+3));
printf("%d,%d,%d,%d,",*((unsigned char*)&v[i]+0),*((unsigned char*)&v[i]+1),*((unsigned char*)&v[i]+2),*((unsigned char*)&v[i]+3));
}
}
y=[213,18,156,184,44,122,126,177,209,66,152,191,33,115,37,230,208,69,205,237,33,41,38,178,220,73,155,185,44,45,114,186]
x=[]
for i in range(4):
key='04e52c7e31022b0b'
x=y[i*8:i*8+8].copy()
flag=''
j=0
c=x
s=list(range(256))
for i in range(256):
j=((j+s[i])+ord(key[i%len(key)]))%256
s[i],s[j]=s[j],s[i]
j=0
i=0
for r in c:
i=(i+1)%256
j=(j+s[i])%256
s[i],s[j]=s[j],s[i]
x=(s[i]+s[j]%256)%256
flag+=chr(r^s[x]%256)
print(flag[4:8]+flag[:4],end="")
#81920c3758be43705ba154bb8f599846
Learn From
周壑的B站VT课
smallzhong
Lu1u
Qfrost
SinkDev
ChatGpt
评论区