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

StackOverflow https://stackoverflow.com/questions/154163

Вопрос

Мне нужно определить, запущено ли мое приложение в виртуализированном экземпляре операционной системы или нет.

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

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

Аналогично, есть ли способ обнаружить Ксен или Виртуальный ящик?

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

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

Это было полезно?

Решение

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

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

 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 я использовал команду: dmid - код ( У меня это есть как на 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 и т.д..тогда это, вероятно, виртуальный сервер.

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

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

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

В RHEL bugtracker есть один для "следует установить бит ISVM (ECX: 31) для конечного идентификатора CPUID 0x00000001" чтобы установить бит 31 регистра ECX в ядре Xen.

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

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

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

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

dmidecode -s bios-version

и это вернется

VirtualBox

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

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

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

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

Эта функция C обнаружит виртуальную машину Гостевой операционной системы:

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

#include <intrin.h>

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

Попробуйте, прочитав SMBIOS структуры, особенно структуры с БИОС Информация.

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

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

Я использую это 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");
        }

В Linux systemd предоставляет команду для определения, запущена ли система как виртуальная машина или нет.

Команда:
$ systemd-detect-virt

Если система виртуализирована, то она выводит название программного обеспечения для виртуализации /технологии.Если нет, то он выводит none

Например, если система работает под управлением KVM, то:

$ systemd-detect-virt
kvm

Вам не нужно запускать его как sudo.

Я попробовал другой подход, предложенный моим другом.Виртуальные машины, работающие на VMWARE, не имеют свойства ТЕМПЕРАТУРЫ процессора.то есть они не Показывают Температуру процессора.Я использую приложение CPU Thermometer для проверки температуры процессора.

(Windows, работающая в VMWARE) enter image description here

(Windows, работающая на реальном процессоре) enter image description here

Поэтому я создаю небольшую программу на языке Си для определения датчика температуры

#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 enter image description here

Вывод на реальном процессоре enter image description here

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top