【问题标题】:Detect virtualized OS from an application?从应用程序中检测虚拟化操作系统?
【发布时间】:2010-09-14 07:50:39
【问题描述】:

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

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

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

同样,有没有办法检测XenVirtualBox

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

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

【问题讨论】:

  • 没有可靠的方法来确定何时在虚拟化环境中运行。我有详细信息,包括 RedPill、NoPill、Scoopy Doo、Jerry、DMI、OUI 的源代码……所有流行的“技术”以及为什么它们在这里不起作用:charette.no-ip.com:81/programming/2009-12-30_Virtualization/…
  • @Stéphane 除了少数描述的技术假设 VM 或 VM 操作员没有故意试图欺骗程序可能会起作用。可能会有误报,但我想你必须定义“可靠”

标签: virtualbox vmware virtualization xen virtual-pc


【解决方案1】:

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

以下是一些代码,可以检测您是否在“矩阵”内运行:
(从this site借来的代码还包含一些关于手头主题的好信息):

 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。

【讨论】:

  • 更正:当您在一些今天可用的虚拟机中运行时,它将返回 1,在某些硬件上。
  • 是的,它确实利用了这样一个事实,即虚拟机并不是真实 PC 的完全准确表示。如果是这样,那么没有(好的)方法可以检测到您正在运行虚拟
  • @erik:它利用了虚拟化操作系统是机器上的“第二个”操作系统这一事实。这意味着需要共享资源。在这段代码中,将检查 IDTR(中断描述符表寄存器:检查维基百科),如果它不在通常的位置,那么我们知道我们是虚拟的
  • 请注意。此代码在 OSX 10.5 上的 VMWare Fusion(版本 2.0 (116369))中运行的 Windows XP Pro 中失败
  • 请注意,RedPill 和最初的 scoopy_doo 技术将在多核 CPU 上返回误报。例如:在一个原生运行的四核系统上,75% 的时间它会告诉你它在虚拟机中运行。谷歌搜索“NoPill”之类的内容以获取更多详细信息。
【解决方案2】:

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

来自男人:

dmidecode 是一个转储文件的工具 计算机的 DMI(有人说是 SMBIOS)表 内容以人类可读的格式。

所以我搜索了输出,发现它可能是 Microsoft 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

另一种方法是搜索eth0的MAC地址与哪个厂商有关:http://www.coffer.com/mac_find/

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

【讨论】:

  • @BlackMamba 这取决于你是否拥有/dev/mem的读取权限。
【解决方案3】:

VMware 有一篇 Mechanisms to determine if software is running in a VMware virtual machine 知识库文章,其中包含一些源代码。

Microsoft 在"Determining If Hypervisor Is Installed" 上也有一个页面。 MS 在其"Server Virtualization Validation Test" 文档的“IsVM 测试”部分中详细说明了虚拟机管理程序的这一要求

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

RHEL bugtracker 有一个用于"should set ISVM bit (ECX:31) for CPUID leaf 0x00000001" 设置 Xen 内核下寄存器 ECX 的第 31 位。

因此,无需了解供应商细节,您似乎可以使用 CPUID 检查来了解您是否在虚拟运行。

【讨论】:

【解决方案4】:

没有。这是不可能完全准确地检测到的。一些虚拟化系统,如QEMU,模拟整个机器到硬件寄存器。让我们扭转局面:你想做什么?也许我们可以帮忙。

【讨论】:

  • 这是可能的。虽然你可以模拟虚拟机执行的每一条指令,但应用程序仍然可以通过资源限制等发现真相。
  • 如果我们在新的强大硬件上模拟旧 PC - 我们可以模拟延迟和资源。
  • 嗯,这是仿真而不是虚拟化。最难隐藏的是时间信息,尤其是在访客可以访问网络并且可以使用外部时钟的情况下。
【解决方案5】:

我认为,在未来,依靠诸如损坏的 SIDT 虚拟化之类的技巧并没有真正的帮助,因为硬件会堵住怪异而凌乱的 x86 架构留下的所有漏洞。最好的办法是游说 Vm 提供者以一种标准的方式告诉您您在 VM 上——至少在用户明确允许这样做的情况下。但是,如果我们假设我们明确允许检测到 VM,我们也可以在其中放置可见标记,对吗?我建议只用一个文件更新你的虚拟机上的磁盘,告诉你你在虚拟机上——例如,文件系统根目录中的一个小文本文件。或者检查 ETH0 的 MAC,并将其设置为给定的已知字符串。

【讨论】:

  • 如果您无法控制正在运行的虚拟机,您的解决方案(在段落末尾)将不起作用。=\
  • 不,但是如果您无法控制虚拟机,那么无论如何,所有的赌注都将被取消。那么它很可能会故意隐藏。所以问题实际上是你为什么、何时以及在何种情况下要这样做。
【解决方案6】:

在 virtualbox 上,假设你可以控制 VM 来宾并且你有 dmidecode,你可以使用这个命令:

dmidecode -s bios-version

它会返回

VirtualBox

