题目来自:2016腾讯游戏安全技术竞赛第二轮第2题——PC方向 题目概要
要求说明:
编写一个【Tencent2016C.dll】,并导出多个接口函数:CheckVMWareX,CheckVirtualPCX,CheckVirtualBoxX,X为1-100之间的数字,比如CheckVMWare1,CheckVirtualPC8,...,CheckVirtualBox98。
CheckVMWareX 系列函数功能:检测自己是否运行于VMWare中,是返回TRUE,否则返回FALSE。
自己一直用VMware,积累一下反虚拟机手段,希望调试病毒能少踩坑,适合新手看,VirtualPC和VirtualBox也都是差不多,以后有时间补上吧~
编译环境:
VS2015创建win32 dll ,静态编译->属性->C/C++->代码生成->运行库(MT)
VMware12 + win7 32位测试
代码原理
执行特权指令检测
原理: 在x86体系中,一些指令在获取硬件相关信息时不产生异常,如sidt、sgdt、sldt、cpuid等,而VMware因为性能原因并没有虚拟这些指令,所以意味着这些指令在vm虚拟机中和物理机中运行时会返回不同的结果。
然而在测试一些常用检测技术时发现部分已失效,应该是VMware更新导致的,先来看两个之前很常用的技术:
Redpill
简单说,就是通过运行sidt指令获取IDT寄存器的值(IDT: 中断描述符表,可以简单理解为查找处理中断时所用的函数,共256项,如第3项就是我们常用的int3断点)。Redpill的作者测试说明虚拟机中的IDT地址通常位于0xFFXXXXXX,而在真实主机上位于0x80xxxxxx。所以可通过判断执行SIDT指令后返回的第一字节是否大于0xD0,判断是否在虚拟机中。同时这项技术必须满足运行在单核处理器上,因为每个核心只有一个IDT表~如果是多核切换就很难确定具体值了~
No Pill
sgdt与sldt指令探测技术,依赖于LDT(局部描述符表)由处理器分配而非操作系统分配的事实。因为Windows正常情况下不使用LDT,但VM提供了LDT的虚拟化支持,结果就是:真机中LDT位置为0,而在虚拟机,不为0。同时对于GTR,虚拟机中应为0xFFXXXXXX , 否则为真机。
简单看一下idt:
打开双机调试(建议Virtual KD,配置较简单)
当前我们已经在调试虚拟机的操作系统了,所以直接看这三个表的地址~
好吧,这哪里是虚拟机,不就是真机啊!看来VM这么多年没少打补丁~
代码也不用测试了,肯定不能成功啊,有兴趣的可以测试下低版本VM~
好在我们还有第三种特权指令可用~
查询I/O通信端口
原理: 使用IN指令来读取特定端口的数据进行两机通讯,但由于IN指令属于特权指令,在处于保护模式下的真机上执行此指令时,除非权限允许,否则将会触发类型为"EXCEPTION_PRIV_INSTRUCTION"的异常,而在虚拟机中并不会发生异常,在指定功能号为0xA/10(获取VMware版本)时,会在EBX中返回其版本号“VMXH”;而当功能号为0x14时,可用于获取VMware内存大小,当大于0时则说明处于虚拟机中。代码分析如下:
//查询I/O通信端口BOOL CheckVMWare1(){ BOOL bResult = TRUE; __try { __asm { push edx push ecx push ebx //保存环境 mov eax, 'VMXh' mov ebx, 0 //将ebx清零 mov ecx, 10 //指定功能号,用于获取VMWare版本,为0x14时获取VM内存大小 mov edx, 'VX' //端口号 in eax, dx //从端口edx 读取VMware到eax cmp ebx, 'VMXh' //判断ebx中是否包含VMware版本’VMXh’,若是则在虚拟机中 setz[bResult] //为零 (ZF=1) 时设置字节 pop ebx //恢复环境 pop ecx pop edx } } __except (EXCEPTION_EXECUTE_HANDLER) //如果未处于VMware中,则触发此异常 { bResult = FALSE; } return bResult;}利用虚拟硬件检测
网卡MAC地址检测
原理: 网卡设备的MAC地址是唯一不变的(虽然也能物理修改)。
MAC地址的前三个字节标识一个提供商,所以一般情况下只需要找到VM固定的前三个字节就可以了~
ipconfig /all
查询到虚拟机的MAC地址00-0C-29-38-8B-E1
//通过MAC地址检测BOOL CheckVMWare2(){ string mac; getMacAddr(mac); //API见附件,以下是3种常见标识 if (mac == "00-05-69" || mac == "00-0c-29" || mac == "00-50-56") { return TRUE; } else { return FALSE; }}CPUID检测
原理: CPUID指用户计算机当前的信息处理器的信息。CPUID 指令是从 Intel 486 处理器以后开始加入支持的(只要不是古董应该都OK )。当eax=1时,运行CPUID指令之后,ecx的高31位可以判断出是否在虚拟机中,如果ecx的高31位为0表示在虚拟机下,否则在宿主机中。
//3.CPUID检测BOOL CheckVMWare3(){ DWORD dwECX = 0; bool b_IsVM = true; _asm { pushad; pushfd; mov eax, 1; cpuid; mov dwECX, ecx; and ecx, 0x80000000; //取最高位 test ecx, ecx; //检测ecx是否为0 setz[b_IsVM]; //为零 (ZF=1) 时设置字节 popfd; popad; } if (b_IsVM) //宿主机 { return FALSE; } else //虚拟机 { return TRUE; }}mov eax, 0cpuid上面代码中,eax为0获取,那么它将返回值是:
eax:最大的基本功能号
ebx:"Genu"
edx: "ineI"
ecx:"ntel"
这几个字符串组合起来就是 "GenuineIntel " 对于 AMD 的处理器来说,它返回的字符串是:"AuthenticAMD ",可对应判断处理器。
类似的,对于虚拟机CPUID还有另一种方式检测
eax为0x40000000时,运行CPUID后,ebx+ecx+edx=”VMWareVMWare”;
BOOL CPUID2(){ DWORD dwECX = 0; bool isVM = true; DWORD dwReg[3] = { 0 }; _asm { pushad; pushfd; mov eax, 0x40000000; cpuid; mov dword ptr[dwReg], ebx; //运行CPUID之后,ebx+ecx+edx=”VMWareVMWare”; mov dword ptr[dwReg + 4], ecx; mov dword ptr[dwReg + 8], edx; popfd; popad; }}通过主板序列号、型号、系统盘所在磁盘名称等其他硬件信息
原理: 这里使用WMI的方式,连接COM接口,循环枚举所有的结果对象找到带有VMware的相关信息(类似的信息有很多,只列举一个)。
BOOL CheckVMWare4(){ string table = "Win32_DiskDrive"; wstring wcol = L"Caption"; string ret; ManageWMIInfo(ret, table, wcol); //API见附件 if (ret.find("VMware") != string::npos) { return TRUE; } else { return FALSE; }}通过能够获取的其它特征信息检测
本质和找MAC地址类似,就是要观察虚拟机,观察它的各种系统信息,设备信息,再找相关的信息。
搜索特定进程
原理: 会有一些虚拟机特有的进程,可以通过检测这些进程是否存在来判断~
显然我们可以从vmtoolsd.exe和vmacthlp.exe突破
//遍历进程BOOL CheckVMWare3(){ DWORD ret = 0; 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 (wcscmp(pe32.szExeFile, L"vmtoolsd.exe") == 0) //注意此处用了wcscmp(pe32.szExeFile是 WCHAR*) { return TRUE; } bMore = Process32Next(hProcessSnap, &pe32); //遍历下一个进程 } CloseHandle(hProcessSnap); return FALSE;}通过注册表检测
虚拟机中有非常多的虚拟硬件(不只是网卡,还有打印机、鼠标等等,都可以判断)
以VMwareHostOpen.exe为例~
//通过注册表检测BOOL CheckVMWare5(){ HKEY hkey; if (RegOpenKey(HKEY_CLASSES_ROOT, L"\\Applications\\VMwareHostOpen.exe", &hkey) == ERROR_SUCCESS) { return TRUE; //RegOpenKey函数打开给定键,如果存在该键返回ERROR_SUCCESS } else { return FALSE; }}通过特定服务检测
//通过特定服务检测BOOL CheckVMWare7(){ int menu = 0; SC_HANDLE SCMan = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); //打开服务控制管理器 if (SCMan == NULL) { cout 输入regedit->注册表</strong>
定位到网卡处:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class{4D36E972-E325-11CE-BFC1-08002bE10318} </p>
新建一个字符串值,命名为NetworkAddress,内容是新的MAC地址
"网络连接"中重启本地连接即可
调整VM设置
VMware Tools中有一些未文档化的功能可以减轻反虚拟机技术探测,下面的代码放到VMware的.vmx文件的最后面:
isolation.tools.getPtrLocation.disable = "TRUE" isolation.tools.setPtrLocation.disable = "TRUE" isolation.tools.setVersion.disable = "TRUE" isolation.tools.getVersion.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" 其中directexec可以使用户模式下的代码被模拟执行而非直接在硬件上运行,因此可以ANTI一些反虚拟机技术。前四条设置被VMware后门命令使用,它们的作用是使得运行在Guest系统中的VMware Tools不能获取宿主系统的信息。这些设置会禁用VMware Tools的一些有用功能,并可能对虚拟机性能有严重负面影响(测试发现速度会明显降低,建议其它技术无效时再使用)
另:
1.monitor_control.restrict_backdoor = "true"
2.开启vmware workstation,虚拟机 -> 设置 -> 处理器 -> 禁用二进制翻译加速 可能在某些时候会有帮助
总体而言感觉IDA插件的方式比较实用,比如有病毒在申请的空间中放入反虚拟机代码,我们可以在空间申请后DUMP下来,在IDA中搜索,再在OD中定位,这样能节省不少时间~
而许多对硬件和信息的探测想anti就比较难了,没什么思路,总不能把虚拟机CPU、文件路径、服务名等等都改了吧,最好的方式,应该还是逆向分析破解吧 ~比如,一段代码在一个条件跳转处过早终止,可能就会用到反虚拟机技术~我们已经清楚正向怎么检测了,逆起来当然有思路喽~ 路漫漫其修远兮啊~ 大部分技术很多年没变化了,有新加入的,也有的随着VMware 更新已无法使用,和网上的一些资料多少会有冲突,查漏补缺,方便需要的朋友参照,文中测试如有不准确处,请各位指出~谢谢 参考资料
《恶意代码分析实战》 诸葛建伟 姜辉 张光凯 译
《虚拟机检测技术剖析》 林桠泉
Tencent2016_PC.rar
287.73 KB, 下载次数: 234, 下载积分: 吾爱币 -1 CB
题目、代码、IDA插件
来源:http://www.12558.net
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
楼主热帖