vm去虚拟化教程:Tencent2016C检测参数说明

1就是未通过,0通过,

CheckVMWareX系列函数功能:检测自己是否运行于VMWare中,是返回TRUE,否则返回FALSE。

自己一直用VMware,积累一下反虚拟机手段,希望调试病毒能少踩坑,适合新手看,VirtualPC和VirtualBox也都是差不多,以后有时间补上吧~

编译环境:
VS2015创建win32 dll ,静态编译->属性->C/C++->代码生成->运行库(MT)
VMware12 + win7 32位测试

图片[1]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

代码原理

执行特权指令检测

原理:在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,配置较简单)

图片[2]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

当前我们已经在调试虚拟机的操作系统了,所以直接看这三个表的地址~

图片[3]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

好吧,这哪里是虚拟机,不就是真机啊!看来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

图片[4]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

查询到虚拟机的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, 0 cpuid

上面代码中,eax为0获取,那么它将返回值是:

  • eax:最大的基本功能号
  • ebx:”Genu”
  • edx: “ineI”
  • ecx:”ntel”

这几个字符串组合起来就是 “GenuineIntel” 对于 AMD 的处理器来说,它返回的字符串是:”AuthenticAMD“,可对应判断处理器。

类似的,对于虚拟机CPUID还有另一种方式检测
eax为0x40000000时,运行CPUID后,ebx+ecx+edx=”VMWareVMWare”;
图片[5]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

 复制代码 隐藏代码
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地址类似,就是要观察虚拟机,观察它的各种系统信息,设备信息,再找相关的信息。

搜索特定进程

原理:会有一些虚拟机特有的进程,可以通过检测这些进程是否存在来判断~

图片[6]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

显然我们可以从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; }

通过注册表检测

虚拟机中有非常多的虚拟硬件(不只是网卡,还有打印机、鼠标等等,都可以判断)

图片[7]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

以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;         } }

通过特定服务检测

图片[8]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

 复制代码 隐藏代码
//通过特定服务检测 BOOL CheckVMWare7() {         int menu = 0;         SC_HANDLE SCMan = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);        //打开服务控制管理器         if (SCMan == NULL)         {                 cout << GetLastError() << endl;                 printf("OpenSCManager Eorror/n");                 return -1;         }         LPENUM_SERVICE_STATUSA service_status;         DWORD cbBytesNeeded = NULL;         DWORD ServicesReturned = NULL;         DWORD ResumeHandle = NULL;         service_status = (LPENUM_SERVICE_STATUSA)LocalAlloc(LPTR, 1024 * 64);         bool ESS = EnumServicesStatusA(SCMan,                                                                                //遍历服务                 SERVICE_WIN32,                 SERVICE_STATE_ALL,                 (LPENUM_SERVICE_STATUSA)service_status,                 1024 * 64,                 &cbBytesNeeded,                 &ServicesReturned,                 &ResumeHandle);         if (ESS == NULL)         {                 printf("EnumServicesStatus Eorror/n");                 return -1;         }         for (int i = 0; i < ServicesReturned; i++)         {                 if (strstr(service_status[i].lpDisplayName, "VMware Tools") != NULL)                 {                         return TRUE;                 }         }         CloseServiceHandle(SCMan);         return FALSE;}

通过特定文件路径检测

 复制代码 隐藏代码
//文件路径检测 BOOL CheckVMWare8() {         if (PathIsDirectory(L"C:\\Program Files\\VMware\\") == 0)                 {                 return FALSE;         }         else         {                 return TRUE;         } }

补位:时间差检测

泉哥的经典文章中有一种时间差检测的方式,感觉思路很好,反调试中可以很好的使用,但经过测试发现在虚拟机和主机中指令执行时间并没有明显区别,也应该是VM后面做过优化了~

原理:通过运行一段特定代码,然后比较这段代码在虚拟机和真实主机之中的相对运行时间,以此来判断是否处于虚拟机之中。可以通过RDTSC指令来实现,RDTSC指令是用于将计算机启动以来的CPU运行周期数存放到EDX:EAX里面,其中EDX是高位,而EAX是低位。

两者之间的运行时间明显差别很多,在虚拟机中的运行速度远不如真实主机的,一般情况下,当它的运行时间大于0xFF时,就可以确定它处于虚拟机之中了,代码如下:

 复制代码 隐藏代码
//通过时间差检测 BOOL CheckVMWareTmp() {                 __asm         {                 rdtsc                   //RDTSC指令将计算机启动以来的CPU运行周期数存放到EDXEAX里面,其中EDX是高位,而EAX是低位。                 xchg ebx, eax          //测试此条指令运行时间                 rdtsc                 sub eax, ebx           //时间差                 cmp eax, 0xFF                 jg detected         }         return FALSE; detected:         return TRUE; }

其它思路

1.搜索物理内存中的VMware字符串
2.搜索特定的驱动模块(.sys)
3.检测0环的系统内核对象,痕迹很多,甚至是内核设备的符号链接也发现了特征
如图:
WinObj查看

图片[9]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

图片[10]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

总之方法很多,要是本着多多益善的思路,组合起来混在SEH里面,再加上花指令,TT,头大~

Anti反虚拟机

好吧,最重要的还是怎么反虚拟机检测啊,新手有这么几个思路:

查找反虚拟机的IDA 脚本

来自《恶意代码分析》
包括了各种特权指令和CPUID:

 复制代码 隐藏代码
from idautils import * from idc import * heads = Heads(SegStart(ScreenEA()), SegEnd(ScreenEA())) antiVM = [] for i in heads:         if (GetMnem(i) == "in" or GetMnem(i) == "cpuid" or GetMnem(i) == "sidt" or GetMnem(i) == "sgdt" or GetMnem(i) == "sldt" or GetMnem(i) == "smsw" or GetMnem(i) == "str"):                 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)

File->Script file 加载py插件

图片[11]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

python大法好啊
我们来逆一波自己的程序好了

目标:反调第一种in指令
首先加载程序,因为代码在dll中,我们对loadLibraryEx下断,几次F9看到加载Tencent2016C.dll后alt+E

图片[12]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

此时把IDA基址改为当前DLL加载基址0X72690000

图片[13]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

现在直接在od中搜索0x726910D8就能定位in的位置了

图片[14]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

F9运行

图片[15]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

对于cpuid指令同理

修改虚拟机相关信息

目标:反调MAC地址
经常看到很多改MAC地址的软件,大部分应该是改了注册表,手动修改:
win+r -> 输入regedit->注册表
定位到网卡处:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class{4D36E972-E325-11CE-BFC1-08002bE10318}

图片[16]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

新建一个字符串值,命名为NetworkAddress,内容是新的MAC地址
“网络连接”中重启本地连接即可

图片[17]-vm去虚拟化教程:Tencent2016C检测参数说明-vm去虚拟化

 

调整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,虚拟机 -> 设置 -> 处理器 -> 禁用二进制翻译加速 可能在某些时候会有帮助

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容