【讨论】:

    【解决方案7】:

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

    例如,像redpill一样使用sidt指令(但这条指令也可以通过动态翻译使其透明),或者将cpuid的运行时间与其他非虚拟化指令进行比较。

    【讨论】:

      【解决方案8】:

      在安装新的 Ubuntu 时,我发现了名为 imvirt 的软件包。看看http://micky.ibh.net/~liske/imvirt.html

      【讨论】:

        【解决方案9】:

        此 C 函数将检测 VM 来宾操作系统:

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

        #include <intrin.h>
        
            bool isGuestOSVM()
            {
                unsigned int cpuInfo[4];
                __cpuid((int*)cpuInfo,1);
                return ((cpuInfo[2] >> 31) & 1) == 1;
            }
        

        【讨论】:

        • add little info,这个是从cpu获取一些信息吗?
        • 我缺乏判断代码的知识,该代码也发布在其他地方。这里有两个有趣的 cmets:To clarify, this piece of code uses the cpuid instruction to detect if the feature bit is set that indicates the code is running on a hypervisor. There is, of course, no requirement that an actual hypervisor always sets this bit, especially for software hypervisors.I would not use this. Tested false positive (Windows 10, VS) on my PC. I have virtualization support turned on in BIOS, but not running in VM, so it might be that (?).
        【解决方案10】:

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

        命令:
        $ systemd-detect-virt

        如果系统被虚拟化,则输出虚拟化软件/技术的名称。 如果不是,则输出none

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

        $ systemd-detect-virt
        kvm
        

        你不需要以 sudo 运行它。

        【讨论】:

          【解决方案11】:

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

          【讨论】:

            【解决方案12】:

            尝试阅读SMBIOS 结构,尤其是带有BIOS 信息的结构。

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

            【讨论】:

            • dmidecode 需要超级用户 (root) 权限才能运行,所以它对应用程序没有那么有用。
            【解决方案13】:

            检查工具virt-what。它使用前面提到的 dmidecode 来确定您是否在虚拟主机上和类型。

            【讨论】:

              【解决方案14】:

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

              (在 VMWARE 中运行的 Windows)

              (在真实 CPU 上运行的 Windows)

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

              #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 上的输出

              【讨论】:

              • powershell 有更简单的方法。 Get-WMIObject MSAcpi_ThermalZoneTemperature -Namespace root/wmi。但它需要管理员权限。
              【解决方案15】:

              我使用这个 C# 类来检测客户操作系统是否在虚拟环境中运行(仅限 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|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;
                          }
                      }
              
              }
              

              用法:

                      if (sysInfo.isVM()) { 
                          Console.WriteLine("VM FOUND");
                      }
              

              【讨论】:

              • 谢谢,但您检测到的是虚拟化软件而不是虚拟化。
              【解决方案16】:

              我想出了一种通用方法,只需一行代码即可检测每种类型的 Windows 虚拟机。支持win7--10(xp尚未测试)。

              为什么我们需要通用方式?

              最常用的方法是从 win32 搜索和匹配供应商值。但是如果有 1000 多家虚拟机制造商呢?那么您将不得不编写一个代码来匹配 1000 多个 VM 签名。但它的时间浪费。即使过了一段时间,也会有新的其他虚拟机启动,而您的脚本将被浪费。

              背景

              我为此工作了好几个月。我做了很多测试,我观察到: win32_portconnector 在 VM 上始终为空且为空。请查看完整报告

              //asked at: https://stackoverflow.com/q/64846900/14919621
              what win32_portconnector is used for ? This question have 3 parts.
              1) What is the use case of win32_portconnector ?    //https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-portconnector
              2) Can I get state of ports using it like Mouse cable, charger, HDMI cables etc ?
              3) Why VM have null results on this query : Get-WmiObject Win32_PortConnector ?
              

              在虚拟机上:

              PS C:\Users\Administrator> Get-WmiObject Win32_PortConnector
              

              在真实环境中:

              PS C:\Users\Administrator> Get-WmiObject Win32_PortConnector
              Tag                         : Port Connector 0
              ConnectorType               : {23, 3}
              SerialNumber                :
              ExternalReferenceDesignator :
              PortType                    : 2
              
              Tag                         : Port Connector 1
              ConnectorType               : {21, 2}
              SerialNumber                :
              ExternalReferenceDesignator :
              PortType                    : 9
              
              Tag                         : Port Connector 2
              ConnectorType               : {64}
              SerialNumber                :
              ExternalReferenceDesignator :
              PortType                    : 16
              
              Tag                         : Port Connector 3
              ConnectorType               : {22, 3}
              SerialNumber                :
              ExternalReferenceDesignator :
              PortType                    : 28
              
              Tag                         : Port Connector 4
              ConnectorType               : {54}
              SerialNumber                :
              ExternalReferenceDesignator :
              PortType                    : 17
              
              Tag                         : Port Connector 5
              ConnectorType               : {38}
              SerialNumber                :
              ExternalReferenceDesignator :
              PortType                    : 30
              
              Tag                         : Port Connector 6
              ConnectorType               : {39}
              SerialNumber                :
              ExternalReferenceDesignator :
              PortType                    : 31
              

              显示代码

              基于这些测试,我制作了一个可以检测 windows 虚拟机的小程序。

              //@graysuit
              //https://graysuit.github.io
              //https://github.com/Back-X/anti-vm
              using System;
              using System.Windows.Forms;
              
              public class Universal_VM_Detector
              {
                  static void Main()
                  {
                      if((new System.Management.ManagementObjectSearcher("SELECT * FROM Win32_PortConnector")).Get().Count == 0)
                      {
                          MessageBox.Show("VM detected !");
                      }
                      else
                      {
                          MessageBox.Show("VM NOT detected !");
                      }
                  }
              }
              

              您可以read code 或获取compiled executable

              稳定性

              在很多环境下测试过,非常稳定。

              • 检测Visrtualbox
              • 检测到 Vmware
              • 检测到 Windows 服务器
              • 检测 RDP
              • 检测到病毒总数
              • 检测到any.run 等等……

              【讨论】:

              • 在安装了 Windows 11 Enterprise 评估版的 Oracle VirtualBox 上完美运行。谢谢。
              猜你喜欢
              • 1970-01-01
              • 2017-02-01
              • 2013-05-21
              • 1970-01-01
              • 2012-10-11
              • 1970-01-01
              • 1970-01-01
              • 2012-10-21
              相关资源
              最近更新 更多