Как узнать виртуальная машина или нет windows

I’m remoting desktop to windows servers in our Lab/datacenter. I have a requirement to figure out all our servers are virtual machines or physical servers programatically, certainly we have the environment sheet tell us which is which. But I need to write code to distinguish it. What technique I need to use? I didn’t find a .Net Assembly to do that. Looking for expert to share your knowledge or guidance, any research direction or link, anything will be appreciated!

D Stanley's user avatar

D Stanley

150k11 gold badges178 silver badges240 bronze badges

asked Mar 18, 2013 at 19:49

ljh's user avatar

You can try to use the following PowerShell script, it utilizes WMI to find out if machine is virtual machine or physical machine.


gwmi -q "select * from win32_computersystem"

Certainly, you can use C# code to query WMI too. The output of script above will be like following:

Domain:   ...
Manufacturer: Microsoft Corporation
Model: Virtual Machine
Name : .....
....

answered Mar 18, 2013 at 20:18

0

To check this from the command prompt you can run this: systeminfo | find "System"

Example output for virtual server:

System Manufacturer:       Microsoft Corporation    
System Model:              Virtual Machine    
System Type:               x64-based PC

Example output for physical server:

System Manufacturer:       HP
System Model:              ProLiant BL460c G6
System Type:               x64-based PC

answered Aug 8, 2013 at 10:49

Mattias Lindberg's user avatar

Mattias LindbergMattias Lindberg

2,0743 gold badges24 silver badges34 bronze badges

As far as I know there is no easy way to do this.

There are a few workarounds but there is, at least as far as I know, not a one-size-fits-all solution.

Ben Armstrong wrote a post about Detecting Microsoft virtual machines and there’s a low-level trick which can determine whether you are running within a Virtual PC or VMWare but that still leaves out VirtualBox and others.

A trick you might want to try is to detect whether VMWare Tools or VirtualBox Tools are installed. In most cases they are installed on the guest OS to provide needed features but it will be hard to maintain the different installation GUIDS on your end so it’s not an ideal solution.

— Also , if the VM is running in a Linux KVM environment, the output is like this one
enter image description here

Nick Westgate's user avatar

answered Mar 18, 2013 at 20:02

Jensen's user avatar

JensenJensen

3,5082 gold badges26 silver badges43 bronze badges

0

There is no easy way to tell if you’re running in a bare metal or in a virtual computer, the best thing you can do is to get some hardware info and made an educated guess, for example, if the machine have a network adapter which contains Microsoft, VMware, Oracle, Hyper-V, Virtual or VirtualBox, most likely it’s a virtual machine given that neither Microsoft, Oracle, or VMware fabricate network cards.

As you use C#, the class for retrieving this and other hardware info is ManagementClass, also there is this nice project that let you retrieve tons of info from your computer using ManagementClass.

answered Mar 18, 2013 at 20:09

Rafael's user avatar

RafaelRafael

2,8271 gold badge16 silver badges17 bronze badges

0

Run the systeminfo command @ command prompt see the system menufacturer n system model details. There you can find the virtule and physical machine information.

answered Apr 2, 2013 at 18:08

Ajay Yadav's user avatar

0

Try this:

FOR /F "tokens=*" %a IN ('wmic bios get bioscharacteristics^|find /c "33"') DO set USBlegacy=%a

This returns «1» for the limited range of desktops and laptops in my environment and «0» for VMWare workstation 9, ESX 5.5, and Citrix 6.5 and 7.6. BIOSCharacteristic «50» (one «reserved for system vendor») I’ve only found in the four virtual environments so that would work in reverse.

Edit: or there’s this:

FOR /F "tokens=*" %a IN ('wmic path win32_pnpentity get ^|find /c "ACPI Fan"') DO set ACPIfan=%a

Returns «5» on an HP Desktop, «0» on VMware workstation 9 and ESX 5.5, not tested on the others.

answered Jul 14, 2015 at 13:37

AndyKn's user avatar

3

you can use this command in cmd or powershell

SYSTEMINFO

You will find a line with the following text (or similar):

System Manufacturer: VMware, Inc.
System Model: VMware Virtual Platform

answered Dec 23, 2017 at 6:44

Hamid KhanZadeh's user avatar

This works very well in Powershell:

