您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 其它行业文档 > 虚拟机去虚拟化及检测技术攻防
在当今信息安全领域,特别是恶意软件分析中,经常需要利用到虚拟机技术,以提高病毒分析过程的安全性以及硬件资源的节约性,因此它在恶意软件领域中是应用越来越来广泛。这里我们所谓的虚拟机(VirtualMachine)是指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。通过虚拟机软件(比如VMware,VirtualPC,VirtualBox),你可以在一台物理计算机上模拟出一台或多台虚拟的计算机,这些虚拟机完全就像真正的计算机那样进行工作,例如你可以安装操作系统、安装应用程序、访问网络资源等等。攻击者为了提高恶意程序的隐蔽性以及破坏真实主机的成功率,他们都在恶意程序中加入检测虚拟机的代码,以判断程序所处的运行环境。当发现程序处于虚拟机(特别是蜜罐系统)中时,它就会改变操作行为或者中断执行,以此提高反病毒人员分析恶意软件行为的难度。本文主要针对基于IntelCPU的虚拟环境VMware中的WindowsXPSP3系统进行检测分析,并列举出当前常见的几种虚拟机检测方法。方法一:通过执行特权指令来检测虚拟机Vmware为真主机与虚拟机之间提供了相互沟通的通讯机制,它使用“IN”指令来读取特定端口的数据以进行两机通讯,但由于IN指令属于特权指令,在处于保护模式下的真机上执行此指令时,除非权限允许,否则将会触发类型为“EXCEPTION_PRIV_INSTRUCTION”的异常,而在虚拟机中并不会发生异常,在指定功能号0A(获取VMware版本)的情况下,它会在EBX中返回其版本号“VMXH”;而当功能号为0x14时,可用于获取VMware内存大小,当大于0时则说明处于虚拟机中。VMDetect正是利用前一种方法来检测VMware的存在,其检测代码分析如下:代码:boolIsInsideVMWare(){boolrc=true;__try{__asm{pushedxpushecxpushebxmoveax,'VMXh'movebx,0//将ebx设置为非幻数’VMXH’的其它值movecx,10//指定功能号,用于获取VMWare版本,当它为0x14时用于获取VMware内存大小movedx,'VX'//端口号ineax,dx//从端口dx读取VMware版本到eax//若上面指定功能号为0x14时,可通过判断eax中的值是否大于0,若是则说明处于虚拟机中cmpebx,'VMXh'//判断ebx中是否包含VMware版本’VMXh’,若是则在虚拟机中setz[rc]//设置返回值popebxpopecxpopedx}}__except(EXCEPTION_EXECUTE_HANDLER)//如果未处于VMware中,则触发此异常{rc=false;}returnrc;}测试结果:图1如图1所示,VMDetect成功检测出VMWare的存在。方法二:利用IDT基址检测虚拟机利用IDT基址检测虚拟机的方法是一种通用方式,对VMware和VirtualPC均适用。中断描述符表IDT(InterruptDescriptorTable)用于查找处理中断时所用的软件函数,它是一个由256项组成的数据,其中每一中断对应一项函数。为了读取IDT基址,我们需要通过SIDT指令来读取IDTR(中断描述符表寄存器,用于IDT在内存中的基址),SIDT指令是以如下格式来存储IDTR的内容:代码:typedefstruct{WORDIDTLimit;//IDT的大小WORDLowIDTbase;//IDT的低位地址WORDHiIDTbase;//IDT的高位地址}IDTINFO;由于只存在一个IDTR,但又存在两个操作系统,即虚拟机系统和真主机系统。为了防止发生冲突,VMM(虚拟机监控器)必须更改虚拟机中的IDT地址,利用真主机与虚拟机环境中执行sidt指令的差异即可用于检测虚拟机是否存在。著名的“红丸”(redpill)正是利用此原理来检测VMware的。Redpill作者在VMware上发现虚拟机系统上的IDT地址通常位于0xFFXXXXXX,而VirtualPC通常位于0xE8XXXXXX,而在真实主机上正如图2所示都位于0x80xxxxxx。Redpill仅仅是通过判断执行SIDT指令后返回的第一字节是否大于0xD0,若是则说明它处于虚拟机,否则处于真实主机中。Redpill的源码甚是精简,源码分析如下:代码:#includestdio.hintmain(){unsignedcharm[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(idtbase:%#x\n,*((unsigned*)&m[2]));//由于前2字节为IDT大小,因此从m[2]开始即为IDT地址if(m[5]0xd0)printf(InsideMatrix!\n,m[5]);//当IDT基址大于0xd0xxxxxx时则说明程序处于VMware中elseprintf(NotinMatrix.\n);return0;}测试结果如图2所示:图2利用此IDT检测的方法存在一个缺陷,由于IDT的值只针对处于正在运行的处理器而言,在单CPU中它是个常量,但当它处于多CPU时就可能会受到影响了,因为每个CPU都有其自己的IDT,这样问题就自然而然的产生了。针对此问题,OffensiveComputing组织成员提出了两种应对方法,其中一种方法就是利用Redpill反复地在系统上循环执行任务,以此构造出一张当前系统的IDT值变化统计图,但这会增加CPU负担;另一种方法就是windowsAPI函数SetThreadAffinityMask()将线程限制在单处理器上执行,当执行此测试时只能准确地将线程执行环境限制在本地处理器,而对于将线程限制在VM处理器上就可能行不通了,因为VM是计划在各处理器上运行的,VM线程在不同的处理器上执行时,IDT值将会发生变化,因此此方法也是很少被使用的。为此,有人提出了使用LDT的检测方法,它在具有多个CPU的环境下检测虚拟机明显优于IDT检测方法,该方法具体内容参见下节内容。方法三:利用LDT和GDT的检测方法在《Intel®64andIA-32ArchitectureSoftwareDeveloper’sManualVolume3A:SystemProgrammingGuide》第二章的Vol.32-5一页(我的Intel开发手册是2008版的)中对于LDT和GDT的描述如下(以下内容为个人翻译):在保护模式下,所有的内存访问都要通过全局描述符表(GDT)或者本地描述符表(LDT)才能进行。这些表包含有段描述符的调用入口。各个段描述符都包含有各段的基址,访问权限,类型和使用信息,而且每个段描述符都拥有一个与之相匹配的段选择子,各个段选择子都为软件程序提供一个GDT或LDT索引(与之相关联的段描述符偏移量),一个全局/本地标志(决定段选择子是指向GDT还是LDT),以及访问权限信息。若想访问段中的某一字节,必须同时提供一个段选择子和一个偏移量。段选择子为段提供可访问的段描述符地址(在GDT或者LDT中)。通过段描述符,处理器从中获取段在线性地址空间里的基址,而偏移量用于确定字节地址相对基址的位置。假定处理器在当前权限级别(CPL)可访问这个段,那么通过这种机制就可以访问在GDT或LDT中的各种有效代码、数据或者堆栈段,这里的CPL是指当前可执行代码段的保护级别。……GDT的线性基址被保存在GDT寄存器(GDTR)中,而LDT的线性基址被保存在LDT寄存器(LDTR)中。由于虚拟机与真实主机中的GDT和LDT并不能相同,这与使用IDT的检测方法一样,因此虚拟机必须为它们提供一个“复制体”。关于GDT和LDT的基址可通过SGDT和SLDT指令获取。虚拟机检测工具Scoopysuite的作者TobiasKlein经测试发现,当LDT基址位于0x0000(只有两字节)时为真实主机,否则为虚拟机,而当GDT基址位于0xFFXXXXXX时说明处于虚拟机中,否则为真实主机。具体实现代码如下:代码:#includestdio.hvoidLDTDetect(void){unsignedshortldt_addr=0;unsignedcharldtr[2];_asmsldtldtrldt_addr=*((unsignedshort*)&ldtr);printf(LDTBaseAddr:0x%x\n,ldt_addr);if(ldt_addr==0x0000){printf(NativeOS\n);}elseprintf(InsideVMware\n);}voidGDTDetect(void){unsignedintgdt_addr=0;unsignedchargdtr[4];_asmsgdtgdtrgdt_addr=*((unsignedint*)&gdtr[2]);printf(GDTBaseAddr:0x%x\n,gdt_addr);if((gdt_addr24)==0xff){printf(InsideVMware\n);}elseprintf(NativeOS\n);}intmain(void){LDTDetect();GDTDetect();return0;}测试结果如图3所示:图3方法四:基于STR的检测方法在保护模式下运行的所有程序在切换任务时,对于当前任务中指向TSS的段选择器将会被存储在任务寄存器中,TSS中包含有当前任务的可执行环境状态,包括通用寄存器状态,段寄存器状态,标志寄存器状态,EIP寄存器状态等等,当此项任务再次被执行时,处理器就会其原先保存的任务状态。每项任务均有其自己的TSS,而我们可以通过STR指令来获取指向当前任务中TSS的段选择器。这里STR(Storetaskregister)指令是用于将任务寄存器(TR)中的段选择器存储到目标操作数,目标操作数可以是通用寄存器或内存位置,使用此指令存储的段选择器指向当前正在运行的任务的任务状态段(TSS)。在虚拟机和真实主机之中,通过STR读取的地址是不同的,当地址等于0x0040xxxx时,说明处于虚拟机中,否则为真实主机。实现代码如下:代码:#includestdio.hintmain(void){unsignedcharmem[4]={0};inti;__asmstrmem;printf(STRbase:0x);for(i=0;i<4;i++){printf(%02x,mem[i]);}if((mem[0]==0x00)&&(mem[1]==0x40))printf(\nINSIDEMATRIX!!\n);elseprintf(\nNativeOS!!\n);return0;}测试结果如图4所示:图4方法五:基于注册表检测虚拟机在windows虚拟机中常常安装有VMwareTools以及其它的虚拟硬件(如网络适配器、虚拟打印机,USB集线器……),它们都会创建任何程序都可以读取的windows注册表项,因此我们可以通过检测注册表中的一些关键字符来判断程序是否处于虚拟机之中。关于这些注册表的位置我们可以通过在注册表中搜索关键词“vmware”来获取,下面是我在VMware下的WinXP中找到的一些注册表项:项名:HKEY_CLASSES_ROOT\Applications\VMwareHostOpen.exe项名:HKEY_CLASSES_ROOT\Installer\Products\C2A6F2EFE6910124C940B2B12CF170FE\ProductName键值“VMwareTools
本文标题:虚拟机去虚拟化及检测技术攻防
链接地址:https://www.777doc.com/doc-4406155 .html