Обнаружить виртуальную ОС из приложения?
Мне нужно определить, запущено ли мое приложение в виртуализированном экземпляре ОС или нет.
Я нашел статью с некоторой полезной информацией по теме. Одна и та же статья появляется в нескольких местах, я не уверен в первоисточнике. VMware реализует конкретную недопустимую инструкцию x86 для возврата информации о себе, тогда как VirtualPC использует магическое число и порт ввода-вывода с инструкцией IN.
Это выполнимо, но в обоих случаях кажется недокументированным поведением. Я предполагаю, что будущий выпуск VMWare или VirtualPC может изменить механизм. Есть ли способ лучше? Есть ли поддерживаемый механизм для любого продукта?
Точно так же, есть ли способ обнаружить Xen или VirtualBox?
Меня не волнуют случаи, когда платформа намеренно пытается спрятаться. Например, honeypots используют виртуализацию, но иногда затеняют механизмы, которые вредоносные программы будут использовать для ее обнаружения. Мне все равно, что мое приложение будет думать, что оно не виртуализировано в этих honeypots, я просто ищу решение "из лучших".
Приложение в основном Java, хотя я ожидаю использовать нативный код плюс JNI для этой конкретной функции. Поддержка Windows XP/Vista является наиболее важной, хотя механизмы, описанные в указанной статье, являются общими характеристиками x86 и не зависят от каких-либо конкретных возможностей ОС.
17 ответов
Вы слышали о синей таблетке, красной таблетке?, Это техника, используемая для определения, работаете ли вы на виртуальной машине или нет. Происхождение термина происходит от фильма "Матрица", где Нео предлагается синяя или красная таблетка (чтобы остаться внутри матрицы = синий или войти в "реальный" мир = красный).
Ниже приведен код, который будет определять, запускаете ли вы внутри "матрицы" или нет:
(код заимствован с этого сайта, который также содержит полезную информацию по данной теме):
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 я использовал команду: 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 и т. Д., То это, вероятно, виртуальный сервер.
В VMware есть механизмы, позволяющие определить, работает ли программное обеспечение в статье базы знаний о виртуальных машинах VMware, в которой есть некоторый исходный код.
У Microsoft также есть страница "Определение, установлен ли гипервизор". MS излагает это требование гипервизора в разделе "Испытание IsVM " документа "Проверка правильности виртуализации сервера".
Документы VMware и MS упоминают об использовании инструкции CPUID для проверки присутствующего гипервизора бита (бит 31 регистра ECX)
У средства отслеживания ошибок RHEL есть один для "должен установить бит ISVM (ECX:31) для листа CPUID 0x00000001", чтобы установить бит 31 регистра ECX под ядром Xen.
Поэтому, не вдаваясь в подробности поставщика, похоже, что вы можете использовать проверку CPUID, чтобы узнать, работает ли вы виртуально или нет.
Нет. Это невозможно обнаружить с полной точностью. Некоторые системы виртуализации, такие как QEMU, эмулируют всю машину вплоть до аппаратных регистров. Давайте перевернем это: что вы пытаетесь сделать? Может быть, мы можем помочь с этим.
Я думаю, что в дальнейшем, опираясь на такие хитрости, как сломанная виртуализация SIDT, на самом деле не поможет, так как аппаратные средства закрывают все дыры, оставленные странной и грязной архитектурой x86. Лучше всего было бы лоббировать поставщиков Vm стандартным способом сообщить, что вы находитесь на виртуальной машине - по крайней мере, в случае, когда пользователь явно разрешил это. Но если мы предположим, что мы явно разрешаем обнаружение виртуальной машины, мы также можем разместить там видимые маркеры, верно? Я бы предложил просто обновить диск на ваших виртуальных машинах файлом, сообщающим вам, что вы находитесь на виртуальной машине - например, небольшим текстовым файлом в корне файловой системы. Или проверьте MAC-адрес ETH0 и установите для него заданную известную строку.
В linux systemd предоставляет команду для определения, работает ли система как виртуальная машина или нет.
Команда:$ systemd-detect-virt
Если система виртуализирована, она выводит имя программного обеспечения / технологии virtuslization. Если нет то выводит none
Например, если система работает с KVM, тогда:
$ systemd-detect-virt
kvm
Вам не нужно запускать его как sudo.
В virtualbox, если у вас есть контроль над гостевой виртуальной машиной и у вас есть dmidecode, вы можете использовать эту команду:
dmidecode -s bios-version
и он вернется
VirtualBox
Эта функция C обнаружит гостевую ОС VM:
(Проверено на Windows, скомпилировано с Visual Studio)
#include <intrin.h>
bool isGuestOSVM()
{
unsigned int cpuInfo[4];
__cpuid((int*)cpuInfo,1);
return ((cpuInfo[2] >> 31) & 1) == 1;
}
Я бы хотел порекомендовать статью, опубликованную на Usenix HotOS '07, " Совместимость - это не прозрачность: мифы и реальность обнаружения VMM", в которой изложены несколько методов, позволяющих определить, работает ли приложение в виртуализированной среде.
Например, используйте инструкцию sidt как redpill (но эту инструкцию также можно сделать прозрачной с помощью динамического перевода) или сравните время выполнения cpuid с другими не виртуализированными инструкциями.
Во время установки нового Ubuntu я обнаружил пакет под названием imvirt. Посмотрите на это по http://micky.ibh.net/~liske/imvirt.html
Под Linux вы можете сообщить о /proc/cpuinfo. Если он в VMware, он обычно появляется не так, как на голом металле, но не всегда. Virtuozzo показывает сквозную связь с базовым оборудованием.
Я попробовал другой подход, предложенный моим другом. Виртуальные машины, работающие на VMWARE, не имеют свойства CPU TEMPERATURE. т.е. они не показывают температуру процессора. Я использую термометр процессора для проверки температуры процессора.
(Windows работает на реальном процессоре)
Поэтому я пишу небольшую программу на C для определения температуры Senser
#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.
}
Проверьте инструмент вирт-чего. Он использует ранее упомянутый 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");
}
Я придумал универсальный способ обнаруживать все типы виртуальных машин Windows с помощью всего одной строчки кода. Поддерживает win7-10 (xp еще не тестировал).
Зачем нужен универсальный способ?
Чаще всего используется способ поиска и сопоставления значений поставщиков из win32. Но что, если существует более 1000 производителей виртуальных машин? тогда вам нужно будет написать код, чтобы сопоставить более 1000 подписей виртуальных машин. Но его время зря. Даже через некоторое время будут запущены новые другие виртуальные машины, и ваш сценарий будет потрачен впустую.
Фон
Я работал над этим много месяцев. Я провел много тестов, по результатам которых заметил, что win32_portconnector всегда имеет значение null и пусто на виртуальных машинах . См. Полный отчет
//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://discord.com/invite/Hu5XPGMTuk
//https://github.com/Back-X/Universal-VM-Detector
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 !");
}
}
}
Вы можете прочитать код или получить скомпилированный исполняемый файл .
Стабильность
Он протестирован во многих средах и очень стабилен.
- Обнаруживает Visrtualbox
- Обнаруживает Vmware
- Обнаруживает Windows Server
- Обнаруживает RDP
- Обнаруживает Virustotal
- Обнаруживает any.run и т.д ...
Не могу добавить комментарий к «Серому программисту» (недостаточно репутации)
Это сообщение просто указывает, что я пытаюсь выполнить «SELECT * FROM Win32_PortConnector» в «Windows XP» или «Windows 7» в моей Windows 10 [версия 10.0.19041.985] Hyper-V. К сожалению, в моей среде это не работает. Я не знаю, почему это не пусто на моих виртуальных машинах. Ниже скриншот.