$ecv = (get-wmiobject win32_BIOS).EmbeddedControllerMajorVersion
$isPhysical = $ecv -gt 0 -and $ecv -lt 255

A VM does not have a controller or has a dummy-value of ‘0’ as a version.

answered Mar 16 at 7:38

Carsten's user avatar

CarstenCarsten

1,65215 silver badges21 bronze badges

2

The only *programmatic* way I know of doing this reliably is:

  1. Write an app that crawls your network (or IP range) to get a list of machines.
  2. Display those machines to a person and ask them to check a box if it’s a VM…
  3. Print the report.

answered Mar 18, 2013 at 20:17

NotMe's user avatar

NotMeNotMe

87.4k27 gold badges171 silver badges245 bronze badges

Как узнать, работаю ли я на виртуальной машине или нет?


Есть ли способ узнать, является ли машина с Windows, на которой я работаю, виртуальной или физической? (Я подключаюсь с помощью RDP к машине. Если это виртуальная машина, она работает и обрабатывается VMWare).


Ответы:


Если это Windows, просто взгляните на аппаратные экраны. У него будет миллиард пять виртуальных устройств под маркой VMWare.







В окне CMD введите:

SYSTEMINFO

Вы найдете строку со следующим текстом (или аналогичным):

System Manufacturer:       VMware, Inc.
System Model:              VMware Virtual Platform



Если это обрабатывается VMware, это не так уж сложно в настоящий момент. Это может измениться в будущем.

# dmidecode -s system-manufacturer
VMware, Inc.






В Windows из CMD:

Systeminfo | findstr /i model

возвращает что-то вроде:

System Model:              VMware Virtual Platform
                           [01]: Intel64 Family 6 Model 26 Stepping 5 GenuineInt


В Linux запустите это:

$ dmesg |grep -i hypervisor
 Hypervisor detected: KVM




Если вы находитесь в Windows, как говорит Castrocra , вы можете запустить systeminfoкоманду из командной оболочки cmd , а затем искать «версию BIOS».

Вероятно, это настоящие машины:

BIOS Version:              Dell Inc. A03, 06/12/2010
BIOS Version:              Phoenix Technologies, LTD MS7254 1.08, 08/03/2007

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

BIOS Version:              VMware, Inc. VMW71.00V.0.B64.1201040214, 04/01/2012





На него уже дан ответ, но FWIW вы можете сделать это в powershell:

gwmi -q "select * from win32_computersystem"

«Производитель» будет «Корпорация Microsoft», а «Модель» будет «Виртуальная машина», если это виртуальная машина, или в противном случае должна отображаться обычная информация о производителе, например «Dell Inc.» и «PowerEdge R210 II» соответственно.






Если это Unix VM, используйте imvirt . Это скрипт на Perl, который обнаруживает VMWare, Xen и некоторые другие.



Один (относительно) простой способ обнаружения ключевой информации о виртуализации — через WMI / WBEM. Вы можете использовать пространство имен root \ CIM2 и получить доступ к классу Baseboard (полному интересной информации BIOS), чтобы получить описание «физической» системы. Этот класс часто включает информацию о материнской плате и шасси — производство, модель, серийный номер, другое.

Запустите следующую команду из командной строки или сеанса PowerShell:

wmic baseboard get manufacturer, product, Serialnumber, version


Еще проще — wmic / node: bios получает серийный номер

Все, что возвращает серийный номер в стиле Dell, является физическим.

Он также выдаст «VMware-42 22 26 a8 dd 6e e3 b3-2e 03 fc 2c 92 ae 2e 89», если это виртуальная машина.


У меня был тот же вопрос, и я обнаружил, что существует много процессов с именем «VM», например VMWareTray.exe.



nbtstat -a Результат покажет вам, что виртуальные машины имеют специальный префикс 00-50-56-XX-XX-XX. Есть также другой префикс, который он использует, но я не могу вспомнить в верхней части головы, но я помню, что Vcenter использует 00-50-56-XX-XX-XX, так что это тот, который я проверяю только.

Я думаю, что это лучший способ, лично.



Как определить, физическая или виртуальная машина

Майкрософт Виндоус
Если вы подключены через RDP к удаленной машине и хотите знать, является ли это виртуальной или физической машиной, вот как это сделать:

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

Вы также можете открыть командную строку и выполнить следующую команду:

системная информация

