反病毒工程师常见的反虚拟机技术
引子
相比反调试技术,反虚拟机技术相对而言简单些,本来想写反调试的,但是发现坛子反调试技术精华一大堆。逆向工程师遇到最多的应该是反调试技术,可能只有反病毒工程师会遇到反虚拟机技术。
反虚拟机技术没有固定的方法,就像做饭一样,蒸着吃炸着吃烤着吃,怎么吃都能吃,怎么吃都好吃,没法总结,也不可能总结全面。虚拟机检测是一门艺术,没有固定的章法。这里只列举一些最常见的希望各路大佬多多补充。
在进程列表中查找VMware字符串
安装了VMware Tools之后进程列表中会多出来三个进程:VMwareService.exe VMwareTray.exe VMwareUser.exe
BOOL IsVMWarePre()
{
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(pe32);
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hProcessSnap == INVALID_HANDLE_VALUE)
{
return FALSE;
}
BOOL bMore = Process32First(hProcessSnap, &pe32);
while(bMore)
{
if (strcmp(pe32.szExeFile, "VMwareService.exe")==0 || strcmp(pe32.szExeFile, "VMwareTray.exe")==0 || strcmp(pe32.szExeFile, "VMwareUser.exe")==0 )
{
return TRUE;
}
bMore = Process32Next(hProcessSnap, &pe32);
}
CloseHandle(hProcessSnap);
return FALSE;
}
在虚拟机注册表中搜索VMware字符串
注册表项包括虚拟机硬盘驱动器,适配器和虚拟鼠标:
[HKEY_LOCAL_MACHINE_HARDWARE\DEVICEMAP\Scsi\Scsi Port 0\Scsi Bus 0\Target Id 0\Logical Unit Id 0]
"Identifier"="VMware Virtual IDE Hard Drive"
"Type"="DiskPeripheral"
[HKEY_LOCAL_MACHINE_HARDWARE\SOFTWARE\Microsoft\Windows\CurrentVersion\Reinstall\0000]
"DeviceDesc"="VMware Accelerated AMD PCNet Adapter"
"DisplayName"="VMware Acceleeated AMD PCNet Adapter"
"Mfg"="VMware,Inc."
"ProviderName"="VMware,Inc."
[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class\{4D36E96F-E325-11CE-BFC1-08002BE10318}\0000]
"LocationInformationOverride"="plugged into PS/2 mouse port"
"InfPath"="oem13.inf"
"InfSection"="VMMouse"
"ProviderName"="VMware,Inc."
根据MAC地址
通常,MAC地址的前三个字节标识一个提供商,VMware对应的MAC地址开头为00:0C:29
反病毒工程师常见的反虚拟机技术
string mac;
if (mac=="00-0c-29")
{
return TRUE;
}
else
return FALSE;
查找漏洞指令
虚拟机监视器VMM(Virtual Machine Monitor)是一个运行在宿主机系统,用来监视虚拟机状态的监视器。VMM为客户及操作系统提供一个完整的虚拟平台。但是VMM也存在一些可以被恶意代码探测到虚拟化的安全缺陷。
内核模式下,VMWare使用二进制翻译技术进行模拟指令的操作,运行于内核态的某些特权指令被解释和模拟,也就是说他们不再物理处理器上运行。相反,在用户模式下,代码直接在处理器运行,几乎和所有硬件交互的指令,要么是特权之灵,要么就是会产生内核态陷阱指令或者是终端指令,vmware截获终端并处理他们,一遍虚拟机仍然认为我是一个真实的主机。
但是在x86架构上,一些指令在获取硬件信息的时候并不产生异常,比如说sldt、cpuid等。VMware为了正确模拟这些指令,就需要在所有指令上进行二进制翻译,这些指令包括但不限于特权指令。
这么干的话由于进行了二进制翻译,所以会造成很大的性能损失。VMware的开发人员显然考虑到了这点,所以允许一些特定指令在没有正确虚拟化的前提下运行,这样的话会造成某些特定的指令在VMWare虚拟机会返回和物理机不一样的结果。
处理器使用某些关键的数据结构和表会被加载与真实系统不同的偏移量,也就是全虚拟化的一个副作用。中断描述表IDT是CPU里面的一个用来确保正确响应终端和异常的数据结构。
在x86架构下,所有的内存获取要么是通过全局描述表GDT获得,要么就是通过本地描述表LDT获得。这些表中包含段描述符提供每一个端的详细储存信息,比如说段基址类型、长度以及权限等等。
其实CPU内部有用来存放IDT、GDT和LDT表的基址和大小的寄存器,也就是IDTR、GDTR和LDTR。这里需要提醒一点,操作系统不需要使用这些表,比如说Windows的平面内存模型就是默认使用GDT不适用LDT。
sidt、sgdt和sldt指令可以读取这些表的位置,并且将相应寄存器存入对应的内存地址。虽然说这些指令通常情况下是操作系统使用的,但是x86架构下这三个指令其实不是特权指令,换句话说就是,可以在用户态内存执行。
在x86架构下,IDTR\GDTR\LDTR的值必须对宿主机操作系统有效,同时,他们偏离了虚拟机操作系统的预期值,原因是sidt、sgdt和sldt可以随时被用户态的代码使用,而且不会产生陷阱,这样的话VMWare就不会正确虚拟化这些指令。
Red Pill和No Pill反虚拟机技术
Red Pill通过运行sidt指令获取IDTR寄存器的值。虚拟机监视器必须重新定位Guest系统的IDTR,来避免与Host系统的IDTR冲突。因为在虚拟机中运行sidt指令时,虚拟机监视器不会得到通知,所以会返回虚拟机的IDTR。
Red Pill通过测试这种差异来探测VMWare的使用。这种方法存在一个缺陷,由于IDT的值只针对处于正在运行的处理器而言,在单CPU中它是个常量,但当它处于多CPU时就可能会受到影响了,因为每个CPU都有其自己的IDT,这样问题就自然而然的产生了。
针对此问题,有一种方法就是利用Red Pill反复地在系统上循环执行任务,以此构造出一张当前系统的IDT值变化统计图,但这会增加CPU负担;另一种方法就是windows API函数SetThreadAffinityMask()将线程限制在单处理器上执行,当执行此测试时只能准确地将线程执行环境限制在本地处理器,而对于将线程限制在VM处理器上就可能行不通了,因为VM是计划在各处理器上运行的,VM线程在不同的处理器上执行时,IDT值将会发生变化,因此此方法也很少被使用。
需要注意的是Red Pill只在但处理器的主机上才能够有效,针对多核心和多处理器可能会出现是小的情况,因为每一个处理器都会有一个对应的IDT,所以sidt指令结果可能不唯一。
用sgdt和sldt指令去探测VMWare环境的技术叫做No Pill,No Pill实现的原理是:LDT数据结构由处理器分配而不是操作系统分配。正常情况下,Windows操作系统里面不会使用LDT数据结构,但是VMware却提供了LDT的虚拟化支持,在VMWare里面,LDT还和一般的LDT不太一样:宿主机系统中LDT位置的值是0,但是在虚拟机中不是0。
BOOL IsVMWarePre()
{
ULONG xdt = 0 ;
ULONG InVM = 0;
__asm
{
push edx
sidt [esp-2]
pop edx
nop
mov xdt , edx
}
if (xdt > 0xd0000000)
{
InVM = 1;
}
else
{
InVM = 0;
}
__asm
{
push edx
sgdt [esp-2]
pop edx
nop
mov xdt , edx
}
if (xdt > 0xd0000000)
{
InVM += 1;
}
if (InVM == 0)
{
return FALSE;
}
else
{
return TRUE;
}
查询I/O通信端口
VMware使用虚拟化I/O端口完成宿主机和虚拟机之间的通信,比如剪切板功能。这个技术的关键在于x86指令集中的in指令,in指令是从一个源操作数指定的端口复制数据到目的操作数指定的内存地址中。
在VMware环境下,虚拟化程序会监视in指令的执行并不或目的通信通道端口为0x5668的I/O操作。VMWare同事会检查第二个而操作数是不是0x5668(VX),如果恰好第二个操作数是VX的话,EAX寄存机载入的值是0x564D5868(VMXh),ECX寄存器中必须被载入对应端口上执行性相应操作的值。0xA表示”get VMWare Version type”;0x14表示“get memory size”。这两个值可以用来探测VMware环境。
bool IsVMWarePre()
{
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;
}
对于这种方法的话,我们可用NOP去替换in指令,或者修补条件跳转,这样的话不论比较结果如何都执行到位探测到虚拟机的程序分支。
根据安装目录
VMware的安装目录C:\Program Files\VMware\VMware Tools也会留下恨及:
BOOL IsVMwarePre()
{
if (PathIsDirectory("C:\\Program Files\\VMware\\VMware Tools\\") == 0)
{
return FALSE;
}
else
return TRUE;
反虚拟机技术如何绕过
修改二进制代码绕过检测,在反汇编或调试的过程中修补二进制代码。
在IDA中高亮显示反虚拟机代码。
在IDA中可以用如下脚本查找前面提到的指令:
from idautils import *
from idc import *
heads = Heads(SegStart(ScreenEA()), SegEnd(ScreenEA()))
antiVM = [
for i in heads:
if (GetMnem(i) == "sidt" or GetMnem(i) == "sgdt" or GetMnem(i) == "sldt" or GetMnem(i) == "smsw" or GetMnem(i) == "str" or GetMnem(i) == "in" or GetMnem(i) == "cpuid"):
antiVM.append(i)
print "Number of potential Anti-VM instructions: %d" % (len(antiVM))
for i in antiVM:
SetColor(i, CIC_ITEM, 0x0000ff)
Message("Anti-VM: %08x\n" % i)
调整VMware的配置
修改如下配置可用于伪装:
isolation.tools.getPtrLocation.disable = "TRUE"
isolation.tools.setPtrLocation.disable = "TRUE"
isolation.tools.getVersion.disable = "TRUE"
isolation.tools.setVersion.disable = "TRUE"
monitor_control.disable_directexec = "TRUE"
monitor_control.disable_chksimd = "TRUE"
monitor_control.disable_ntreloc = "TRUE"
monitor_control.disable_selfmod = "TRUE"
monitor_control.disable_reloc = "TRUE"
monitor_control.disable_btinout = "TRUE"
monitor_control.disable_btmemspace = "TRUE"
monitor_control.disable_btpriv = "TRUE"
monitor_control.disable_btseg = "TRUE"
除此之外,还建议卸载VMware Tools,禁用共享文件夹。
反病毒工程师常见的反虚拟机技术
- End -
反病毒工程师常见的反虚拟机技术
看雪ID:SpyGOD https://bbs.pediy.com/user-home-830337.htm
|