Обнаруживать виртуальную ОС из приложения?

Мне нужно определить, работает ли мое приложение в экземпляре виртуальной ОС или нет.

Я нашел статью с полезной информацией по этой теме. Эта же статья появляется в нескольких местах, я не уверен в исходном источнике. VMware реализует определенную недействительную инструкцию x86 для возврата информации о себе, а VirtualPC использует магическое число и порт ввода/вывода с инструкцией IN.

Это работоспособно, но в обоих случаях оно выглядит недокументированным. Я полагаю, что будущий выпуск VMWare или VirtualPC может изменить механизм. Есть ли способ лучше? Есть ли поддерживаемый механизм для любого продукта?

Аналогично, существует ли способ обнаружить Xen или VirtualBox?

Меня не интересуют случаи, когда платформа намеренно пытается скрыть себя. Например, приманки используют виртуализацию, но иногда скрывают механизмы, которые вредоносная программа будет использовать для ее обнаружения. Меня не волнует, что мое приложение будет думать, что оно не виртуализировано в этих приманках, я просто ищу решение "лучшего усилия".

Приложение в основном Java, хотя я ожидаю использовать собственный код плюс JNI для этой конкретной функции. Поддержка Windows XP/Vista наиболее важна, хотя механизмы, описанные в указанной статье, являются общими функциями x86 и не зависят от какой-либо конкретной ОС.

Ответ 1

Вы слышали о синей таблетке, красной таблетке?. Это метод, используемый для проверки того, работаете ли вы на виртуальной машине или нет. Происхождение термина связано с матричным фильмом, где Нео предлагается голубая или красная таблетка (оставаться внутри матрицы = синий или введите "реальный" мир = красный).

Ниже приведен код, который будет определять, что вы используете внутри "матрицы" или нет:
(код, заимствованный из этого сайта, который также содержит некоторую приятную информацию об этой теме):

 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 в противном случае.

Ответ 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

Другой способ - найти, к какому производителю относится MAC-адрес eth0: http://www.coffer.com/mac_find/

Если он вернет Microsoft, vmware и т.д., то это, вероятно, виртуальный сервер.

Ответ 3

Я думаю, что продвижение вперед, полагаясь на трюки, такие как сломанная виртуализация SIDT, на самом деле не поможет, поскольку оборудование подключает все дыры, которые оставили странную и грязную архитектуру x86. Лучше всего было бы лоббировать поставщиков Vm для стандартного способа сказать, что вы находитесь на виртуальной машине - по крайней мере, для случая, когда пользователь явно разрешил это. Но если мы предположим, что мы явно разрешаем обнаружение виртуальной машины, мы можем также разместить там видимые маркеры, верно? Я бы предложил просто обновить диск на ваших виртуальных машинах файлом, сообщающим вам, что вы находитесь на виртуальной машине - небольшой текстовый файл в корне файловой системы, например. Или проверьте MAC-адрес ETH0 и установите его для данной известной строки.

Ответ 4

У VMware есть Механизмы, чтобы определить, запущено ли программное обеспечение на виртуальной машине VMware Статья базы знаний, в которой есть исходный код.

У Microsoft также есть страница на "Определение, установлен ли Hypervisor" . MS объясняет это требование гипервизора в разделе IsVM TEST из "Проверка валидации сервера"

В документах VMware и MS упоминается использование команды CPUID для проверки присутствующего в гипервизоре бита (бит 31 регистра ECX)

У Bugtracker RHEL есть один для "должен установить бит ISVM (ECX: 31) для листа CPUID 0x00000001" для установки бит 31 регистра ECX под ядром Xen.

Таким образом, не вдаваясь в спецификацию поставщика, похоже, что вы можете использовать проверку CPUID, чтобы знать, работаете ли вы практически или нет.

Ответ 5

Нет. Это невозможно обнаружить с полной точностью. Некоторые системы виртуализации, такие как QEMU, эмулируют всю машину до аппаратных регистров. Позвольте этому повернуть: что вы пытаетесь сделать? Может быть, мы сможем помочь с этим.

Ответ 6

В виртуальном боксе, предполагая, что у вас есть контроль над гостевой VM, и у вас есть dmidecode, вы можете использовать эту команду:

dmidecode -s bios-version

и он вернет

VirtualBox

Ответ 7

В Linux вы можете сообщить о /proc/cpuinfo. Если это в VMware, оно обычно возникает иначе, чем если бы оно было на голом металле, но не всегда. Virtuozzo показывает сквозной доступ к базовому оборудованию.

Ответ 8

Я бы порекомендовал документ, опубликованный в Usenix HotOS '07, "Сопротивляемость - это не прозрачность: мифы о мифах и реальностях VMM", в которых заключены несколько методов, чтобы определить, работает ли приложение в виртуализованной среде.

Например, используйте команду sidt как redpill (но эту инструкцию также можно сделать прозрачной путем динамического перевода) или сравнить время выполнения cpuid с другими невиртуализированными инструкциями.

Ответ 9

При установке новичка Ubuntu я обнаружил пакет под названием imvirt. Посмотрите на него http://micky.ibh.net/~liske/imvirt.html

Ответ 10

Попробуйте прочитать структуры SMBIOS, особенно структуры с BIOS.

В Linux вы можете использовать утилиту dmidecode для просмотра информации.

Ответ 11

Проверьте инструмент virt-what. Он использует ранее упомянутый dmidecode, чтобы определить, находитесь ли вы на виртуализированном хосте и типе.

Ответ 12

Эта функция C обнаружит гостевую ОС VM:

(Протестировано в Windows, скомпилировано с помощью Visual Studio)

#include <intrin.h>

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

Ответ 13

Я использую этот класс C#, чтобы определить, работает ли гостевая ОС внутри виртуальной среды (только окна):

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

Ответ 14

Я попробовал другой подход, предложенный моим другом. Виртуальные машины, работающие на VMWARE, не имеют свойства CPU TEMPERATURE. i.e Они не показывают температуру процессора. Я использую приложение термометра процессора для проверки температуры процессора.

(Windows Running In VMWARE) введите описание изображения здесь

(Windows работает на реальном процессоре) введите описание изображения здесь

Итак, я кодирую программу Small 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 введите описание изображения здесь