Эта команда предоставит вам подробную информацию о вашей машине. Теперь прокрутите вверх и найдите строку «Производитель системы».

Если это физическая машина (пользовательская сборка), то результатом в большинстве случаев будет:

Производитель системы: Производитель системы

В случае пользовательской сборки это должен быть IBM, Dell или другой производитель.

Если это виртуальная машина VMware, результат должен быть:

Производитель системы: VMware, Inc.
Модель системы: VMware Virtual Platform

Если это машина Hyper V или Microsoft Virtual PC, результат должен выглядеть следующим образом:

Производитель системы: Microsoft Corporation
Модель системы: виртуальная машина

Надеюсь, вы найдете эту информацию полезной.

Если у вас вот так вдруг появилась необходимость определить, на какой все-таки платформе для виртуализации работает ваша Windows виртуальная машина, то вы можете легко это сделать с помощью стандартной системной утилиты «systeminfo». Запустив её в cmd-окне, вы получите всю необходимую информацию:

  • Oracle VirtualBox и Windows XP:

winxp-virtualbox.png

  • VMware ESXi 5.0 и Windows XP:

esxi5-winxp.png

  • VMware ESXi 4.1 U1 и Windows 2008 R2:

esxi4-win2008r2.png

К сожалению, у меня нет под рукой виртуальной машины, работающей на «Xen» или «XenServer» хосте, но думаю, что «systeminfo» и здесь выдаст правильную информацию.

Время на прочтение
14 мин

Количество просмотров 24K

Одним жуть каким прохладным январским утром от знакомого прилетел вопрос — как на C# определить, не запущена ли программа в ОС (оконное приложение в ОС Windows 7 или новее) на виртуальной машине.

Требования к такому детектору были достаточно жёсткими:

  1. Должен быть полностью в исходных кодах,
  2. Должен собираться с помощью Visual Studio,
  3. Должен работать из-под аккаунта непривилегированного пользователя (нельзя использовать методы, требующие, к примеру, установки драйверов устройств, или иных операций, для которых нужны права администратора),
  4. Разрешено использовать .NET Framework 4.5 и никаких лишних зависимостей (типа Visual C++ Redistributable Package).

Под катом описание реализованного детектора на C# (в следующей части — с некоторыми элементами C++) и приличным количеством неприличного кода с использованием Visual Studio 2015 Community.

Структура публикации

  • 1 уровень. Изучение матчасти и простейших существующих решений:
    • немного теории касательно виртуализации,
    • реализация проверки ВМ с помощью данных из Windows Management Instrumentation (WMI).
  • 2 уровень. Поиск статей и публикаций про детектирование запуска в виртуальных машинах:
    • допиливаем реализацию с WMI,
    • работа с инструкцией CPUID.
  • 3 уровень. Поиск материалов с хакерских конференций:
    • допиливаем работу с CPUID,
    • делаем сводную таблицу параметров и результатов тестирования.

1 уровень. Изучение матчасти и существующих решений

Немного теории касательно виртуализации

Прежде, чем пытаться писать детектор сферического коня в вакууме виртуальной машины, следует кратко обозначить, как в рамках задачи мы понимаем термин «виртуальная машина».

Понятие виртуализации можно поделить на две категории (1):

  • Первая — виртуализация ресурсов. Рассмотрим на живом примере, почему принцип работы сервиса хранения файлов Dropbox можно трактовать как виртуализацию ресурсов:
    1. Мы точно знаем, как загружать/выгружать файлы, и как взаимодействовать с сервисом, но как это всё работает внутри мы с уверенностью сказать не можем => инкапсуляция.
    2. У каждого пользователя есть свой уникальный аккаунт, у каждого аккаунта установлена квота на размер сохраняемых файлов, для каждого аккаунта можно настраивать разрешения на доступ индивидуально (хотя по факту данные разных пользователей вполне могут храниться на одном и том же носителе информации) => разделение ресурсов.
    3. Скорее всего, под капотом Dropbox находится не один и не два компьютера, а как минимум пара сотен серверов, функционирующих и обрабатывающих команды от клиентов в рамках системы Dropbox как единое целое => кластеризация.
  • Вторая — виртуализация платформ — создание программных систем на основе существующих аппаратно-программных комплексов, зависящих или независящих от них (1).

