从应用程序中检测虚拟化的操作系统?
我需要检测我的应用程序是否在虚拟化的操作系统实例中运行。
我find了一些有关这个主题的有用信息。 同样的文章出现在多个地方,我不确定原始的来源。 VMware执行特定的无效x86指令来返回关于它自己的信息,而VirtualPC使用带有IN指令的幻数和I / O端口。
这是可行的,但在两种情况下似乎都是无证的行为。 我猜想未来的VMWare或VirtualPC可能会改变这个机制。 有没有更好的办法? 有没有支持两种产品的机制?
同样,有没有办法检测Xen或VirtualBox ?
我不关心这个平台故意要隐藏自己的情况。 例如,蜜jar使用虚拟化,但有时会掩盖恶意软件用来检测它的机制。 我不在乎我的应用程序会认为它在这些蜜jar中没有被虚拟化,我只是在寻找一个“尽力而为”的解决scheme。
该应用程序主要是Java,但我期望使用本机代码加上JNI这个特定的function。 Windows XP / Vista的支持是最重要的,尽pipe在引用文章中描述的机制是x86的通用function,并不依赖于任何特定的操作系统设施。
你有没有听说过蓝色药丸,红色药丸? 。 这是一种用来查看您是否在虚拟机内运行的技术。 该术语的起源源于matrix电影 ,其中Neo提供了蓝色或红色的药丸(留在matrix=蓝色,或进入“真实的”世界=红色)。
以下是一些代码,可以检测到你是否在“matrix”内部运行:
(从这个网站借用的代码也包含了关于这个话题的一些很好的信息):
int swallow_redpill () { unsigned char m[2+4], rpill[] = "\x0f\x01\x0d\x00\x00\x00\x00\xc3"; *((unsigned*)&rpill[3]) = (unsigned)m; ((void(*)())&rpill)(); return (m[5]>0xd0) ? 1 : 0; }
在虚拟机器中运行时,函数将返回1,否则返回0。
在Linux下,我使用了命令: dmidecode (在CentOS和Ubuntu上都有)
从男人:
dmidecode是一种以可读格式转储计算机的DMI(有些人说SMBIOS)表格内容的工具。
所以我search了输出,发现它可能是微软的Hyper-V
Handle 0x0001, DMI type 1, 25 bytes System Information Manufacturer: Microsoft Corporation Product Name: Virtual Machine Version: 5.0 Serial Number: some-strings UUID: some-strings Wake-up Type: Power Switch Handle 0x0002, DMI type 2, 8 bytes Base Board Information Manufacturer: Microsoft Corporation Product Name: Virtual Machine Version: 5.0 Serial Number: some-strings
另一种方法是searcheth0的MAC地址与哪个制造商有关: http : //www.coffer.com/mac_find/
如果它返回微软,VMware等。那么它可能是一个虚拟服务器。
我认为,依靠诸如破碎的SIDT虚拟化之类的技巧,将来不会有什么帮助,因为硬件将插入所有那些奇怪而杂乱的x86架构留下的漏洞。 最好的做法是游说Vm提供商,以标准方式告诉你在虚拟机上 – 至less在用户明确允许的情况下。 但是如果我们假设我们明确允许虚拟机被检测到,那么我们也可以在那里放置可见的标记,对吧? 我build议只更新虚拟机上的磁盘,并提供一个文件告诉你你在虚拟机上 – 例如文件系统根目录下的一个小文本文件。 或者检查ETH0的MAC,并将其设置为给定的已知string。
VMware有一个机制来确定软件是否在VMware虚拟机知识库文章中运行,它有一些源代码。
微软还在“确定是否安装了虚拟机pipe理程序”上有一个页面。 MS在其“服务器虚拟化validationtesting”文档的“ IsVM TEST”部分阐述了虚拟机pipe理程序的这一要求
VMware和MS文档都提到使用CPUID指令来检查pipe理程序存在位(寄存器ECX的位31)
RHEL bugtracker有一个“为CPUID叶0x00000001设置ISVM位(ECX:31)”来设置Xen内核下寄存器ECX的位31。
所以没有涉及到供应商的具体情况,看起来你可以使用CPUID检查来知道你是否虚拟运行。
不可以。这是不可能完全准确地检测到的。 一些虚拟化系统,如QEMU ,将整个机器模拟到硬件寄存器。 让我们来回顾一下:你在做什么? 也许我们可以帮忙。
在virtualbox上,假设您可以控制VM guest,并且您有dmidecode,则可以使用以下命令:
dmidecode -s bios-version
它会返回
VirtualBox
在Linux下,你可以在/ proc / cpuinfo上报告。 如果它在VMware中,它通常会以与裸机不同的方式出现,但并不总是如此。 Virtuozzo显示了底层硬件的传递。
我想推荐一篇关于Usenix HotOS '07, Compibility is Not Transparency:VMM Detection Myths and Realities的文章 ,其中总结了几种技术来说明应用程序是否在虚拟环境中运行。
例如,像redpill一样使用sidt指令(但是也可以通过dynamic转换使该指令变为透明),或者将cpuid的运行时间与其他非虚拟化指令进行比较。
在安装新的Ubuntu时,我发现了一个名为imvirt的包。 看看http://micky.ibh.net/~liske/imvirt.html
尝试阅读SMBIOS结构,尤其是带有BIOS信息的结构。
在Linux中,您可以使用dmidecode实用程序浏览信息。
检查工具virt-what 。 它使用前面提到的dmidecode来确定您是否在虚拟主机和types上。
这个C函数将检测VM Guest OS:
(在Windows上testing,用Visual Studio编译)
#include <intrin.h> bool isGuestOSVM() { unsigned int cpuInfo[4]; __cpuid((int*)cpuInfo,1); return ((cpuInfo[2] >> 31) & 1) == 1; }
我使用这个C#
类来检测Guest OS是否在虚拟环境中运行( 仅适用于Windows ):
sysInfo.cs
using System; using System.Management; using System.Text.RegularExpressions; namespace ConsoleApplication1 { public class sysInfo { public static Boolean isVM() { bool foundMatch = false; ManagementObjectSearcher search1 = new ManagementObjectSearcher("select * from Win32_BIOS"); var enu = search1.Get().GetEnumerator(); if (!enu.MoveNext()) throw new Exception("Unexpected WMI query failure"); string biosVersion = enu.Current["version"].ToString(); string biosSerialNumber = enu.Current["SerialNumber"].ToString(); try { foundMatch = Regex.IsMatch(biosVersion + " " + biosSerialNumber, "VMware|VIRTUAL|AMI|Xen", RegexOptions.IgnoreCase); } catch (ArgumentException ex) { // Syntax error in the regular expression } ManagementObjectSearcher search2 = new ManagementObjectSearcher("select * from Win32_ComputerSystem"); var enu2 = search2.Get().GetEnumerator(); if (!enu2.MoveNext()) throw new Exception("Unexpected WMI query failure"); string manufacturer = enu2.Current["manufacturer"].ToString(); string model = enu2.Current["model"].ToString(); try { foundMatch = Regex.IsMatch(manufacturer + " " + model, "Microsoft|VMWare|Virtual", RegexOptions.IgnoreCase); } catch (ArgumentException ex) { // Syntax error in the regular expression } return foundMatch; } } }
用法:
if (sysInfo.isVM()) { Console.WriteLine("VM FOUND"); }
我试过了我的朋友build议的一种不同的方法。在VMWARE上运行的虚拟机不具有CPU TEMPERATURE属性。 即他们不显示CPU的温度。 我正在使用CPU温度计应用程序检查CPU温度。
(Windows在VMWARE中运行)
(Windows在真实CPU上运行)
所以我编写了一个小C程序来检测Senser的温度
#include "stdafx.h" #define _WIN32_DCOM #include <iostream> using namespace std; #include <comdef.h> #include <Wbemidl.h> #pragma comment(lib, "wbemuuid.lib") int main(int argc, char **argv) { HRESULT hres; // Step 1: -------------------------------------------------- // Initialize COM. ------------------------------------------ hres = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) { cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl; return 1; // Program has failed. } // Step 2: -------------------------------------------------- // Set general COM security levels -------------------------- hres = CoInitializeSecurity( NULL, -1, // COM authentication NULL, // Authentication services NULL, // Reserved RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation NULL, // Authentication info EOAC_NONE, // Additional capabilities NULL // Reserved ); if (FAILED(hres)) { cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl; CoUninitialize(); return 1; // Program has failed. } // Step 3: --------------------------------------------------- // Obtain the initial locator to WMI ------------------------- IWbemLocator *pLoc = NULL; hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc); if (FAILED(hres)) { cout << "Failed to create IWbemLocator object." << " Err code = 0x" << hex << hres << endl; CoUninitialize(); return 1; // Program has failed. } // Step 4: ----------------------------------------------------- // Connect to WMI through the IWbemLocator::ConnectServer method IWbemServices *pSvc = NULL; // Connect to the root\cimv2 namespace with // the current user and obtain pointer pSvc // to make IWbemServices calls. hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace NULL, // User name. NULL = current user NULL, // User password. NULL = current 0, // Locale. NULL indicates current NULL, // Security flags. 0, // Authority (for example, Kerberos) 0, // Context object &pSvc // pointer to IWbemServices proxy ); if (FAILED(hres)) { cout << "Could not connect. Error code = 0x" << hex << hres << endl; pLoc->Release(); CoUninitialize(); return 1; // Program has failed. } cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl; // Step 5: -------------------------------------------------- // Set security levels on the proxy ------------------------- hres = CoSetProxyBlanket( pSvc, // Indicates the proxy to set RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx NULL, // Server principal name RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx NULL, // client identity EOAC_NONE // proxy capabilities ); if (FAILED(hres)) { cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl; pSvc->Release(); pLoc->Release(); CoUninitialize(); return 1; // Program has failed. } // Step 6: -------------------------------------------------- // Use the IWbemServices pointer to make requests of WMI ---- // For example, get the name of the operating system IEnumWbemClassObject* pEnumerator = NULL; hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t(L"SELECT * FROM Win32_TemperatureProbe"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (FAILED(hres)) { cout << "Query for operating system name failed." << " Error code = 0x" << hex << hres << endl; pSvc->Release(); pLoc->Release(); CoUninitialize(); return 1; // Program has failed. } // Step 7: ------------------------------------------------- // Get the data from the query in step 6 ------------------- IWbemClassObject *pclsObj = NULL; ULONG uReturn = 0; while (pEnumerator) { HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if (0 == uReturn) { break; } VARIANT vtProp; // Get the value of the Name property hr = pclsObj->Get(L"SystemName", 0, &vtProp, 0, 0); wcout << " OS Name : " << vtProp.bstrVal << endl; VariantClear(&vtProp); VARIANT vtProp1; VariantInit(&vtProp1); pclsObj->Get(L"Caption", 0, &vtProp1, 0, 0); wcout << "Caption: " << vtProp1.bstrVal << endl; VariantClear(&vtProp1); pclsObj->Release(); } // Cleanup // ======== pSvc->Release(); pLoc->Release(); pEnumerator->Release(); CoUninitialize(); return 0; // Program successfully completed. }
在Vmware机器上输出
在一个真正的CPU上的输出