Virtualisiertes Betriebssystem von einer Anwendung erkennen?


68

Ich muss feststellen, ob meine Anwendung in einer virtualisierten Betriebssysteminstanz ausgeführt wird oder nicht.

Ich habe einen Artikel mit nützlichen Informationen zum Thema gefunden. Der gleiche Artikel erscheint an mehreren Stellen, ich bin mir der Originalquelle nicht sicher. VMware implementiert einen bestimmten ungültigen x86-Befehl, um Informationen über sich selbst zurückzugeben, während VirtualPC eine magische Nummer und einen E / A-Port mit einem IN-Befehl verwendet.

Dies ist praktikabel, scheint jedoch in beiden Fällen ein undokumentiertes Verhalten zu sein. Ich nehme an, eine zukünftige Version von VMWare oder VirtualPC könnte den Mechanismus ändern. Gibt es einen besseren Weg? Gibt es für beide Produkte einen unterstützten Mechanismus?

Gibt es eine Möglichkeit, Xen oder VirtualBox zu erkennen ?

Ich mache mir keine Sorgen über Fälle, in denen die Plattform absichtlich versucht, sich zu verstecken. Beispielsweise verwenden Honeypots Virtualisierung, verschleiern jedoch manchmal die Mechanismen, mit denen Malware sie erkennt. Es ist mir egal, dass meine App denkt, dass sie in diesen Honeypots nicht virtualisiert ist. Ich suche nur nach einer "Best Effort" -Lösung.

Die Anwendung ist hauptsächlich Java, obwohl ich erwarte, für diese spezielle Funktion nativen Code plus JNI zu verwenden. Die Unterstützung von Windows XP / Vista ist am wichtigsten, obwohl die im Artikel beschriebenen Mechanismen allgemeine Funktionen von x86 sind und nicht auf einer bestimmten Betriebssystemfunktion beruhen.


5
Es gibt keine zuverlässige Methode, um festzustellen, wann in einer virtualisierten Umgebung ausgeführt wird. Ich habe Details einschließlich Quellcode auf RedPill, NoPill, Scoopy Doo, Jerry, DMI, OUI, ... all die beliebten "Techniken" und warum sie hier nicht funktionieren: charette.no-ip.com:81/programming / 2009-12-30_Virtualization /…
Stéphane

@ Stéphane Außer einer Handvoll der beschriebenen Techniken wird wahrscheinlich vorausgesetzt, dass die VM oder der VM-Bediener nicht absichtlich versucht, das Programm zu täuschen. Es kann falsche Negative geben, aber ich nehme an, Sie müssen "zuverlässig" definieren
Schilcote

Antworten:


71

Hast du von blauer Pille, roter Pille gehört? . Mit dieser Technik können Sie feststellen, ob Sie in einer virtuellen Maschine ausgeführt werden oder nicht. Der Ursprung des Begriffs stammt aus dem Matrixfilm, in dem Neo eine blaue oder eine rote Pille angeboten wird (um in der Matrix zu bleiben = blau oder um in die 'reale' Welt = rot einzutreten).

Das Folgende ist ein Code, der erkennt, ob Sie in der Matrix ausgeführt werden oder nicht:
(Code, der von dieser Site ausgeliehen wurde und auch einige nützliche Informationen zum jeweiligen Thema enthält):

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

Die Funktion gibt 1 zurück, wenn Sie in einem virutalen Computer ausgeführt werden, andernfalls 0.


11
Korrektur: Es wird 1 zurückgegeben, wenn Sie in einigen der heute verfügbaren virtuellen Maschinen auf einigen Hardwareteilen ausgeführt werden.
Kirk Strauser

Ja, es nutzt in der Tat die Tatsache, dass die virtuelle Maschine keine ganz genaue Darstellung eines realen PCs ist. Wenn
ja,

7
@erik: Es nutzt die Tatsache, dass ein virutalisiertes Betriebssystem ein "zweites" Betriebssystem auf einem Computer ist. Dies bedeutet, dass Ressourcen gemeinsam genutzt werden müssen. In diesem Code wird das IDTR (Interrupt Descriptor Table Register: Check Wikipedia) überprüft, wenn es nicht an der üblichen Stelle ist, dann wissen wir, dass wir virtuell sind
am

1
Nur um es zu beachten. Dieser Code schlägt unter Windows XP Pro unter VMWare Fusion (Version 2.0 (116369)) unter OSX 10.5
Epochwolf