Во второй категории сразу введём два термина: система, предоставляющая аппаратные ресурсы и ПО для виртуализации (хостовая система, host) и эмулируемая система (гостевая система, guest).

При этом в реальности в роли «гостевой системы» могут выступать:

  1. Абсолютно всё аппаратное и программное обеспечение эмулируемой системы — такой тип виртуализации называется полной эмуляцией или симуляцией. Примеры программ, обеспечивающих такой тип виртуализации: Bochs, QEMU.
  2. Всё программное обеспечение и только часть аппаратного (часть достаточная для обеспечения изоляции гостевой системы) — такой тип виртуализации назовём частичной эмуляцией или нативной виртуализацией. Примеры программ, обеспечивающих такой тип виртуализации: VMWare Workstation, VMWare ESXi, Microsoft Hyper-V, Oracle VirtualBox.
  3. Также существуют частичная виртуализация, паравиртуализация, виртуализация уровня операционной системы и виртуализация уровня приложений. Во всех трёх случаях физически ОС у нас одна, а гостевыми системами считаются либо отдельные процессы, либо отдельные группы процессов (например, user-mode процессы).

Итог сего экскурса: в рамках статьи и создания детектора виртуальной машины нас будут интересовать только нативная виртуализация платформ (то есть проверять мы будем только запуск в окружении Hyper-V, VirtualBox или других программ, использующих нативную виртуализацию). При этом далее термин «виртуальная машина» мы будем трактовать согласно определению с сайта VMWare: «это строго изолированный контейнер ПО, содержащий операционную систему и приложения» (2).

Реализация проверки ВМ с помощью данных из Windows Management Instrumentation (WMI)

После того, как цель (определение факта работы программы в окружении с частичной эмуляцией) была более-менее уточнена, найдём самые известные виртуальные машины этого типа (далее для краткости ВМ) и способы отличить запуск ОС на реальном железе от запуска в окружении этих ВМ.

Прочитав замечательно свёрстанные рекламные страницы разработчиков популярных программ виртуализации, в голове вырисовывается некая общая схема их работы (разумеется схема работы программ, а не разработчиков):

  • Есть хостовая ОС. Не ограничивая общности будем считать, что она одна.
  • На хостовой ОС установлена программа для обеспечения виртуализации (далее гипервизор).
  • Гипервизор предоставляет интерфейс для установки и последующей работы гостевой ОС.

Примечание

: имеют место случаи, когда хостовая ОС и гипервизор есть единое тонкое целое, что позволяет уменьшить расходование ресурсов компьютера по сравнению с использованием хостовой ОС и гипервизора по отдельности (примеры: VMWare ESXi или Windows Hyper-V Server).

Вот только на практике почти в каждом гипервизоре имеется возможность установить «гостевые дополнения» (guest additions) — специальный набор программ и драйверов, дающих гипервизору
расширенный контроль за функциями гостевой ОС (проверка, а не зависла ли гостевая ОС, динамическое изменение доступной ОС оперативной памяти, «общая» мышка для хостовой и гостевой ОС). Однако, как же реализуют такое действо, если, согласно рекламе, «ВМ — это строго изолированный контейнер ПО»?

Получается, что гостевые дополнения, устанавливаемые на гостевую ОС, каким-то строго определённым образом взаимодействуют напрямую с гипервизором, запущенным в хостовой ОС. То есть если программа определения ВМ сможет воспользоваться таким взаимодействием — она докажет, что ОС запущена на ВМ! Правда, по условиям задачи доказательство надо проводить из-под User-Mode без использования собственных драйверов…

Сразу вырисовываются следующие места для проверки:

  • Поиск определённого оборудования в системе, которого на физической машине просто не может быть.
  • Поиск аппаратных интерфейсов, которые реализованы только в ВМ (или наоборот, только на физической машине).

Собственно, если ввести в поисковой строке «detect hyper-v C#» или «detect vmware C#», примерно на это и натыкаешься, осталось только обобщить.

Наиболее полное описание различных критериев проверки было найдено в статье 2013 года в журнале Хакер (3),— возьмём статью за основу. А для получения соответствующих данных об оборудовании и процессах ОС воспользуемся механизмом Windows Management Instrumentation (WMI) — дословно «инструментарием управления Windows». В частности, через WMI можно несложно, быстро и без прав администратора получить большое количество информации об оборудовании, которое видит ОС.

