本文使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 [署名 4.0 国际 (CC BY 4.0)](https://creativecommons.org/licenses/by/4.0/deed.zh) 本文作者: 苏洋 创建时间: 2013年11月17日 统计字数: 10037字 阅读时间: 21分钟阅读 本文链接: https://soulteary.com/2013/11/17/%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A3%80%E6%B5%8B%E7%9B%B8%E5%85%B3%E8%B5%84%E6%96%99.html ----- # 虚拟机检测相关资料 收集整理一下虚拟机检测相关的文章,过段时间用。 ## 方法一 方法一: 使用RDTSC指令来判断虚拟机类型(通过指令执行的时钟周期来判断): ```asm int Ret = 0; __asm { rdtsc xchg ecx, eax rdtsc sub eax, ecx cmp eax, 0xFF jnb DETECT sub edx, edx mov DWORD PTR [Ret], 0 jmp RETURN DETECT: sub eax, ecx mov DWORD PTR [Ret], 1 RETURN: } ``` RDTSC指令将计算机启动以来的CPU运行周期数放到EDX:EAX里面,EDX是高位,EAX是低位。 RDTSC指令还可以[antidebug](http://www.pediy.com/kssd/pediy07/pediy7-510.htm),实现[微秒级计时器](http://www.cnblogs.com/dgx/archive/2009/05/24/1488541.html)。 ## 方法二 方法二:VMware“后门” ```asm mov ecx, 0Ah ; CX=function# (0Ah=get_version) mov eax, 'VMXh' ; EAX=magic mov dx, 'VX' ; DX=magic in eax, dx ; specially processed io cmd ; output: EAX/EBX/ECX = data cmp ebx, 'VMXh' ; also eax/ecx modified (maybe vmw/os ver?) je under_VMware ``` VMware后门开在IO端口0x5658。 利用这个后门时,必需: EAX = 0x564D5868 ("VMXh") EBX 为参数,一般不用。 ECX 低 16 位为功能号。其实是一个函数数组的索引。 Vmware 调用对应的函数处理后门请求。 这个函数数组共有36 个元素,但某些没有定义。 ECX 的高 16 位为功能参数。 EDX = 0x5658 ("VX"),为 IO 端口号。 ```asm bool IsVMWare() { unsigned long _EBX; __try { __asm { // Run the magic code sequence push ebx mov eax, 0x564D5868 mov ebx, 0x8685D465 // Ensure EBX doesnt contain 0x564D5868 :) mov ecx, 10 // The command for obtaining VMWare version information mov dx, 0x5658 in eax, dx mov _EBX, ebx pop ebx }; } __except(1) { // An exception occured, we aint in VMWare return false; } // The code was executed successfuly, check for the magic value return _EBX == 0x564D5868; } ``` ```asm bool IsInsideVMWare() { bool rc = true; __try { __asm { push edx push ecx push ebx mov eax, 'VMXh' mov ebx, 0 // any value but not the MAGIC VALUE mov ecx, 10 // get VMWare version mov edx, 'VX' // port number in eax, dx // read port // on return EAX returns the VERSION cmp ebx, 'VMXh' // is it a reply from VMWare? setz [rc] // set return value pop ebx pop ecx pop edx } } __except(EXCEPTION_EXECUTE_HANDLER) { rc = false; } return rc; } bool IsInsideVPC() { bool rc = false; __try { _asm push ebx _asm mov ebx, 0 // Flag _asm mov eax, 1 // VPC function number // call VPC _asm __emit 0Fh _asm __emit 3Fh _asm __emit 07h _asm __emit 0Bh _asm test ebx, ebx _asm setz [rc] _asm pop ebx } // The except block shouldn't get triggered if VPC is running!! __except(IsInsideVPC_exceptionFilter(GetExceptionInformation())) { } return rc; } ```asm ```c++ int DCVM () { unsigned char m[2 + 4], rpill[] = "/x0f/x01/x0d/x00/x00/x00/x00/xc3"; *((unsigned *)&rpill[3]) = (unsigned)m; ((void( *)())&rpill)(); printf ("idt base: %#x/n", *((unsigned *)&m[2])); if (m[5] > 0xd0) printf ("虚拟机/r/n", m[5]); else printf ("真实机器/r/n"); return 0; } #include int LdtCheck() { unsigned char m[2]; __asm sldt m; //L,not i printf("LDTR: %2.2x %2.2x/n", m[0], m[1]); return (m[0] != 0x00 && m[1] != 0x00) ? 1 : 0; } int main(int argc, char *argv[]) { if (LdtCheck()) printf("Virtual Machine detected./n"); else printf("Native machine detected./n"); return 0; } #include inline int idtCheck () { unsigned char m[2]; __asm sidt m; printf("IDTR: %2.2x %2.2x/n", m[0], m[1]); return (m[1] > 0xd0) ? 1 : 0; } int gdtCheck() { unsigned char m[2]; __asm sgdt m; printf("GDTR: %2.2x %2.2x/n", m[0], m[1]); return (m[1] > 0xd0) ? 1 : 0; } int ldtCheck() { unsigned char m[2]; __asm sldt m; printf("LDTR: %2.2x %2.2x/n", m[0], m[1]); return (m[0] != 0x00 && m[1] != 0x00) ? 1 : 0; } int main(int argc, char *argv[]) { idtCheck(); gdtCheck(); if (ldtCheck()) printf("Virtual Machine detected./n"); else printf("Native machine detected./n"); return 0; } ``` 方法三:环境内进程检测 ```c++ //是否包含某进程 BOOL IsContainsProcess(CString strProName) { PROCESSENTRY32 pe32; //定义结构体变量来保存进程的信息 pe32.dwSize = sizeof(pe32); //填充大小 HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //创建快照 if (hProcessSnap == INVALID_HANDLE_VALUE) { //MessageBox("进程快照失败","提示",MB_OK); exit(1); } //遍历所有快照 BOOL bMore = ::Process32First(hProcessSnap, &pe32); while(bMore) { if (strProName == pe32.szExeFile) { return TRUE; //如果存在该进程,则返回TRUE bMore = FALSE; //停止循环 } else { bMore =::Process32Next(hProcessSnap, &pe32); } } //扫尾 CloseHandle(hProcessSnap); return FALSE; } //程序启动时刻 if ( (IsContainsProcess("VBoxTray.exe")) || (IsContainsProcess("VBoxService.exe")) || (IsContainsProcess("VMwareUser.exe")) || (IsContainsProcess("VMwareTray.exe")) || (IsContainsProcess("VMUpgradeHelper.exe")) || (IsContainsProcess("vmtoolsd.exe")) || (IsContainsProcess("vmacthlp.exe")) ) { AfxMessageBox("请不要在虚拟机中运行该程序"); exit(0); } ``` ## 方法四 方法四:windows主机检查注册表键值 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet等键值是否包含常见虚拟机的键值 ## 方法五 方法五:检查CPUID/硬盘型号/主板型号 参考代码如下,适用没有做过硬件名称修改的虚拟机: 如果使用的是 VMware/Xen HVM/KVM 这样的全虚拟就更难判断一些,最准确的办法是读取 CPUID 来判断,Xen 源代码下面有一段检测是否是 Xen 的 C 语言代码 tools/misc/xen-detect.c,这段代码提供了一个很好的例子,重写了代码,用宏替代了函数,增加了对 VMware 和 KVM 的识别,用 gcc 编译后就可以运行: ```c /* * check if a linux system running on a virtual machine (vmware/xen hvm/kvm) */ #include stdio.h #include string.h #define HYPERVISOR_INFO 0×40000000 #define CPUID(idx, eax, ebx, ecx, edx) asm volatile ( "test % 1, % 1 ; jz 1f ; ud2a ; .ascii "xen" ; 1: cpuid" : " = b" (*ebx), " = a" (*eax), " = c" (*ecx), " = d" (*edx) : "0″ (idx) ); int main(void) { unsigned int eax, ebx, ecx, edx; char string[13]; CPUID(HYPERVISOR_INFO, &eax, &ebx, &ecx, &edx); *(unsigned int *)(string + 0) = ebx; *(unsigned int *)(string + 4) = ecx; *(unsigned int *)(string + 8) = edx; string[12] = 0; if (strncmp(string, "XenVMMXenVMM", 12) == 0) { printf("xen hvmn"); } else if (strncmp(string, "VMwareVMware", 12) == 0) { printf("vmwaren"); } else if (strncmp(string, "KVMKVMKVM", 12) == 0) { printf("kvmn"); } else printf("bare hardwaren"); return 0; ``` 使用wmi检查硬盘型号是否包含VMWARE字符串 ```vb Dim $VFlag = True Dim $obj = ObjGet("winmgmts:\\localhost\root\CIMV2") Dim $objItem = $obj.ExecQuery("SELECT * FROM Win32_DiskDrive", "WQL", 0x10 + 0x20) If IsObj($objItem) Then For $Item In $objItem If StringInStr($Item.Model, "VMWARE") Then $VFlag = False ExitLoop EndIf Next If $VFlag Then MsgBox(0, "", "NOT VMWARE") Else MsgBox(0, "", "IS VMWARE") EndIf Else MsgBox(0, "error", "NO obj") EndIf ``` 组合起来的方法 ```c# $a = _CheckVM() If $a = "" Then MsgBox(0, "我不在虚拟机里", "我不在虚拟机里!") Else MsgBox(0, "我在虚拟机里!", "我在虚拟机里,我的理由是" & @CRLF & @CRLF & $a) EndIf ;检查是否运行于虚拟机环境。返回空值说明运行于真实环境,返回一个字符串,说明运行于虚拟机环境并给出解释。 ;方法:检查虚拟机服务/进程,硬盘,BIOS 主板/芯片组 ;发现2个以上即确定为虚拟机环境 Func _CheckVM() $strComputer = '.' $objWMIService = ObjGet('winmgmts:\\' & $strComputer & '\root\cimv2') $vmhit_count = 0 $vmhit_details = "" ; 检查虚拟机管理程序 If ProcessExists("VBoxService.exe") Or ProcessExists("VBoxTray.exe") Or ProcessExists("VMwareTray.exe") Or ProcessExists("VMwareUser.exe") Then _AddVMHit($vmhit_count, $vmhit_details, "RUNNING SOFTWARE", "Found a Vbox or VMware guest OS service or tray process") ; 检查虚拟机设备 If Not IsObj($objWMIService) Then MsgBox(0, "", "? WTF?") Return "" EndIf ; 检查虚拟机硬盘 $colItems = $objWMIService.ExecQuery('SELECT * FROM Win32_DiskDrive', 'WQL', 0x10 + 0x20) If IsObj($colItems) Then For $objItem In $colItems $vReturn = $objItem.Model Select Case StringInStr($vReturn, "VBOX HARDDISK") _AddVMHit($vmhit_count, $vmhit_details, "DISKS", "Found device ""VBOX HARDDISK""") Case StringInStr($vReturn, "QEMU HARDDISK") _AddVMHit($vmhit_count, $vmhit_details, "DISKS", "Found device ""QEMU HARDDISK""") Case StringInStr($vReturn, "VMWARE VIRTUAL IDE HARD DRIVE") _AddVMHit($vmhit_count, $vmhit_details, "DISKS", "Found device ""VMWARE VIRTUAL IDE HARD DRIVE""") Case StringInStr($vReturn, "VMWARE Virtual S SCSI Disk Device") _AddVMHit($vmhit_count, $vmhit_details, "DISKS", "Found device ""VMWARE Virtual S SCSI Disk Device""") EndSelect Next EndIf ; 检查虚拟机BIOS $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_BIOS", "WQL", 0x10 + 0x20) If IsObj($colItems) Then For $objItem In $colItems Select Case StringInStr($objItem.BIOSVersion(0), "Vbox") _AddVMHit($vmhit_count, $vmhit_details, "BIOS", "Found Vbox BIOS version") Case StringInStr($objItem.SMBIOSBIOSVersion, "virt") _AddVMHit($vmhit_count, $vmhit_details, "BIOS", "Found Vbox BIOS version") EndSelect Next EndIf ; 检查虚拟机主板/芯片组 $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_Baseboard", "WQL", 0x10 + 0x20) If IsObj($colItems) Then For $objItem In $colItems Select Case StringInStr($objItem.Name, "Base Board") And StringInStr($objItem.Product, "440BX Desktop Reference Platform") _AddVMHit($vmhit_count, $vmhit_details, "MOTHERBOARD", "Found VMware-style motherboard, ""440BX Desktop Reference Platform"" / Name=""Base Board""") EndSelect Next EndIf If $vmhit_count >= 2 Then Return $vmhit_details & @CRLF & @CRLF & "Hits in " & $vmhit_count & " of 4 hardware categories - probably a virtual machine." Else Return "" EndIf EndFunc ;==>_CheckVM Func _AddVMHit(ByRef $vmhit_count, ByRef $vmhit_details, $this_hit_category, $this_hit_text) If StringInStr($vmhit_details, "In CATEGORY:" & $this_hit_category & ":") Then $vmhit_details &= " and " & $this_hit_text Else If $vmhit_details > "" Then $vmhit_details &= @CRLF $vmhit_details &= "In CATEGORY:" & $this_hit_category & ": " & $this_hit_text $vmhit_count += 1 EndIf EndFunc ;==>_AddVMHit ``` ```c# Opt("MustDeclareVars", 1) If _VCheck() Then MsgBox(0, "", "True") Else MsgBox(0, "", "False") EndIf Func _VCheck() Local $strComputer = ".", $sMake, $sModel, $sBIOSVersion, $bIsVM, $sVMPlatform Local $objWMIService = ObjGet("winmgmts:\\" & $strComputer & "\root\CIMV2") Local $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_ComputerSystem") If IsObj($colItems) Then For $objItem In $colItems ;MsgBox(0,"","Name: " & $objItem.Name) $sMake = $objItem.Manufacturer $sModel = $objItem.Model Next EndIf $colItems = $objWMIService.ExecQuery("SELECT * FROM Win32_BIOS", "WQL", 0x10 + 0x20) If IsObj($colItems) Then For $objItem In $colItems ;MsgBox(0,"",$objItem.BIOSVersion(0)) $sBIOSVersion = $objItem.SMBIOSBIOSVersion Next EndIf $bIsVM = False $sVMPlatform = "" MsgBox(0, "", "Manufacturer=" & $sMake) MsgBox(0, "", "Model=" & $sModel) MsgBox(0, "", "BIOSVersion=" & $sBIOSVersion) If $sModel = "Virtual Machine" Then ; Microsoft virtualization technology detected, assign defaults $sVMPlatform = "Hyper-V" $bIsVM = True ; Try to determine more specific values Switch $sBIOSVersion Case "VRTUAL - 1000831" $bIsVM = True $sVMPlatform = "Hyper-V 2008 Beta or RC0" Case "VRTUAL - 5000805", "BIOS Date: 05/05/08 20:35:56 Ver: 08.00.02" $bIsVM = True $sVMPlatform = "Hyper-V 2008 RTM" Case "VRTUAL - 3000919" $bIsVM = True $sVMPlatform = "Hyper-V 2008 R2" Case "A M I - 2000622" $bIsVM = True $sVMPlatform = "VS2005R2SP1 or VPC2007" Case "A M I - 9000520" $bIsVM = True $sVMPlatform = "VS2005R2" Case "A M I - 9000816", "A M I - 6000901" $bIsVM = True $sVMPlatform = "Windows Virtual PC" Case "A M I - 8000314" $bIsVM = True $sVMPlatform = "VS2005 or VPC2004" EndSwitch ElseIf $sModel = "VMware Virtual Platform" Then ; VMware detected $sVMPlatform = "VMware" $bIsVM = True ElseIf $sModel = "VirtualBox" Then ; VirtualBox detected $bIsVM = True $sVMPlatform = "VirtualBox" Else EndIf If $bIsVM Then MsgBox(0, "", "IsVirtualMachine=True") MsgBox(0, "", "VirtualMachinePlatform=" & $sVMPlatform) Else MsgBox(0, "", "IsVirtualMachine=False") EndIf Return $bIsVM EndFunc ;==>_VCheck ``` 特别感谢以下资料出处: - [判断是否在虚拟机运行的简单方法](http://hi.baidu.com/lisl03/item/660c2104d344e46ed55a113e) - [反虚拟机程序测试](http://www.cnblogs.com/tk091/archive/2012/04/21/2461158.html) - [如何判断Linux是否运行在虚拟机上](http://www.hx95.com/Article/Tech/201112/49785.html) - [[AU3基础] 如何判断当前环境是否在Vmware的虚拟机里面?](http://www.autoitx.com/thread-37290-1-1.html) - [ANTI ANTI-VMWare](http://www.2cto.com/Article/201012/80771.html) - [判断程序的执行环境是VM](http://blog.csdn.net/iiprogram/article/details/2201709)