虚拟机检测技术

现在的主流虚拟机产品充分利用硬件虚拟技术,如intel-vt和AMD-SVM,虚拟效率高、透明性好,VMM的处理器虚拟化、内存虚拟化、I/O虚拟化等物理资源虚拟的实现都是软硬件结合,充分了解虚拟化原理才能做到很好的检测虚拟机运行。本文将介绍intel-vt虚拟机各个方面的实现原理,了解与真实机的不同,进行针对性的检测。

虚拟机环境在操作系统和硬件资源之间增加一层VMM(虚拟机监视器),这里的操作系统也称为客户机;VMM更像是传统的物理操作系统,管理着硬件资源;客户机执行某些敏感操作——中断、某些寄存器操作、IO指令操作等时,会退出到VMM层,由VMM做相应的转换、运算或者修改操作之后再进入客户机。

VMM是虚拟机的关键,现在普遍认为有三种模型:Hosted宿主模型、Hypervisor模型和混合模型。Hosted宿主模型的典型代表就是VMWare桌面版,物理资源由操作系统管理,VMM提供驱动和应用层进程,通过调用操作系统服务实现处理器、内存、IO设备虚拟化,客户机作为宿主的一个进程;Hypervisor模型中VMM几乎就是完整的操作系统,直接管理物理资源,提供虚拟化功能,实现相对复杂,好多操作系统的功能要自己来实现;混合模型既有管理物理资源的部分,又有一个和客户及操作系统同级的特殊客户机,结合了前两种虚拟机模型的特点。

下面以windows平台的vmware软件,介绍各个方面的虚拟化实现以及检测技术。

处理器虚拟化

处理器虚拟化的关键是能在客户机执行敏感指令和产生中断时陷入VMM,同时允许客户机主动退出到VMM。Intel VT技术的VT-x专用于硬件辅助处理器虚拟化。

VT-x扩展处理器架构,提供了根操作模式和非根操作模式,VMM运行在根模式,客户机运行在非根模式。从非根模式到根模式称退出到VMM,反之称进入VMM。通过配置VMCS(Virtual-Machine Control Structure 虚拟机控制结构)的相关区域,指定何时客户机陷入VMM。VMCS记录了非常关键的信息,两种模式的环境保存、定义何时退出到VMM、退出和进入VMM时执行操作等。

处理器虚拟化的神奇之处在于虚拟机直接使用物理CPU的功能,软件虚拟时代复杂的处理器模拟工作省去,通过配置VMCS就可以做到完全虚拟化。 根据处理器虚拟化原理,检测是否运行在虚拟机中可以通过指令执行、中断、特殊寄存器等和真实机差异做到。下面逐一介绍:

最常用的检测代码,虚拟机和物理机通信需要读取特定端口的信息。而IN指令是特权指令,物理机执行会发生异常。