Для получения данных через WMI нам понадобится построить запрос на языке WQL (WMI Query Language), который по сути является сильно упрощённым SQL. Например, если мы хотим получить через WMI имеющуюся в ОС информацию о процессорах, требуется выполнить следующий запрос:

SELECT * FROM Win32_Processor

Ответ на этот запрос — набор объектов типа Win32_Processor с заранее известными названиями полей (подробный список доступных полей и классов см. в 4). Разумеется, если нам не требуются все-все поля, вместо * можно перечислить через запятую только необходимые. В WQL-операторе SELECT, по аналогии с SQL, также поддерживается условие WHERE, позволяющее делать выборку только по объектам, значения в полях которых удовлетворяют указанным условиям.

Для «затравки» научимся получать следующие данные из WMI-объектов следующих типов (данные и ожидаемые в ВМ значения взяты из 3):

WMI-объект и его свойства Условие на WQL-запрос объектов Как использовать
Win32_Processor  
     Manufacturer   В случае VirtualBox равен ‘VBoxVBoxVBox’, в случае VMWare — ‘VMwareVMware’, в случае Parallels — ‘prl hyperv ‘.
Win32_BaseBoard  
     Manufacturer   В случае Hyper-V равен ‘Microsoft Corporation’ при том, что Microsoft материнские платы не выпускает (интересно, а что показывает этот параметр на планшетах Microsoft Surface?).
Win32_DiskDrive  
     PNPDeviceID   В случае VirtualBox содержит ‘VBOX_HARDDISK’, в случае VMWare содержит ‘VEN_VMWARE’.
Win32_NetworkAdapter  
     MACAddress PhysicalAdapter=1 Известно, что по трём старшим байтам MAC-адреса можно определить производителя — и производители виртуальных машин не исключение (то есть если адаптер с признаком PhysicalAdapter=1 но имеет MAC-адрес из пула VMWare — то с высокой вероятностью программа была запущена на ВМ).
Win32_Process  
     Name   При установке гостевых дополнений на ВМ в системе появляются дополнительные процессы с известными именами.

Реализуем получение данных об оборудовании через WMI в отдельном проекте в виде библиотеки TTC.Utils.Environment.

Структурируем проект следующим образом:

  1. Entities — объекты с данными (сущности), полученными от WMI.
  2. Services — сервисы; например, служба, инкапсулирующая взаимодействие с WMI-обёрткой .NET.
  3. Interfaces — интерфейсы; например, интерфейс сервиса работы с WMI.
  4. Queries — объекты, содержащие параметры запросов к WMI, с помощью которых извлекаются заданные типы сущностей.

Хочется, чтобы пользователь данной библиотеки мог написать примерно такой код:

var bios = wmiService.QueryFirst<WmiBios>(new WmiBiosQuery());
var processors = wmiService.QueryAll<WmiProcessor>(new WmiProcessorQuery());

и не волновался по поводу механизма взаимодействия с WMI, построения запроса или преобразования ответа в строго типизированный класс языка C#.

Что ж, реализовать такое на самом деле не очень сложно.

Сначала подключим к проекту ссылку на библиотеку System.Management (именно в ней находятся классы .NET для доступа к WMI). Далее опишем интерфейс сервиса IWmiService (реализация этого интерфейса будет извлекать данные и преобразовывать их в строго типизированные объекты):

Код IWmiService.cs

/// <summary>
/// Интерфейс сервиса получения данных Windows Management Instrumentation (WMI).
/// </summary>
public interface IWmiService
{
    /// <summary>
    /// Получение первой записи из указанного запроса к WMI.
    /// </summary>
    /// <typeparam name="TResult">Тип сущности, в которую выгружаются результаты запроса.</typeparam>
    /// <param name="wmiQuery">Объект, содержащий параметры WMI-запроса.</param>
    /// <returns>Сущность с результатами запроса.</returns>
    TResult QueryFirst<TResult>(WmiQueryBase wmiQuery)
        where TResult : class, new();

    /// <summary>
    /// Получение набора записей из указанного запроса к WMI.
    /// </summary>
    /// <typeparam name="TResult">Тип сущности, в которую выгружаются результаты запроса.</typeparam>
    /// <param name="wmiQuery">Объект, содержащий параметры WMI-запроса.</param>
    /// <returns>Коллекция сущностей с результатами запроса.</returns>
    IReadOnlyCollection<TResult> QueryAll<TResult>(WmiQueryBase wmiQuery)
        where TResult : class, new();
}

