前言

在当今信息安全领域,特别是恶意软件分析中,经常需要利用到 虚拟机技术,以提高病毒分析过程的安全性以及硬件资源的节约性,因此它在恶意软件领域中是应用越来越来广泛。这里我们所谓的 虚拟机(Virtual Machine)是指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。通过 虚拟机软件(比如VMware,Virtual PC ,VirtualBox),你可以在一台物理计算机上模拟出一台或多台 虚拟的计算机,这些 虚拟机完全就像真正的计算机那样进行工作,例如你可以安装操作系统、安装应用程序、访问网络资源等等。***者为了提高恶意程序的隐蔽性以及破坏真实主机的成功率,他们都在恶意程序中加入检测 虚拟机的代码,以判断程序所处的运行环境。当发现程序处于 虚拟机(特别是蜜罐系统)中时,它就会改变操作行为或者中断执行,以此提高反病毒人员分析恶意软件行为的难度。本文主要针对基于Intel CPU的 虚拟环境VMware中的Windows XP SP3系统进行检测分析,并列举出当前常见的几种 虚拟机检测方法。

方法一:通过执行特权指令来检测虚拟机

Vmware为真主机与 虚拟机之间提供了相互沟通的通讯机制,它使用“IN”指令来读取特定端口的数据以进行两机通讯,但由于IN指令属于特权指令,在处于保护模式下的真机上执行此指令时,除非权限允许,否则将会触发类型为“EXCEPTION_PRIV_INSTRUCTION”的异常,而在 虚拟机中并不会发生异常,在指定功能号0A(获取VMware版本)的情况下,它会在EBX中返回其版本号“VMXH”;而当功能号为0x14时,可用于获取VMware内存大小,当大于0时则说明处于 虚拟机中。VMDetect正是利用前一种方法来检测VMware的存在,其检测代码分析如下:
代码:
bool IsInsideVMWare() {   bool rc = true;    __try   {     __asm     {       push   edx       push   ecx       push   ebx        mov    eax, 'VMXh'       mov    ebx, 0  // 将ebx设置为非幻数’VMXH’的其它值       mov    ecx, 10 // 指定功能号,用于获取VMWare版本,当它为0x14时用于获取VMware内存大小       mov    edx, 'VX' // 端口号       in     eax, dx // 从端口dx读取VMware版本到eax //若上面指定功能号为0x14时,可通过判断eax中的值是否大于0,若是则说明处于虚拟机中       cmp    ebx, 'VMXh' // 判断ebx中是否包含VMware版本’VMXh’,若是则在虚拟机中       setz   [rc] // 设置返回值        pop    ebx       pop    ecx       pop    edx     }   }   __except(EXCEPTION_EXECUTE_HANDLER)  //如果未处于VMware中,则触发此异常   {     rc = false;   }    return rc; } 
测试结果:
  名称:  1.jpg查看次数: 3916文件大小:  57.4 KB
                 图1
如图1所示,VMDetect成功检测出VMWare的存在。

方法二:利用IDT基址检测虚拟

利用IDT基址检测 虚拟机的方法是一种通用方式,对VMware和Virtual PC均适用。中断描述符表IDT(Interrupt Descriptor Table)用于查找处理中断时所用的软件函数,它是一个由256项组成的数据,其中每一中断对应一项函数。为了读取IDT基址,我们需要通过SIDT指令来读取IDTR(中断描述符表寄存器,用于IDT在内存中的基址),SIDT指令是以如下格式来存储IDTR的内容:
代码:
typedef struct {     WORD IDTLimit;    // IDT的大小     WORD LowIDTbase;  // IDT的低位地址     WORD HiIDTbase;  // IDT的高位地址 } IDTINFO; 
由于只存在一个IDTR,但又存在两个操作系统,即 虚拟机系统和真主机系统。为了防止发生冲突,VMM( 虚拟机监控器)必须更改 虚拟机中的IDT地址,利用真主机与 虚拟机环境中执行sidt指令的差异即可用于检测 虚拟机是否存在。著名的“红丸”(redpill)正是利用此原理来检测VMware的。Redpill作者在VMware上发现 虚拟机系统上的IDT地址通常位于0xFFXXXXXX,而Virtual PC通常位于0xE8XXXXXX,而在真实主机上正如图2所示都位于0x80xxxxxx。Redpill仅仅是通过判断执行SIDT指令后返回的第一字节是否大于0xD0,若是则说明它处于 虚拟机,否则处于真实主机中。Redpill的源码甚是精简,源码分析如下:
代码:
#include <stdio.h> int main () {    unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3";  //相当于SIDT[adrr],其中addr用于保存IDT地址   *((unsigned*)&rpill[3]) = (unsigned)m;  //将sidt[addr]中的addr设为m的地址   ((void(*)())&rpill)();  //执行SIDT指令,并将读取后IDT地址保存在数组m中    printf ("idt base: %#x\n", *((unsigned*)&m[2]));   //由于前2字节为IDT大小,因此从m[2]开始即为IDT地址   if (m[5]>0xd0) printf ("Inside Matrix!\n", m[5]); //当IDT基址大于0xd0xxxxxx时则说明程序处于VMware中   else printf ("Not in Matrix.\n");   return 0; } 
测试结果如图2所示:
  名称:  2.jpg查看次数: 3918文件大小:  54.6 KB
                                     图2
利用此IDT检测的方法存在一个缺陷,由于IDT的值只针对处于正在运行的处理器而言,在单CPU中它是个常量,但当它处于多CPU时就可能会受到影响了,因为每个CPU都有其自己的IDT,这样问题就自然而然的产生了。针对此问题,Offensive Computing组织成员提出了两种应对方法,其中一种方法就是利用Redpill反复地在系统上循环执行任务,以此构造出一张当前系统的IDT值变化统计图,但这会增加CPU负担;另一种方法就是windows API函数SetThreadAffinityMask()将线程限制在单处理器上执行,当执行此测试时只能准确地将线程执行环境限制在本地处理器,而对于将线程限制在VM处理器上就可能行不通了,因为VM是计划在各处理器上运行的,VM线程在不同的处理器上执行时,IDT值将会发生变化,因此此方法也是很少被使用的。为此,有人提出了使用LDT的检测方法,它在具有多个CPU的环境下检测 虚拟机明显优于IDT检测方法,该方法具体内容参见下节内容。

方法三:利用LDT和GDT的检测方法

在 《Intel