BYTE InDetect()
{
	BYTE inorout = 1;
	__try
	{
		__asm
		{
			pushad
			mov eax,'VMXh'
			mov edx, 'VX'
			in  eax, dx
			popad
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		inorout = 0;
	}
	return inorout;
}

pic1

另一个特权指令CPUID也经常用到。CPUID以EAX为参数,eax、ebx、ecx、edx保存返回值。当参数位1 时,返回值ecx的第31位会被hypervisor赋值为1。如果存在VMM,就可以用这一位判断。

BYTE InDetect()
{
	DWORD result = 0;
	__try
	{
		__asm
		{
			pushad
			xor ecx, ecx
			mov eax,1
			cpuid
			mov result, ecx
			popad
		}
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
		result = 0;
	}
	printf("%08x\n", result);
	return result & (1<<31);
}

pic2

另外CPUID保存了0x40000000 - 0x400000FF功能号,VMM用它们来传递一些信息,所以可以用这些来传递信息。

BYTE InDetect()
{
	DWORD b = 0, c = 0, d = 0;
	char result[13];
	__try
	{
		__asm
		{
			pushad
			mov eax,0x40000000
			cpuid
			mov b, ebx
			mov c, ecx
			mov d, edx
			popad
		}
		memcpy(result + 0, &b, 4);
		memcpy(result + 4, &c, 4);
		memcpy(result + 8, &d, 4);
		result[12] = '\0';
		printf("%s\n", result);
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
	}
	return 0;
}

pic3

检测效果可见一斑。

中段描述符表的基地址在虚拟机和物理机中的地址会有所不同,“红药丸”检测原理就是利用了SIDT读取ITD基址来判断。物理机的基址往往是0x80XXXXXX,而虚拟机中则是0xFFXXXXXX:

void RedPill() 
{
	typedef struct
	{
		WORD limit;
		WORD Low;
		WORD High;
	} IDTINFO;
	IDTINFO idt;

	_asm SIDT idt;
	printf("%04x%04x\n",idt.High,idt.Low);
}

不过这种检测手段实际上已经不能使用了。新版本的虚拟机在内存虚拟化方面做得非常好,想隐藏IDT的基址也不是很难做到。

内存虚拟化和I/O设备虚拟化

客户机是一个完整的操作系统,完全虚拟的情况下有自己的地址空间。而物理机看来客户机只是一个进程,所以需要做相应的转化才能使得客户机访问到真正的物理地址。这里的转化过程包括从 客户机虚拟地址——客户机物理地址——物理机物理地址。第一步转换客户机操作系统可自行完成(VMCS可以配置CR3影响进程页表,这里我觉得用处很大)。第二步转换就要靠VMM的实现。

早期虚拟机使用“影子页表”。影子页表需要对客户机的每个进程都维护一份页表,虚拟机自娱自乐的做内部转换,而实际上VMM捕获内存访问之后使用影子页表来操作真正的物理内存。实际上就是维护了客户机进程虚拟地址到物理机物理地址的直接转换,在寻址上对客户机做了隐藏。影子页表实现非常复杂,开销也很大。硬件虚拟技术也着重解决了这个问题。

Intel VT-x提供EPT(Extended Page Table 扩展页表)内存虚拟化技术,通过引入EPT页表实现客户机物理地址到真实物理地址的转换。EPT的实现也非常复杂,有兴趣的可以查阅Intel资料做进一步了解。

IO设备虚拟化的目的是让客户机能直接访问真实I/O地址空间,同时DMA可以直接访问客户机的内存空格。Intel VT-d技术做了DMA重映射,将DMA的目的设备地址做转换,实现DMA到客户机地址的直接传输。

直接利用内存虚拟化和IO虚拟化做检测的方法并不太大众。我能想到的就是利用速度上的差异,不过这种方法跟硬件非常相关,很难找到一个统一的方法。不过既然是虚拟化的,那就肯定不可能跟物理实现完全一样。

其他虚拟化实现及检测

这里主要介绍时间虚拟化。我们可以注意到虚拟机挂起之后等一段时间重新恢复运行,时间始终和客户机保持一致,看起来挂起的那段时间虚拟机时钟并未停止。这是通过虚拟机启动时补充时钟中断做到的。

所以检测手段随着而来,做一个固定频率计时器,假设虚拟机会在某时刻挂起,这样就会出现空挡。虽然鸡肋,不过在某些情况下还是非常有用的:

pic4

可以看到红线附近明显有了跳转。这种情况在物理机睡眠时也会发生,所以检测方法并不太有效。

检测指令执行速度也是常用手段。某些指令的执行可能会陷入VMM,所以导致速度较慢。如果连续执行多次,那么速度差异就会显现出来。获取硬件定时器RTC的指令RDTSC可以作为我们需要的指令,连续执行两次RTC,时间差的差距会很大:

void TSCDetect()
{
	DWORD result;
	__asm
	{
		RDTSC
		mov ecx,eax
		RDTSC	
		sub	eax,ecx
		mov result,eax
	}
	printf("%08x\n",result);
}

pic5

可以看到差非常多,也是有效的检测手段。

另一个跟时间相关的是进程调度。真实机可以做到真正的轮询,优先级相同的两个线程大致可以运行同样的时间,但是虚拟机本身就是一个进程,并不能完全得到物理资源。所以我想虚拟机里的进程调度并不完全符合操作系统特性,某些优先级高的线程并不一定能获得理论上的时间。我写的小demo效果并不太明显,希望跟大家一起讨论。

此外可以利用虚拟机中的一些注册表名称、硬件设备名称特性判断。比如在虚拟机中,有好多包含“vmware”字符串的注册表键值:

pic6

这样的字串出现次数非常多,虚拟硬件设备名往往会包含“vmware”,可以说是防不胜防。也可以查看是否安装vmtool进行检测。此外因为硬件设备名是客观存在的,不一定非得检测注册表值。比如MAC地址就会有特征,硬盘modulename也会有特征,BIOS DMI信息也会有VMWARE的标志;另外常用的手段是枚举内核对象,查看内核对象名称,是否有虚拟机特征。 这些手段在必要的时候也能用到。

另外网络虚拟机导致进出流量都会打上标签,比如TCP/IP数据包的操作系统标识、TTL值等,可以据此实现远程虚拟机检测。

虚拟机检测是一项长期有意义的工程,随着所谓“云”的发展,虚拟技术会得到更多的应用,相应的检测手段必然会在攻防中占据重要一席。