Теперь установим как будут выглядеть сущности в нашем проекте. Предположим для детектирования нам потребуются следующие поля из WMI-объектов типа Win32_BaseBoard:

Код WmiBaseBoard.cs — до

public class WmiBaseBoard
{
    public string Manufacturer { get; private set; }

    public string Product { get; private set; }

    public string SerialNumber { get; private set; }
}

В идеале надо писать DTO, чтобы с его помощью транслировать данные из результата WML-запроса в вышеуказанную сущность, но если постулировать, что свойства сущностей в проекте будут 1 к 1 соответствовать полям объектов из результатов WML-запроса, то делать DTO на каждую сущность значит писать достаточно много однообразного кода.

Воспользуемся главным свойством любого программиста (ленью) и вместо создания полноценной DTO просто отметим атрибутом каждое свойство следующим атрибутом, позволяющим связать свойство и поле результата WML-запроса:

Код WmiResultAttribute.cs

/// <summary>
/// Указание, какому свойству сущности соответствует поле объекта WMI.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class WmiResultAttribute : Attribute
{
    public WmiResultAttribute(string propertyName)
    {
        PropertyName = propertyName;
    }

    /// <summary>
    /// Имя поля в объекте WMI.
    /// </summary>
    public string PropertyName { get; }
}

Разметив свойства сущности указанными атрибутами, получим:

Код WmiBaseBoard.cs — после

public class WmiBaseBoard
{
    internal const string MANUFACTURER = "Manufacturer";
    internal const string PRODUCT = "Product";
    internal const string SERIAL_NUMBER = "SerialNumber";

    // ReSharper disable UnusedAutoPropertyAccessor.Local
    [WmiResult(MANUFACTURER)]
    public string Manufacturer { get; private set; }

    [WmiResult(PRODUCT)]
    public string Product { get; private set; }

    [WmiResult(SERIAL_NUMBER)]
    public string SerialNumber { get; private set; }
    // ReSharper restore UnusedAutoPropertyAccessor.Local
}

Осталось разобраться с объектом, который будет хранить запрос. Уверен, вы обратили внимание, что в предыдущем примере кода названия полей WQL-результатов запроса вынесены в internal-константы. Это было сделано специально чтобы не дублировать их в классе запроса. Кстати, получился интересный побочный эффект — с использованием такой модели вы не сможете прочесть из WMI данные поля некоторого WMI-объекта пока не укажете, в какое свойство какой сущности он должен извлекаться.

Код WmiQueryBase.cs

using System.Management;
/// <summary>
/// Базовый класс данных параметров запроса к WMI.
/// </summary>
public class WmiQueryBase
{
    private readonly SelectQuery _selectQuery;

    /// <summary>
    /// Конструктор запроса к WMI.
    /// </summary>
    /// <param name="className">Название таблицы, к которой производится запрос.</param>
    /// <param name="condition">Условие запроса.</param>
    /// <param name="selectedProperties">Результирующие столбцы запроса.</param>
    protected WmiQueryBase(string className, 
        string condition = null, string[] selectedProperties = null)
    {
        _selectQuery = new SelectQuery(className, condition, selectedProperties);
    }

    /// <summary>
    /// Объект со сформированным SELECT-запросом к WMI.
    /// </summary>
    internal SelectQuery SelectQuery
    {
        get { return _selectQuery; }
    }
}

Код WmiBaseBoardQuery.cs

using TTC.Utils.Environment.Entities;
public class WmiBaseBoardQuery : WmiQueryBase
{
    public WmiBiosQuery()
        : base("Win32_BaseBoard", null, new[]
        {
            WmiBios.MANUFACTURER,
            WmiBios.PRODUCT,
            WmiBios.SERIAL_NUMBER,
        })
    {
    }
}

При такой структуре классов *Query есть только одна неприятность: неудобно формировать параметры WHERE-части WML-запроса внутри класса. Приходится действовать по старинке и ручками формировать строку в зависимости от параметров:

Код WmiNetworkAdapterQuery.cs