22
Beachten Sie, dass RedPill und die anfänglichen scoopy_doo-Techniken auf Multi-Core-CPUs falsch positive Ergebnisse zurückgeben. Beispiel: Auf einem Quad-Core-System, das nativ ausgeführt wird, wird in 75% der Fälle angezeigt, dass es in einer VM ausgeführt wird. Google für Dinge wie "NoPill", um zusätzliche Details zu erhalten.
Stéphane

24

Unter Linux habe ich den Befehl verwendet: dmidecode (ich habe ihn sowohl unter CentOS als auch unter Ubuntu)

vom Mann:

dmidecode ist ein Tool zum Speichern des DMI-Tabelleninhalts (einige sagen SMBIOS) eines Computers in einem für Menschen lesbaren Format.

Also habe ich die Ausgabe durchsucht und herausgefunden, dass es sich wahrscheinlich um Microsoft Hyper-V handelt

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

Eine andere Möglichkeit besteht darin, zu suchen, mit welchem ​​Hersteller die MAC-Adresse von eth0 zusammenhängt: http://www.coffer.com/mac_find/

Wenn Microsoft, VMware usw. zurückgegeben werden, handelt es sich wahrscheinlich um einen virtuellen Server.


@BlackMamba Es hängt davon ab, ob Sie die Leseberechtigung dazu haben /dev/mem.
Schwern

14

VMware verfügt über einen Mechanismus zum Bestimmen, ob Software in einem Knowledge Base-Artikel der virtuellen VMware-Maschine ausgeführt wird, der Quellcode enthält.

Microsoft hat auch eine Seite zum Thema "Feststellen, ob Hypervisor installiert ist" . MS beschreibt diese Anforderung eines Hypervisors im Abschnitt " IsVM-TEST" des Dokuments "Validierungstest für die Servervirtualisierung "

Die VMware- und MS-Dokumente erwähnen beide die Verwendung des CPUID-Befehls zum Überprüfen des Hypervisor-Present-Bits (Bit 31 des Registers ECX).

Der RHEL-Bugtracker hat einen für "sollte das ISVM-Bit (ECX: 31) für das CPUID-Blatt 0x00000001 setzen" , um das Bit 31 des Registers ECX unter dem Xen-Kernel zu setzen.

Ohne auf die Herstellerangaben einzugehen, können Sie mithilfe der CPUID-Prüfung feststellen, ob Sie virtuell arbeiten oder nicht.


1
Alle kommen nach und nach auf die Idee , aus demselben Liedblatt zu singen .
JdeBP

12

Nein. Dies ist nicht vollständig zu erkennen. Einige Virtualisierungssysteme wie QEMU emulieren eine gesamte Maschine bis in die Hardwareregister. Lassen Sie uns das umdrehen: Was versuchen Sie zu tun? Vielleicht können wir dabei helfen.


Das ist möglich. Obwohl Sie alle Anweisungen emulieren können, die eine virtuelle Maschine ausführt, kann die Anwendung die Wahrheit dennoch durch Ressourcenbeschränkung usw. ermitteln.
ZelluX

Wenn wir einen alten PC auf einer neuen leistungsstarken Hardware emulieren, können wir Latenzen und Ressourcen emulieren.
Eugene Mala

Nun, das ist eher Emulation als Virtualisierung. Am schwierigsten zu verbergen sind Zeitinformationen, insbesondere wenn der Gast über Netzwerkzugriff verfügt und eine externe Uhr verwenden kann.
Matthew Sharp

12

Ich denke, dass es in Zukunft nicht wirklich hilfreich sein wird, sich auf Tricks wie die kaputte SIDT-Virtualisierung zu verlassen, da die Hardware alle Lücken schließt, die die seltsame und unordentliche x86-Architektur hinterlassen hat. Am besten setzen Sie sich bei den VM-Anbietern für eine Standardmethode ein, um festzustellen, ob Sie sich auf einer VM befinden - zumindest für den Fall, dass der Benutzer dies ausdrücklich zugelassen hat. Wenn wir jedoch davon ausgehen, dass wir die Erkennung der VM explizit zulassen, können wir dort genauso gut sichtbare Markierungen platzieren, oder? Ich würde vorschlagen, nur die Festplatte auf Ihren VMs mit einer Datei zu aktualisieren, die Ihnen mitteilt, dass Sie sich auf einer VM befinden - beispielsweise einer kleinen Textdatei im Stammverzeichnis des Dateisystems. Oder überprüfen Sie den MAC von ETH0 und setzen Sie diesen auf einen bestimmten bekannten String.


