从应用程序中检测虚拟化操作系统?

问题描述 投票:63回答:15

我需要检测我的应用程序是否在虚拟化OS实例中运行。

我发现an article有一些关于这个主题的有用信息。同一篇文章出现在多个地方,我不确定原始来源。 VMware实现了一个特定的无效x86指令来返回有关它自身的信息,而VirtualPC使用一个幻数和I / O端口和一个IN指令。

这是可行的,但在两种情况下似乎都是无证件行为。我想VMWare或VirtualPC的未来版本可能会改变机制。有没有更好的办法?这两种产品都有支持的机制吗?

同样,有没有办法检测XenVirtualBox

我并不担心平台故意试图隐藏自己的情况。例如,蜜罐使用虚拟化,但有时会掩盖恶意软件用来检测它的机制。我并不在乎我的应用程序会认为它在这些蜜罐中没有虚拟化,我只是在寻找“尽力而为”的解决方案。

该应用程序主要是Java,但我希望在这个特定的功能中使用本机代码和JNI。 Windows XP / Vista支持是最重要的,尽管参考文章中描述的机制是x86的通用功能,并且不依赖于任何特定的OS工具。

vmware virtualbox virtual-pc xen
15个回答
66
投票

你听说过blue pill, red pill?吗?这是一种用于查看您是否在虚拟机内运行的技术。该术语的起源源自the matrix movie,其中Neo被提供蓝色或红色药丸(留在矩阵内=蓝色,或进入'真实'世界=红色)。

以下是一些代码,可以检测您是否在“矩阵”内运行: (从qazxsw poi借来的代码,其中也包含有关手头主题的一些很好的信息):

this site

在虚拟机内运行时,该函数将返回1,否则返回0。


5
投票

在安装新手Ubuntu时,我发现了名为imvirt的软件包。在dmidecode看一下


5
投票

此C函数将检测VM Guest OS:

(在Windows上测试,使用Visual Studio编译)

http://micky.ibh.net/~liske/imvirt.html

4
投票

检查工具#include <intrin.h> bool isGuestOSVM() { unsigned int cpuInfo[4]; __cpuid((int*)cpuInfo,1); return ((cpuInfo[2] >> 31) & 1) == 1; } 。它使用前面提到的dmidecode来确定您是否在虚拟主机和类型上。


3
投票

在linux上,systemd提供了一个命令,用于检测系统是否作为虚拟机运行。

命令: virt-what

如果系统是虚拟化的,它将输出virtuslization softwarwe / technology的名称。如果没有,那么它输出$ systemd-detect-virt

例如,如果系统正在运行KVM,那么:

none

您不需要将其作为sudo运行。


1
投票

我使用这个$ systemd-detect-virt kvm 类来检测Guest OS是否在虚拟环境中运行(仅限Windows):

sysInfo.cs

C#

用法:

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|A M I|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;
            }
        }

}

0
投票

我尝试了我朋友建议的不同方法。在VMWARE上运行的虚拟机没有CPU TEMPERATURE属性。即他们不显示CPU的温度。我正在使用CPU温度计应用程序来检查CPU温度。

(在VMWARE中运行的Windows) if (sysInfo.isVM()) { Console.WriteLine("VM FOUND"); }

(Windows在真实CPU上运行)enter image description here

所以我编写了一个小型C程序来检测温度传感器

enter image description here

在Vmware机器上输出#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. }

在真正的CPU enter image description here输出


24
投票

在Linux下我使用了命令:dmidecode(我在CentOS和Ubuntu都有它)

来自男人:

dmidecode是一种以人类可读的格式转储计算机的DMI(某些说SMBIOS)表内容的工具。

所以我搜索了输出,发现它可能是Microsoft Hyper-V

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

另一种方法是搜索eth0的MAC地址与哪个制造商相关: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

如果它返回微软,vmware等等,那么它可能是一个虚拟服务器。


12
投票

不可以。完全准确无法检测到。某些虚拟化系统(如http://www.coffer.com/mac_find/)将整个机器模拟到硬件寄存器。让我们来看看:你想做什么?也许我们可以帮忙。


12
投票

VMware有一篇QEMU知识库文章,其中包含一些源代码。

微软还有一个关于Mechanisms to determine if software is running in a VMware virtual machine的页面。 MS在他们的"Determining If Hypervisor Is Installed"文档的IsVM TEST“部分中阐述了虚拟机管理程序的这一要求

VMware和MS文档都提到使用CPUID指令检查虚拟机管理程序存在位(寄存器ECX的第31位)

RHEL bugtracker有一个用于"Server Virtualization Validation Test"在Xen内核下设置寄存器ECX的第31位。

因此,如果没有进入供应商细节,看起来您可以使用CPUID检查来了解您是否正在运行。


11
投票

我认为,继续前进,依靠像破坏的SIDT虚拟化这样的技巧并没有真正帮助,因为硬件插入了奇怪和凌乱的x86架构所留下的所有漏洞。最好的方法是游说Vm提供商以标准方式告诉您在虚拟机上 - 至少在用户明确允许的情况下。但是,如果我们假设我们明确允许检测VM,我们也可以将可见标记放在那里,对吗?我建议只更新VM上的磁盘,并提供一个文件,告诉您自己在VM上 - 例如,文件系统根目录中的一个小文本文件。或者检查ETH0的MAC,并将其设置为给定的已知字符串。


8
投票

在virtualbox上,假设您可以控制VM guest虚拟机并且有dmidecode,则可以使用以下命令:

"should set ISVM bit (ECX:31) for CPUID leaf 0x00000001"

它会回来的

dmidecode -s bios-version

7
投票

我想推荐一篇发表在Usenix HotOS '07上的论文,Comptibility is Not Transparency:VMM Detection Myths and Realities,它总结了几种技术来判断应用程序是否在虚拟化环境中运行。

例如,使用sidt指令作为redpill(但是这条指令也可以通过动态转换使其透明),或者将cpuid的运行时与其他非虚拟化指令进行比较。


5
投票

在Linux下,您可以报告/ proc / cpuinfo。如果它在VMware中,它通常与裸机上的不同,但并非总是如此。 Virtuozzo显示了底层硬件的传递。


5
投票

尝试阅读VirtualBox 结构,特别是带有SMBIOS信息的结构。

在Linux中,您可以使用BIOS实用程序来浏览信息。

© www.soinside.com 2019 - 2024. All rights reserved.