using System.Text;
using TTC.Utils.Environment.Entities;
public class WmiNetworkAdapterQuery : WmiQueryBase
{
    private static readonly string[] COLUMN_NAMES =
        {
            WmiNetworkAdapter.GUID,
            WmiNetworkAdapter.MAC_ADDRESS,
            WmiNetworkAdapter.PNP_DEVICE_ID,
        };

    public WmiNetworkAdapterQuery(WmiNetworkAdapterType adapterType = WmiNetworkAdapterType.All)
        : base("Win32_NetworkAdapter", null, COLUMN_NAMES)
    {
        if (adapterType == WmiNetworkAdapterType.Physical)
            SelectQuery.Condition = "PhysicalAdapter=1";
        else if (adapterType == WmiNetworkAdapterType.Virtual)
            SelectQuery.Condition = "PhysicalAdapter=0";
    }
}

Хорошо: данные по сущностям раскидали, запросы писать с грехом-пополам научились, осталось только разобраться как будет выглядеть сервис, работающий с указанными классами:

Код WmiService.cs

/// <summary>
/// Сервис получения данных Windows Management Instrumentation (WMI).
/// </summary>
public class WmiService : IWmiService
{
    /// <summary>
    /// Извлечение заданных в запросе столбцов из полученных записей WMI с приведением типов.
    /// </summary>
    /// <typeparam name="TResult">Тип сущности, в которую выгружаются результаты запроса.</typeparam>
    /// <param name="managementObject">Объект, полученный в результате запроса WMI.</param>
    /// <returns>Сущность с результатами запроса.</returns>
    private static TResult Extract<TResult>(ManagementBaseObject managementObject)
        where TResult : class, new()
    {
        var result = new TResult();
        foreach (var property in typeof(TResult).GetProperties())
        {
            var wmiAttribute = (WmiResultAttribute)Attribute.GetCustomAttribute(property, typeof(WmiResultAttribute));
            if (wmiAttribute != null)
            {
                var sourceValue = managementObject.Properties[wmiAttribute.PropertyName].Value;
                var targetType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
                object targetValue;
                if (sourceValue == null)
                {
                    targetValue = null;
                }
                else if (targetType == typeof(DateTime))
                {
                    targetValue = ManagementDateTimeConverter.ToDateTime(sourceValue.ToString()).ToUniversalTime();
                }
                else if (targetType == typeof(Guid))
                {
                    targetValue = Guid.Parse(sourceValue.ToString());
                }
                else
                {
                    targetValue = Convert.ChangeType(
                        managementObject.Properties[wmiAttribute.PropertyName].Value, targetType);
                }
                property.SetValue(result, targetValue);
            }
        }
        return result;
    }

    /// <summary>
    /// Получение набора данных из указанного запроса к WMI.
    /// </summary>
    /// <param name="selectQuery">Запрос для получения данных.</param>
    /// <param name="searcher">Существующий объект для выполнения запросов к WMI.</param>
    /// <returns>Результирующая коллекция объектов в таблице.</returns>
    private ManagementObjectCollection QueryAll(SelectQuery selectQuery, ManagementObjectSearcher searcher = null)
    {
        searcher = searcher ?? new ManagementObjectSearcher();
        searcher.Query = selectQuery;
        return searcher.Get();
    }

    /// <summary>
    /// Получение первой строки данных из указанного запроса к WMI.
    /// </summary>
    /// <param name="selectQuery">Запрос для получения данных.</param>
    /// <param name="searcher">Существующий объект для выполнения запросов к WMI.</param>
    /// <returns>Результирующая коллекция объектов в таблице.</returns>
    private ManagementBaseObject QueryFirst(SelectQuery selectQuery, ManagementObjectSearcher searcher = null)
    {
        return QueryAll(selectQuery, searcher).Cast<ManagementBaseObject>().FirstOrDefault();
    }

    public TResult QueryFirst<TResult>(WmiQueryBase wmiQuery)
        where TResult : class, new()
    {
        var managementObject = QueryFirst(wmiQuery.SelectQuery);
        return managementObject == null ? null : Extract<TResult>(managementObject);
    }

    public IReadOnlyCollection<TResult> QueryAll<TResult>(WmiQueryBase wmiQuery)
        where TResult : class, new()
    {
        var managementObjects = QueryAll(wmiQuery.SelectQuery);
        return managementObjects?.Cast<ManagementBaseObject>()
            .Select(Extract<TResult>)
            .ToList();
    }
}

