Как узнать виртуальная машина или нет windows
Как определить, что вы работаете под виртуальной машиной?
есть ли способ определить, изнутри виртуальной машины, что ваш код работает внутри виртуальной машины?
Я думаю, что есть более или менее простые способы определить конкретные системы VM, особенно если VM имеет установленные расширения поставщика (например, для VirtualBox или VMWare). Но есть ли общий способ определить, что вы не работаете непосредственно на CPU?
12 ответов
многие исследования по этому вопросу посвящены обнаружению так называемых атак» синих таблеток», то есть вредоносного гипервизора, который активно пытается избежать обнаружения.
классический трюк для обнаружения виртуальной машины-заполнить ITLB, запустить инструкцию, которая должны быть виртуализированным (что обязательно очищает такое состояние процессора, когда он дает контроль гипервизору), затем запустите еще один код, чтобы определить, заполнен ли ITLB. Первая статья на нем местонахождение здесь, и довольно красочное объяснение от научный сотрудник в блог и альтернатива Wayback Machine ссылка на статью в блоге (изображения сломаны).
суть обсуждений в том, что всегда есть способ обнаружить вредоносный гипервизор, и гораздо проще обнаружить тот, который не пытается скрыть.
использование стороннего поддерживаемого инструмента такова, что это лучшая долгосрочная стратегия, чем попытка свернуть собственную логику обнаружения: больше глаз (тестирование против большего количества продуктов виртуализации) и т. д.
более эмпирический подход заключается в проверке известных драйверов устройств VM. Вы можете написать запросы WMI, чтобы найти, скажем, адаптер дисплея VMware, дисковод, сетевой адаптер и т. д. Это было бы удобно, если бы вы знали, что вам нужно беспокоиться только об известных типах узлов VM в вашей среде. Вот!—1—>пример выполнения этого в Perl, который может быть перенесен на язык по вашему выбору.
Это зависит от того, что вы после:
Если виртуальная машина не скрывается от вас специально, вы можете использовать некоторый известный крюк. Например, поиск драйверов VmWare или наличие определенных строк в памяти или некоторых других контрольных знаков.
Если VM действительно хочет, чтобы вы делали для него специальные вещи, у него будет какой-то очевидный крючок, например, изменение идентификатора процессора или добавление некоторых специальных регистров, к которым вы можете получить доступ обнаружить его. Или специальное устройство в известном месте в памяти (предполагая, что вы можете получить доступ к физическому пространству памяти вашего мира). Обратите внимание, что современные конструкции машин, такие как IBM Power6 и Sun UltraSparc T1/T2, предназначены для работы гипервизора всегда и никогда непосредственно на необработанном оборудовании. Интерфейс к «оборудованию», которое использует ОС, на самом деле является интерфейсом программного уровня гипервизора, без возможности обойти его. В этом случае обнаружение тривиально, так как это постоянное «да». Это вероятное будущее направление для всех компьютерных систем, которые могут позволить себе накладные расходы, посмотрите на поддержку в последних проектах, таких как Чип Freescale QorIQ P4080, например (www.freescale.com/qoriq).
Если виртуальная машина намеренно пытается скрыть, и вы преследуете ее присутствие, это игра в кошки-мышки, где нарушение синхронизации и различный профиль производительности виртуальной машины почти всегда будет выдавать его. Очевидно, это зависит от того, как ВМ реализовано и сколько аппаратной поддержки существует в архитектуре (я думаю, что мэйнфрейм zSeries намного лучше скрывает наличие виртуальной машины или стека виртуальных машин под вашей конкретной ОС, чем обычная машина x86, например). См.http://jakob.engbloms.se/archives/97 для обсуждения этой темы. Можно попытаться скрыть как виртуальную машину, но обнаружение, скорее всего, всегда выиграет, если оно достаточно сильно попытается.
Я однажды наткнулся на фрагмент кода сборки, который сказал вам, если вы были в виртуальной машине. Я погуглил но не смог найти оригинал статьи.
надеюсь, что это помогает.
в большинстве случаев, вы не должны пытаться. Вы не должны беспокоиться, если кто-то запускает ваш код в виртуальной машине, за исключением нескольких конкретных случаев.
один хороший пример-это, по-видимому, выполнение запроса WMI для производителя материнской платы, и если он возвращает «Microsoft», вы находитесь в виртуальной машине. Я думал, что это только для VMWare. Вероятно, есть разные способы сказать для каждого программного обеспечения хоста VM.
эта статья здесь http://blogs.technet.com/jhoward/archive/2005/07/26/407958.aspx есть несколько хороших предложений и ссылок на несколько способов определить, находитесь ли вы в виртуальной машине (VMWare и VirtualPC, по крайней мере).
вы можете определить, находитесь ли вы в виртуальной машине, посмотрев на MAC-адрес вашего сетевого подключения. Xen, например, обычно рекомендует использовать определенный диапазон адресов 00:16:3e:xx:xx: xx.
Это не гарантируется, поскольку администратор системы может указать, какой MAC-адрес им нравится.
в системах Linux вы можете попытаться найти общие файлы в /proc.
пример, existente /proc/ vz / tell you является OpenVZ.
здесь (java + windows) решение для определения того, является ли базовая машина физической или виртуальной.
Примеры Виртуальных Машин:
производитель
модель
Виртуальная Платформа VMware
вы можете использовать выходные данные, чтобы решить, является ли это виртуальной машиной или физической машиной:
физический выход машины:
серия BIOS: 2HC3J12
Аппаратная Модель: Inspiron 7570
Производитель Оборудования: Dell Inc.
виртуальная машина вывод:
серия BIOS: 0
Аппаратная Модель: Innotec GmBH
Производитель Оборудования: Virtual Box
Если это VM делает работу хорошо, она должна быть невидимой для клиента, что она виртуализируется. Однако можно взглянуть и на другие улики.
Я бы предположил, что поиск известных драйверов или программного обеспечения, специфичного для среды VM, был бы лучшим способом.
Детектим виртуалки
Определяем факт запуска приложения в 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). Короче, выглядит все это приблизительно так:
Заключение
Как видишь, признаков, характерных для виртуальных машин, предостаточно, и для того, чтобы их увидеть, сильно глубоко копать совсем не нужно.
Как определить, что вы работаете под виртуальной машиной?
Существует ли способ определить из виртуальной машины, что ваш код выполняется внутри виртуальной машины?
Я полагаю, что существуют более или менее простые способы идентификации конкретных систем виртуальных машин, особенно если на виртуальной машине установлены расширения поставщика (например, для 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 для некоторого обсуждения этой темы. Можно попытаться спрятаться как виртуальная машина, но обнаружение, скорее всего, всегда выиграет, если оно будет достаточно сильно стараться.
Однажды я наткнулся на фрагмент кода ассемблера, который говорил вам, находитесь ли вы в виртуальной машине. Я погуглил, но не смог найти оригинальную статью.
Как определить виртуальную машину
Есть ли способ узнать, является ли машина 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 попробуем детектировать уже неизвестные ВМ.