Как определить что машина виртуальная
Как определить, что вы работаете под виртуальной машиной?
Существует ли способ определить из виртуальной машины, что ваш код выполняется внутри виртуальной машины?
Я полагаю, что существуют более или менее простые способы идентификации конкретных систем виртуальных машин, особенно если на виртуальной машине установлены расширения поставщика (например, для VirtualBox или VMWare). Но есть ли общий способ определить, что вы не работаете непосредственно на процессоре?
12 ответов:
Большая часть исследований по этому вопросу посвящена обнаружению так называемых атак «голубых таблеток», то есть вредоносного гипервизора, который активно пытается уклониться от обнаружения.
Суть обсуждения этого вопроса заключается в том, что всегда есть способ обнаружить вредоносный гипервизор, и гораздо проще обнаружить тот, который не пытается скрыть.
Использование стороннего поддерживаемого инструмента, такого как это-лучшая долгосрочная стратегия, чем попытка свернуть свою собственную логику обнаружения: больше глаз (тестирование против большего количества продуктов виртуализации) и т. д.
Более эмпирический подход заключается в проверке известных драйверов устройств виртуальной машины. Вы можете написать запросы WMI, чтобы найти, скажем, адаптер дисплея VMware, дисковод, сетевой адаптер и т. д. Это было бы удобно, если бы вы знали, что вам нужно беспокоиться только об известных типах узлов виртуальных машин в вашей среде. Вот пример выполнения этого в Perl, который может быть перенесен на язык по вашему выбору.
Это зависит от того, что вы ищете:
Если виртуальная машина не скрывается от вас специально, вы можете использовать какой-нибудь известный крючок. Например, поиск драйверов VmWare или наличие определенных строк в памяти или некоторых других контрольных знаков.
Если виртуальная машина действительно хочет, чтобы вы делали для нее специальные вещи, у нее будет какой-то очевидный крюк, например, изменение идентификатора процессора или добавление некоторых специальных регистров, к которым вы можете получить доступ, чтобы обнаружить его. Или особый устройство в известном месте в памяти (предполагая, что вы можете получить необработанный доступ к физическому пространству памяти вашего мира). Обратите внимание, что современные машины, такие как IBM Power6 и Sun UltraSparc T1/T2, всегда работают с гипервизором и никогда не работают непосредственно на необработанном оборудовании. Интерфейс к «аппаратному обеспечению», которое использует ОС, на самом деле является интерфейсом программного уровня гипервизора, без возможности обойти его. В этом случае обнаружение тривиально, так как это постоянное «да». Это наиболее вероятно будущее направление для всех компьютерных систем, которые могут позволить себе накладные расходы, посмотрите на поддержку в последних проектах, таких как Freescale qoriq p4080 chip, например (www.freescale.com/qoriq).
Если виртуальная машина намеренно пытается скрыться, а вы преследуете ее присутствие, это игра в кошки-мышки, где нарушение синхронизации и различный профиль производительности виртуальной машины почти всегда будут выдавать его. Очевидно, это зависит от того, как реализована виртуальная машина и сколько аппаратная поддержка существует в архитектуре (я думаю, что мэйнфрейм zSeries намного лучше скрывает присутствие виртуальной машины или стека виртуальных машин под вашей конкретной ОС, чем обычная x86-машина, например). См. http://jakob.engbloms.se/archives/97 для некоторого обсуждения этой темы. Можно попытаться спрятаться как виртуальная машина, но обнаружение, скорее всего, всегда выиграет, если оно будет достаточно сильно стараться.
Однажды я наткнулся на фрагмент кода ассемблера, который говорил вам, находитесь ли вы в виртуальной машине. Я погуглил, но не смог найти оригинальную статью.
Детектим виртуалки
Определяем факт запуска приложения в VirtualBox, VMware Workstation, Virtual PC и Parallels Workstation
Не каждому хочется, чтобы его, кхм, новый текстовый редактор какие-нибудь неприятные дядьки исследовали под виртуальной машиной. Детект виртуалок — обязательный функционал определенного рода софта, и поэтому наша рубрика ну совершенно никак не может обойтись без обзора соответствующих способов!
Как распознать виртуальную машину?
Во-первых, любая виртуальная машина несет на своем борту какое-нибудь специфическое оборудование. Это касается видеоадаптера, жесткого диска, идентификатора процессора, версии BIOS, MAC-адреса сетевой карты.
Во-вторых, виртуальные машины оставляют следы в системе в виде запущенных вспомогательных процессов, драйверов и других специфических объектов.
В-третьих, если как следует покопаться в реестре виртуальной машины, там можно найти много всяких интересных ключей, характерных только для виртуальных машин.
Ну и в-четвертых, некоторые производители специально оставляют возможности, позволяющие обнаружить их продукты.
Что же касается общих признаков наличия виртуальной машины, предложенных в свое время госпожой Рутковской (характерное расположение таблиц IDT, GDT и LDT, а также время выполнения операций процессором), то в настоящий момент все эти признаки трудно поддаются анализу и приведению к какому-нибудь общему знаменателю, главным образом из-за многоядерности и многоликости современных процессоров.
Анализируем оборудование
Начнем, пожалуй, с жесткого диска. Если посмотреть идентификатор жесткого диска в диспетчере устройств на виртуальной машине, то в его составе можно увидеть интересные строчки:
Самый простой способ узнать наименование жесткого диска — прочитать значение ключа с именем «0» в ветке реестра HKLMHARDWARESYSTEMCurrentControlSetServicesDiskEnum.
В этом месте перечисляются все дисковые накопители в системе, и первым, как раз в ключе с именем «0», будет тот диск, с которого произошла загрузка системы.
Идентификатор жесткого диска VirtualBox в реестре
Хакер #174. Собираем квадрокоптер
Как читать реестр, я думаю, ты знаешь. Используем сначала API RegOpenKeyEx для открытия нужного ключа, далее с помощью RegQueryValueEx читаем значение. Выглядеть это должно примерно вот так:
Далее все просто — используем strstr для поиска нужных нам строк в считанном значении и, в зависимости от результата сравнения, делаем вывод. Версия BIOS содержится в ключе «SystemProductName» в ветке HKLMHARDWAREDESCRIPTIONSystemBIOS. К примеру, для VMware там будет лежать строка «VMware Virtual Platform», а для VirtualBox — «VBOX –1».
Прочитать это все можно с помощью все тех же API — RegOpenKeyEx и RegQueryValueEx.
Версия BIOS Parallels Workstation в реестре
Данные о видеоадаптере можно подглядеть в HKLMSystemCarrentControlSetEnumPCI. В этой ветке перечислено все, что подключено к шине PCI, в том числе и видеокарта. Для VirtualPC это строчка вида VEN_5333&DEV_8811&SUBSYS_00000000&REV_00, которая определяет видеоадаптер S3 Trio 32/64, эмулируемый виртуалкой от Microsoft — на реальном железе такое оборудование нынче днем с огнем не сыскать (а у меня такая была в конце прошлого века. — Прим. ред.). Для VirtualBox видеокарта описана последовательностью VEN_80EE&DEV_BEEF&SUBSYS_00000000&REV_00, что расшифровывается как «VirtualBox Display», а у Parallels Workstation — строка VEN_1AB8&DEV_4005&SUBSYS_04001AB8&REV_00 определяет видеоадаптер «Parallels Display».
Помимо этого, в VirtualBox можно найти строку VEN_80EE&DEV_CAFE&SUBSYS_00000000&REV_00, определяющую некий «VirtualBox Device», а у Parallels Workstation строки VEN_1AB8&DEV_4000&SUBSYS_04001AB8&REV_00 и VEN_1AB8&DEV_4006&SUBSYS_04061AB8&REV_00, определяющие «Parallels Tools Device» и «Parallels Memory Controller» соответственно.
Алгоритм действий следующий: пытаемся открыть нужный нам ключ, и если он открывается успешно, то оборудование, описанное этим ключом, в наличии и можно делать вывод о присутствии какой-либо виртуальной машины:
Идентификатор процессора определяется с помощью команды cpuid. Благодаря ей можно получить много всякой полезной информации об установленном процессоре. Вид выдаваемой этой командой информации зависит от содержимого регистра EAX. Результат работы команды записывается в регистры EBX, ECX и EDX. Подробно про эту команду можно почитать в любой книге по программированию на ассемблере. Для наших целей мы будем использовать эту инструкцию, предварительно положив в регистр EAX значение 0x40000000:
После выполнения этого кода на VMware Workstation в переменных ID_1, ID_2 и ID_3 будут записаны значения 0x61774d56, 0x4d566572 и 0x65726177 соответственно (в символьном представлении это не что иное, как «VMwareVMware»), на VirtualBox в ID_1 и в ID_2 будет лежать значение 0x00000340, а на Parallels Workstation в ID_1 0x70726c20, в ID_2 — 0x68797065 и в ID_3 — 0x72762020 (что соответствует строке «prl hyperv»).
Использование MAC-адреса для идентификации производителя сетевой карты, конечно, не самый надежный способ (ибо MAC-адрес довольно-таки просто поменять), но тем не менее его вполне можно применить для детекта виртуальных машин в качестве дополнительной проверки.
Ты наверняка знаешь, что первые три байта MAC-адреса сетевой карты определяют ее производителя. Производители виртуальных машин в этом плане не исключение:
Вытащить эти первые три байта из MAC-адреса нам поможет API-функция GetAdaptersInfo:
Вспомогательные процессы, окна и другие «подозрительные» объекты
Для нормальной работы практически все виртуальные машины требуют установки дополнений к гостевой операционной системе, например VBoxGuestAddition для VirtualBox или Parallels Tools для Parallels Workstation. Без этих дополнений работа с виртуальной машиной несколько затруднительна (ни тебе нормального разрешения экрана и полноэкранного режима, ни взаимодействия с USB-девайсами, ни нормальной настройки сетевых подключений). В общем, все производители виртуалок не рекомендуют использовать их без этих дополнений. А эти самые дополнения оставляют очень заметный след в виде запущенных процессов:
Для поиска процесса по имени мы воспользуемся функциями CreateToolhelp32Snapshot, Process32First и Process32Next:
Помимо непосредственно самих процессов, демаскирующим признаком могут стать окна, открытые этими процессами. Окон в каждой из рассматриваемых виртуальных машин может быть довольно много, и все их мы перечислять не будем, а ограничимся одним или двумя. Итак:
Открытые окна для VMware (красным выделено окно класса VMSwitchUserControlClass)
Найти окно по имени класса очень просто — для этого есть функция FindWindow:
Помимо процессов и окон, указывающих на наличие ВМ, можно найти и другие «подозрительные» объекты — например, если покопаться в гостевой ОС виртуальной машины утилитой WinObj или какой-нибудь аналогичной, то можно найти вот такие объекты:
«Подозрительные» объекты в VirtualBox
Проверить наличие «подозрительного» объекта очень просто, достаточно попытаться открыть его с помощью CreateFile:
Что еще «подозрительного» можно найти в реестре?
Помимо признаков наличия специфического оборудования, в реестре можно увидеть и другие следы, оставляемые виртуальными машинами. Некоторые из них базируются в ветке HKLMHARDWAREACPIDSDT. Достаточно в этом месте проверить наличие таких вот ключей:
Проверку реализуем так же, как мы проверяли наличие определенного оборудования. Просто делаем попытку открыть нужный нам ключ и, в случае успеха, делаем вывод о наличии ВМ.
Ключ PRLS__ в реестре Parallels Workstation
Возможности, заложенные производителем
Некоторые производители (в частности, VMware и Microsoft) специально реализуют возможности управления своими продуктами, которые можно использовать для наших целей.
В Virtual PC используются инвалидные (не «инвалидные», а «альтернативно одаренные». И вообще-то они «недействительные». — Прим. ред.) команды процессора с опкодами 0x0F, 0x3F, 0x07 и 0x0B, попытка выполнения которых на реальном процессоре вызовет исключение, в то время как на Virtual PC все пройдет нормально. С помощью этих команд можно достаточно просто задетектить виртуалку от Microsoft:
В VMware Workstation для взаимодействия гостевой и основной ОС реализован небольшой бэкдор в виде порта с номером 0x5658. Для его использования необходимо в EAX положить «магическое» число 0x564d5868 (в символьном представлении — «VMXh»), а в ECX записать одну из команд взаимодействия гостевой и основной ОС (например, команда 0x0A возвращает версию установленной VMware Workstation). Короче, выглядит все это приблизительно так:
Заключение
Как видишь, признаков, характерных для виртуальных машин, предостаточно, и для того, чтобы их увидеть, сильно глубоко копать совсем не нужно.
☁️ Как проверить, является ли система Linux физической или виртуальной машиной
Как определить является ли система Linux физической или виртуальной машиной
Существует множество способов определить, является ли система физической или виртуальной.
В настоящее время мне известны следующие методы.
Я сообщу, если найду другие способы в ближайшие дни.
Метод 1 – Использование утилиты Dmidecode
Самый простой способ узнать, работаем ли мы на виртуальной или физической машине, – это использовать утилиту dmidecode.
Dmidecode, декодер таблиц DMI, используется для поиска аппаратных компонентов вашей системы, а также другой полезной информации, такой как серийные номера и версия BIOS.
Dmidecode предустановлен в большинстве дистрибутивов Linux.
На всякий случай, если он еще не установлен, вы можете установить его с помощью диспетчера пакетов вашего дистрибутива.
Скажем, например, следующая команда установит dmidecode в системах на основе DEB, таких как Ubuntu, Linux Mint.
После установки Dmidecode выполните следующую команду, чтобы узнать, является ли ваша система физической или виртуальной машиной:
Если это физическая система, вы получите результат, подобный приведенному ниже.
Если это виртуальная система, созданная с помощью Virtualbox, вы получите следующий результат:
Для тех, кому интересно, innotek – это немецкая компания-разработчик программного обеспечения, которая разрабатывает программное обеспечение для виртуализации ПК под названием VirtualBox.
Если это виртуальная система, созданная с помощью KVM / QEMU, вывод будет:
Как видно из вышеприведенного вывода, если это физическая система, dmidecode покажет имя производителя (например, Dell Inc.).
Если это виртуальная система, то она покажет программное обеспечение / технологию виртуализации (например, VirtualBox или QEMU).
Кроме того, вы можете использовать эту команду, чтобы проверить, является ли это физической или виртуальной системой.
Еще одна команда, чтобы узнать, является ли это физической или виртуальной системой:
Еще одна команда dmidecode для определения типа удаленной системы:
И еще одна команда dmidecode предназначена для достижения той же цели:
Метод 2 – Использование утилиты Facter
Facter – это утилита командной строки для сбора и отображения информации о системе.
В отличие от Dmidecode, Facter по умолчанию не предустановлен.
Возможно, вам потребуется установить его, как показано ниже, в зависимости от используемого вами дистрибутива Linux.
После установки facter выполните следующую команду, чтобы проверить, является ли система физической или виртуальной машиной:
Если эта команда не работает, попробуйте с привилегиями sudo:
Как вариант, используйте следующую команду:
Метод 3 – Использование утилиты lshw
Утилита lshw – это небольшая утилита командной строки, которая отображает подробную информацию об оборудовании Unix-подобной системы.
Она отображает все детали оборудования, включая конфигурацию памяти, версию прошивки, конфигурацию материнской платы, версию и скорость процессора, конфигурацию кеша, скорость шины и т. д.
В некоторых дистрибутивах Linux предустановлен lshw.
Если он еще не установлен, вы можете установить его, как показано ниже.
В Arch Linux и производных:
В RHEL и производных, таких как CentOS, scientific Linux:
На Debian, Ubuntu, Linux Mint:
После установки lshw выполните следующую команду, чтобы узнать, является ли ваша система физической или виртуальной:
Метод 4 – Использование утилиты dmesg
Узнать тип системы можно с помощью утилиты dmesg.
dmesg используется для проверки кольцевого буфера ядра или управления им.
Чтобы проверить, является ли ваша система Linux физической или виртуальной, просто запустите:
Если ваша система физическая, вы не увидите никаких выходных данных.
Если ваша система – виртуальная машина, вы увидите результат, подобный приведенному ниже.
Метод 5 – Использование команды hostnamectl
Мы можем узнать, является ли наша система виртуальной или физической, используя команду hostnamectl.
Для работы требуется systemd.
Метод 6 – Использование systemd-detect-virt
Инструмент systemd-detect-virt обнаруживает технологию виртуализации и может отличить полную виртуализацию машины от аппаратной или контейнерной виртуализации.
Выполните следующую команду, чтобы проверить, является ли система физической или виртуальной:
Метод 7 – Использование скрипта virt-what
Virt-what – это небольшой скрит оболочки, разработанный Red Hat, чтобы определить, работаем ли мы на виртуальной или физической машине.
virt-what собран для всех популярных дистрибутивов Linux, таких как RHEL, Fedora, CentOS, Debian, Ubuntu, Arch Linux (AUR).
В Arch Linux вы можете установить его из AUR с помощью любых помощников AUR, например Yay.
В RHEL, Fedora, CentOS:
Если ничего не выводится и скрит завершается с кодом 0 (без ошибок), это означает, что либо система является физической, либо является типом виртуальной машины, о которой мы не знаем или не можем обнаружить.
Если ваша система виртуальная, вы увидите результат, как показано ниже.
Метод 8 – Использование скрипта imvirt
Imvirt – это еще один небольшой скрипт Perl, который помогает вам определить, работает ли мы на виртуальной машине.
В Arch Linux вы можете установить его из AUR с помощью вспомогательной программы Yay.
Как определить виртуальную машину
Есть ли способ узнать, является ли машина Windows, над которой я работаю, виртуальной или физической? (Я подключаюсь к RDP к машине. Если это виртуальная машина, она работает и обрабатывается VMWare).
14 ответов
Если это Windows, просто взгляните на экраны оборудования. Он будет иметь миллиард и пять виртуальных устройств с поддержкой VMWare.
Вы найдете строку со следующим текстом (или подобным):
Если он обрабатывается VMware, в настоящий момент это не слишком сложно. Это может измениться в будущем.
возвращает что-то вроде:
В Linux запустите это:
Это, вероятно, машины real :
Это, с другой стороны, почти наверняка является машиной virtual :
Ответ получен, но FWIW вы можете сделать это в powershell:
«Производитель» будет «Microsoft Corporation», а «Модель» будет «Виртуальной машиной», если это виртуальная машина, или она должна отображать регулярные данные производителя, если нет, например. «Dell Inc.» и «PowerEdge R210 II» соответственно.
Один (относительно) простой способ обнаружения ключевой информации о виртуализации через WMI /WBEM. Вы можете использовать пространство имен root CIM2 и получить доступ к классу Baseboard (полный интересной информации о BIOS), чтобы получить описание «физического» system. Этот класс часто включает информацию о материнской плате и шасси — производстве, модели, серийном номере и т. д.
Выполните следующую команду из командной строки или сеанса PowerShell:
Еще проще — wmic /node: bios get serialnumber
Все, что возвращает серийный номер в стиле Dell, является физическим.
Он также вернет «VMware-42 22 26 a8 dd 6e e3 b3-2e 03 fc 2c 92 ae 2e 89», если это виртуальная машина.
У меня был тот же вопрос, и выяснилось, что в имени есть много процессов, работающих с «VM», например VMWareTray.exe
Определяем факт запуска приложения в VirtualBox, VMware Workstation, Virtual PC и Parallels Workstation
Не каждому хочется, чтобы его, кхм, новый текстовый редактор какие-нибудь неприятные дядьки исследовали под виртуальной машиной. Детект виртуалок — обязательный функционал определенного рода софта, и поэтому наша рубрика ну совершенно никак не может обойтись без обзора соответствующих способов!
Во-первых, любая виртуальная машина несет на своем борту какое-нибудь специфическое оборудование. Это касается видеоадаптера, жесткого диска, идентификатора процессора, версии BIOS, MAC-адреса сетевой карты.
Во-вторых, виртуальные машины оставляют следы в системе в виде запущенных вспомогательных процессов, драйверов и других специфических объектов.
В-третьих, если как следует покопаться в реестре виртуальной машины, там можно найти много всяких интересных ключей, характерных только для виртуальных машин.
Ну и в-четвертых, некоторые производители специально оставляют возможности, позволяющие обнаружить их продукты.
Что же касается общих признаков наличия виртуальной машины, предложенных в свое время госпожой Рутковской (характерное расположение таблиц IDT, GDT и LDT, а также время выполнения операций процессором), то в настоящий момент все эти признаки трудно поддаются анализу и приведению к какому-нибудь общему знаменателю, главным образом из-за многоядерности и многоликости современных процессоров.
Начнем, пожалуй, с жесткого диска. Если посмотреть идентификатор жесткого диска в диспетчере устройств на виртуальной машине, то в его составе можно увидеть интересные строчки:
Самый простой способ узнать наименование жесткого диска — прочитать значение ключа с именем «0» в ветке реестра HKLMHARDWARESYSTEMCurrentControlSetServicesDiskEnum.
В этом месте перечисляются все дисковые накопители в системе, и первым, как раз в ключе с именем «0», будет тот диск, с которого произошла загрузка системы.
Идентификатор жесткого диска VirtualBox в реестре
Хакер #174. Собираем квадрокоптер
Как читать реестр, я думаю, ты знаешь. Используем сначала API RegOpenKeyEx для открытия нужного ключа, далее с помощью RegQueryValueEx читаем значение. Выглядеть это должно примерно вот так:
Далее все просто — используем strstr для поиска нужных нам строк в считанном значении и, в зависимости от результата сравнения, делаем вывод. Версия BIOS содержится в ключе «SystemProductName» в ветке HKLMHARDWAREDESCRIPTIONSystemBIOS. К примеру, для VMware там будет лежать строка «VMware Virtual Platform», а для VirtualBox — «VBOX –1».
Прочитать это все можно с помощью все тех же API — RegOpenKeyEx и RegQueryValueEx.
Версия BIOS Parallels Workstation в реестре
Данные о видеоадаптере можно подглядеть в HKLMSystemCarrentControlSetEnumPCI. В этой ветке перечислено все, что подключено к шине PCI, в том числе и видеокарта. Для VirtualPC это строчка вида VEN_5333&DEV_8811&SUBSYS_00000000&REV_00, которая определяет видеоадаптер S3 Trio 32/64, эмулируемый виртуалкой от Microsoft — на реальном железе такое оборудование нынче днем с огнем не сыскать (а у меня такая была в конце прошлого века. — Прим. ред.). Для VirtualBox видеокарта описана последовательностью VEN_80EE&DEV_BEEF&SUBSYS_00000000&REV_00, что расшифровывается как «VirtualBox Display», а у Parallels Workstation — строка VEN_1AB8&DEV_4005&SUBSYS_04001AB8&REV_00 определяет видеоадаптер «Parallels Display».
Помимо этого, в VirtualBox можно найти строку VEN_80EE&DEV_CAFE&SUBSYS_00000000&REV_00, определяющую некий «VirtualBox Device», а у Parallels Workstation строки VEN_1AB8&DEV_4000&SUBSYS_04001AB8&REV_00 и VEN_1AB8&DEV_4006&SUBSYS_04061AB8&REV_00, определяющие «Parallels Tools Device» и «Parallels Memory Controller» соответственно.
Алгоритм действий следующий: пытаемся открыть нужный нам ключ, и если он открывается успешно, то оборудование, описанное этим ключом, в наличии и можно делать вывод о присутствии какой-либо виртуальной машины:
Идентификатор процессора определяется с помощью команды cpuid. Благодаря ей можно получить много всякой полезной информации об установленном процессоре. Вид выдаваемой этой командой информации зависит от содержимого регистра EAX. Результат работы команды записывается в регистры EBX, ECX и EDX. Подробно про эту команду можно почитать в любой книге по программированию на ассемблере. Для наших целей мы будем использовать эту инструкцию, предварительно положив в регистр EAX значение 0x40000000:
После выполнения этого кода на VMware Workstation в переменных ID_1, ID_2 и ID_3 будут записаны значения 0x61774d56, 0x4d566572 и 0x65726177 соответственно (в символьном представлении это не что иное, как «VMwareVMware»), на VirtualBox в ID_1 и в ID_2 будет лежать значение 0x00000340, а на Parallels Workstation в ID_1 0x70726c20, в ID_2 — 0x68797065 и в ID_3 — 0x72762020 (что соответствует строке «prl hyperv»).
Использование MAC-адреса для идентификации производителя сетевой карты, конечно, не самый надежный способ (ибо MAC-адрес довольно-таки просто поменять), но тем не менее его вполне можно применить для детекта виртуальных машин в качестве дополнительной проверки.
Ты наверняка знаешь, что первые три байта MAC-адреса сетевой карты определяют ее производителя. Производители виртуальных машин в этом плане не исключение:
Вытащить эти первые три байта из MAC-адреса нам поможет API-функция GetAdaptersInfo:
Для нормальной работы практически все виртуальные машины требуют установки дополнений к гостевой операционной системе, например VBoxGuestAddition для VirtualBox или Parallels Tools для Parallels Workstation. Без этих дополнений работа с виртуальной машиной несколько затруднительна (ни тебе нормального разрешения экрана и полноэкранного режима, ни взаимодействия с USB-девайсами, ни нормальной настройки сетевых подключений). В общем, все производители виртуалок не рекомендуют использовать их без этих дополнений. А эти самые дополнения оставляют очень заметный след в виде запущенных процессов:
Для поиска процесса по имени мы воспользуемся функциями CreateToolhelp32Snapshot, Process32First и Process32Next:
Помимо непосредственно самих процессов, демаскирующим признаком могут стать окна, открытые этими процессами. Окон в каждой из рассматриваемых виртуальных машин может быть довольно много, и все их мы перечислять не будем, а ограничимся одним или двумя. Итак:
Открытые окна для VMware (красным выделено окно класса VMSwitchUserControlClass)
Найти окно по имени класса очень просто — для этого есть функция FindWindow:
Помимо процессов и окон, указывающих на наличие ВМ, можно найти и другие «подозрительные» объекты — например, если покопаться в гостевой ОС виртуальной машины утилитой WinObj или какой-нибудь аналогичной, то можно найти вот такие объекты:
«Подозрительные» объекты в VirtualBox
Проверить наличие «подозрительного» объекта очень просто, достаточно попытаться открыть его с помощью CreateFile:
Помимо признаков наличия специфического оборудования, в реестре можно увидеть и другие следы, оставляемые виртуальными машинами. Некоторые из них базируются в ветке HKLMHARDWAREACPIDSDT. Достаточно в этом месте проверить наличие таких вот ключей:
Проверку реализуем так же, как мы проверяли наличие определенного оборудования. Просто делаем попытку открыть нужный нам ключ и, в случае успеха, делаем вывод о наличии ВМ.
Ключ PRLS__ в реестре Parallels Workstation
Некоторые производители (в частности, VMware и Microsoft) специально реализуют возможности управления своими продуктами, которые можно использовать для наших целей.
В Virtual PC используются инвалидные (не «инвалидные», а «альтернативно одаренные». И вообще-то они «недействительные». — Прим. ред.) команды процессора с опкодами 0x0F, 0x3F, 0x07 и 0x0B, попытка выполнения которых на реальном процессоре вызовет исключение, в то время как на Virtual PC все пройдет нормально. С помощью этих команд можно достаточно просто задетектить виртуалку от Microsoft:
В VMware Workstation для взаимодействия гостевой и основной ОС реализован небольшой бэкдор в виде порта с номером 0x5658. Для его использования необходимо в EAX положить «магическое» число 0x564d5868 (в символьном представлении — «VMXh»), а в ECX записать одну из команд взаимодействия гостевой и основной ОС (например, команда 0x0A возвращает версию установленной VMware Workstation). Короче, выглядит все это приблизительно так:
Как видишь, признаков, характерных для виртуальных машин, предостаточно, и для того, чтобы их увидеть, сильно глубоко копать совсем не нужно.
Одним жуть каким прохладным январским утром от знакомого прилетел вопрос как на C# определить, не запущена ли программа в ОС (оконное приложение в ОС Windows 7 или новее) на виртуальной машине.
Требования к такому детектору были достаточно жёсткими:
Под катом описание реализованного детектора на C# (в следующей части с некоторыми элементами C++) и приличным количеством неприличного кода с использованием Visual Studio 2015 Community.
Структура публикации
- 1 уровень. Изучение матчасти и простейших существующих решений:
2 уровень. Поиск статей и публикаций про детектирование запуска в виртуальных машинах:
3 уровень. Поиск материалов с хакерских конференций:
1 уровень. Изучение матчасти и существующих решений
Немного теории касательно виртуализации
Прежде, чем пытаться писать детектор сферического коня в вакууме виртуальной машины, следует кратко обозначить, как в рамках задачи мы понимаем термин «виртуальная машина».
Понятие виртуализации можно поделить на две категории (1):
- Первая виртуализация ресурсов. Рассмотрим на живом примере, почему принцип работы сервиса хранения файлов Dropbox можно трактовать как виртуализацию ресурсов:
Во второй категории сразу введём два термина: система, предоставляющая аппаратные ресурсы и ПО для виртуализации (хостовая система, host) и эмулируемая система (гостевая система, guest).
При этом в реальности в роли «гостевой системы» могут выступать:
- Абсолютно всё аппаратное и программное обеспечение эмулируемой системы такой тип виртуализации называется полной эмуляцией или симуляцией. Примеры программ, обеспечивающих такой тип виртуализации: Bochs, QEMU.
Всё программное обеспечение и только часть аппаратного (часть достаточная для обеспечения изоляции гостевой системы) такой тип виртуализации назовём частичной эмуляцией или нативной виртуализацией. Примеры программ, обеспечивающих такой тип виртуализации: VMWare Workstation, VMWare ESXi, Microsoft Hyper-V, Oracle VirtualBox.
Итог сего экскурса: в рамках статьи и создания детектора виртуальной машины нас будут интересовать только нативная виртуализация платформ (то есть проверять мы будем только запуск в окружении 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 имеющуюся в ОС информацию о процессорах, требуется выполнить следующий запрос:
Ответ на этот запрос набор объектов типа 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.
Структурируем проект следующим образом:
Хочется, чтобы пользователь данной библиотеки мог написать примерно такой код:
и не волновался по поводу механизма взаимодействия с WMI, построения запроса или преобразования ответа в строго типизированный класс языка C#.
Что ж, реализовать такое на самом деле не очень сложно.
Теперь установим как будут выглядеть сущности в нашем проекте. Предположим для детектирования нам потребуются следующие поля из WMI-объектов типа Win32_BaseBoard:
Воспользуемся главным свойством любого программиста (ленью) и вместо создания полноценной DTO просто отметим атрибутом каждое свойство следующим атрибутом, позволяющим связать свойство и поле результата WML-запроса:
Разметив свойства сущности указанными атрибутами, получим:
Осталось разобраться с объектом, который будет хранить запрос. Уверен, вы обратили внимание, что в предыдущем примере кода названия полей WQL-результатов запроса вынесены в internal-константы. Это было сделано специально чтобы не дублировать их в классе запроса. Кстати, получился интересный побочный эффект с использованием такой модели вы не сможете прочесть из WMI данные поля некоторого WMI-объекта пока не укажете, в какое свойство какой сущности он должен извлекаться.
При такой структуре классов *Query есть только одна неприятность: неудобно формировать параметры WHERE-части WML-запроса внутри класса. Приходится действовать по старинке и ручками формировать строку в зависимости от параметров:
Хорошо: данные по сущностям раскидали, запросы писать с грехом-пополам научились, осталось только разобраться как будет выглядеть сервис, работающий с указанными классами:
Пара слов касательно метода WmiService.Extract
У объектов WMI обычно достаточно большое количество свойств (причем многие поля могут иметь значение NULL). В предположении, что в рамках задачи выгружать из WMI мы будем только небольшое количество свойств объектов, логично начать маппинг данных с перебора свойств результирующей сущности. Далее, при наличии у свойства атрибута WmiResultAttribute мы считываем из объекта результата запроса значение свойства с указанным в атрибуте именем и выполняем преобразование типов. При этом, если свойство сущности имеет тип, с которым стандартный метод Convert.ChangeType не справится или преобразует тип не так, как нам хочется, мы легко можем передать управление на своё преобразование (как это сделано для типов System.DateTime и System.Guid).
Кстати, было бы ещё лучше разделить Extract на два метода: первый извлекает информацию из типа класса, второй заполняет экземпляры (иначе метод QueryAll для второго и последующих элементов выходной коллекции делает ненужную работу по повторному изучению структуры его типа). Но конкретно для целей детектирования виртуалки мы вряд ли будет ожидать более 10 объектов за один запрос, поэтому предлагаю списать эту задачу с пометкой «не реализовано, ибо природная лень». Но если у кого-то дойдут руки до такой модификации с радостью приму вашу доработку.
Послесловие
Чтобы не заканчивать эту часть статьи только библиотекой, сделаем самое простое приложение, использующее возможности данной библиотеки для детектирования нескольких самых популярных виртуальных машин фирм VMWare, Microsoft, Parallels и Oracle на осове вышеизложенных критериев.
Создадим отдельный проект консольное приложение TTC.Utils.VMDetect и создадим в нём такой класс DemoTrivialVmDetector:
Весь код, включая библиотеку и простейшее тестовое приложение, выложен в репозитории на github, отзывы и комментарии приветствуются.
В следующей части мы чутка структурируем работу с известными ВМ и с помощью ассемблерной инструкции CPUID попробуем детектировать уже неизвестные ВМ.