Пара слов касательно метода WmiService.Extract<TResult>.

У объектов WMI обычно достаточно большое количество свойств (причем многие поля могут иметь значение NULL). В предположении, что в рамках задачи выгружать из WMI мы будем только небольшое количество свойств объектов, логично начать маппинг данных с перебора свойств результирующей сущности. Далее, при наличии у свойства атрибута WmiResultAttribute мы считываем из объекта результата запроса значение свойства с указанным в атрибуте именем и выполняем преобразование типов. При этом, если свойство сущности имеет тип, с которым стандартный метод Convert.ChangeType не справится или преобразует тип не так, как нам хочется, мы легко можем передать управление на своё преобразование (как это сделано для типов System.DateTime и System.Guid).

Кстати, было бы ещё лучше разделить Extract на два метода: первый извлекает информацию из типа класса, второй заполняет экземпляры (иначе метод QueryAll для второго и последующих элементов выходной коллекции делает ненужную работу по повторному изучению структуры его типа). Но конкретно для целей детектирования виртуалки мы вряд ли будет ожидать более 10 объектов за один запрос, поэтому предлагаю списать эту задачу с пометкой «не реализовано, ибо природная лень». Но если у кого-то дойдут руки до такой модификации — с радостью приму вашу доработку.

Послесловие

Чтобы не заканчивать эту часть статьи только библиотекой, сделаем самое простое приложение, использующее возможности данной библиотеки для детектирования нескольких самых популярных виртуальных машин фирм VMWare, Microsoft, Parallels и Oracle на осове вышеизложенных критериев.

Создадим отдельный проект — консольное приложение TTC.Utils.VMDetect и создадим в нём такой класс DemoTrivialVmDetector:

Код WmiService.cs

/// <summary>
/// Тестовый класс для проверки запуска из-под ВМ.
/// </summary>
class DemoTrivialVmDetector
{
    private readonly IWmiService _wmiService;

    public DemoTrivialVmDetector(IWmiService wmiService)
    {
        _wmiService = wmiService;
    }

    public MachineType GetMachineType()
    {
        var wmiProcessor = _wmiService.QueryFirst<WmiProcessor>(new WmiProcessorQuery());
        if (wmiProcessor.Manufacturer != null)
        {
            if (wmiProcessor.Manufacturer.Contains("VBoxVBoxVBox"))
                return MachineType.VirtualBox;
            if (wmiProcessor.Manufacturer.Contains("VMwareVMware"))
                return MachineType.VMWare;
            if (wmiProcessor.Manufacturer.Contains("prl hyperv"))
                return MachineType.Parallels;
        }

        var wmiBaseBoard = _wmiService.QueryFirst<WmiBaseBoard>(new WmiBaseBoardQuery());
        if (wmiBaseBoard.Manufacturer != null)
        {
            if (wmiBaseBoard.Manufacturer.Contains("Microsoft Corporation"))
                return MachineType.HyperV;
        }

        var wmiDiskDrives = _wmiService.QueryAll<WmiDiskDrive>(new WmiDiskDriveQuery());
        if (wmiDiskDrives != null)
            foreach (var wmiDiskDrive in wmiDiskDrives)
            {
                if (wmiDiskDrive.PnpDeviceId.Contains("VBOX_HARDDISK"))
                    return MachineType.VirtualBox;
                if (wmiDiskDrive.PnpDeviceId.Contains("VEN_VMWARE"))
                    return MachineType.VMWare;
            }

        return MachineType.Unknown;
    }
}

Весь код, включая библиотеку и простейшее тестовое приложение, выложен в репозитории на github, отзывы и комментарии приветствуются.

В следующей части мы чутка структурируем работу с известными ВМ и с помощью ассемблерной инструкции CPUID попробуем детектировать уже неизвестные ВМ.

Источники

  1. Виртуализация: новый подход к построению IT-инфраструктуры
  2. Виртуализация при помощи VMWare
  3. Детектим виртуалки (xakep.ru)
  4. WMI: Win32 Provider (MSDN)

  • Как узнать версию apache windows
  • Как узнать в каком режиме установлен windows uefi или legacy
  • Как узнать версию windows на установочном диске
  • Как узнать все пароли на компьютере windows 10
  • Как узнать видеокарту своего компьютера на windows xp