2
Ihre Lösung (am Ende Ihres Absatzes) funktioniert nicht, wenn Sie keine Kontrolle über die VM haben, auf der Sie ausgeführt werden. = \
Erik Forbes

3
Nein, aber wenn Sie keine Kontrolle über die VM haben, sind alle Wetten trotzdem ungültig. Dann könnte es sich absichtlich verstecken. Die Frage ist also wirklich, warum und wann und in welcher Situation Sie dies tun möchten.
Jakobengblom2

8

Unter virtualbox können Sie diesen Befehl verwenden, vorausgesetzt, Sie haben die Kontrolle über den VM-Gast und Sie haben dmidecode:

dmidecode -s bios-version

und es wird zurückkehren

VirtualBox

7

Ich möchte ein Dokument empfehlen, das unter Usenix HotOS '07 veröffentlicht wurde: " Komprimierbarkeit ist nicht Transparenz: Mythen und Realitäten der VMM-Erkennung" , in dem verschiedene Techniken zum Feststellen der Anwendung der Anwendung in einer virtualisierten Umgebung abgeschlossen werden.

Verwenden Sie beispielsweise die sidt-Anweisung wie redpill (diese Anweisung kann jedoch auch durch dynamische Übersetzung transparent gemacht werden), oder vergleichen Sie die Laufzeit von cpuid mit anderen nicht virtualisierten Anweisungen.


7

Diese C-Funktion erkennt das VM-Gastbetriebssystem:

(Getestet unter Windows, kompiliert mit Visual Studio)

#include <intrin.h>

    bool isGuestOSVM()
    {
        unsigned int cpuInfo[4];
        __cpuid((int*)cpuInfo,1);
        return ((cpuInfo[2] >> 31) & 1) == 1;
    }

kleine Infos hinzufügen, bekommt dieser einige Infos von der CPU?
Entschuldigung IwontTell

5

Unter Linux können Sie über / proc / cpuinfo berichten. Wenn es sich um VMware handelt, wird es normalerweise anders angezeigt als wenn es auf Bare-Metal-Basis ausgeführt wird, jedoch nicht immer. Virtuozzo zeigt einen Durchgang zur zugrunde liegenden Hardware.


5

Lesen Sie die SMBIOS- Strukturen, insbesondere die Strukturen mit den BIOS- Informationen.

Unter Linux können Sie das Dienstprogramm dmidecode verwenden, um die Informationen zu durchsuchen.


2
dmidecodeZum Ausführen ist eine Superuser-Berechtigung (Root) erforderlich, daher ist dies in einer Anwendung nicht so nützlich.
Raedwald


4

Überprüfen Sie das Werkzeug virt-what . Es verwendet den zuvor erwähnten dmidecode, um festzustellen, ob Sie sich auf einem virtualisierten Host befinden und welchen Typ.


3

Ich verwende diese C#Klasse, um festzustellen, ob das Gastbetriebssystem in einer virtuellen Umgebung ausgeführt wird ( nur 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;
            }
        }

}

Verwendung:

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

Vielen Dank, aber Sie erkennen Virtualisierungssoftware, nicht Virtualisierung.
Entschuldigung IwontTell

3

Unter Linux bietet systemd einen Befehl zum Erkennen, ob das System als virtuelle Maschine ausgeführt wird oder nicht.

Befehl:
$ systemd-detect-virt

Wenn das System virtualisiert ist, gibt es den Namen der Virtuslization-Software / -Technologie aus. Wenn nicht, wird ausgegebennone

Wenn auf dem System beispielsweise KVM ausgeführt wird, gilt Folgendes:

$ systemd-detect-virt
kvm

Sie müssen es nicht als sudo ausführen.


2

Ich habe einen anderen von meinem Freund vorgeschlagenen Ansatz ausprobiert. Virtuelle Maschinen, die auf VMWARE ausgeführt werden, haben keine CPU-TEMPERATUR-Eigenschaft. dh sie zeigen nicht die Temperatur der CPU. Ich verwende die CPU-Thermometer-Anwendung zum Überprüfen der CPU-Temperatur.

(Windows läuft in VMWARE) Geben Sie hier die Bildbeschreibung ein

(Windows läuft auf einer echten CPU) Geben Sie hier die Bildbeschreibung ein

Also codiere ich ein kleines C-Programm, um den Temperatursensor zu erkennen

#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.

}

Ausgabe auf einem VMware-Computer Geben Sie hier die Bildbeschreibung ein

Ausgabe auf einer echten CPU Geben Sie hier die Bildbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.