Определяем факт запуска приложения в VirtualBox, VMware Workstation, Virtual PC и Parallels Workstation

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

Как распознать виртуальную машину?

Во-первых, любая виртуальная машина несет на своем борту какое-нибудь специфическое оборудование. Это касается видеоадаптера, жесткого диска, идентификатора процессора, версии BIOS, MAC-адреса сетевой карты.

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

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

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

Что же касается общих признаков наличия виртуальной машины, предложенных в свое время госпожой Рутковской (характерное расположение таблиц IDT, GDT и LDT, а также время выполнения операций процессором), то в настоящий момент все эти признаки трудно поддаются анализу и приведению к какому-нибудь общему знаменателю, главным образом из-за многоядерности и многоликости современных процессоров.

Анализируем оборудование

Начнем, пожалуй, с жесткого диска. Если посмотреть идентификатор жесткого диска в диспетчере устройств на виртуальной машине, то в его составе можно увидеть интересные строчки:

DiskVirtual для VirtualPC DiskVBOX_HARDDISK для Virtual Box Prod_VMware_Virtual для VMware Workstation

Самый простой способ узнать наименование жесткого диска — прочитать значение ключа с именем «0» в ветке реестра HKLMHARDWARESYSTEMCurrentControlSetServicesDiskEnum.

В этом месте перечисляются все дисковые накопители в системе, и первым, как раз в ключе с именем «0», будет тот диск, с которого произошла загрузка системы.

Идентификатор жесткого диска VirtualBox в реестре
Идентификатор жесткого диска VirtualBox в реестре

Как читать реестр, я думаю, ты знаешь. Используем сначала API RegOpenKeyEx для открытия нужного ключа, далее с помощью RegQueryValueEx читаем значение. Выглядеть это должно примерно вот так:

... // Открывем нужный ключ реестра RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\SYSTEM\ CurrentControlSet\ Services\Disk\Enum", 0, KEY_QUERY_VALUE, &rKey); // Читаем значение RegQueryValueExA(rKey, "0", NULL, &Type, (LPBYTE)RegKey, &RegPath); // Закрываем все, что открыли ранее RegCloseKey(rKey); ...

Далее все просто — используем strstr для поиска нужных нам строк в считанном значении и, в зависимости от результата сравнения, делаем вывод. Версия BIOS содержится в ключе «SystemProductName» в ветке HKLMHARDWAREDESCRIPTIONSystemBIOS. К примеру, для VMware там будет лежать строка «VMware Virtual Platform», а для VirtualBox — «VBOX –1».

Прочитать это все можно с помощью все тех же API — RegOpenKeyEx и RegQueryValueEx.

Версия BIOS Parallels Workstation в реестре
Версия 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» соответственно.

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

... if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\CurrentControlSet\ Enum\PCI\VEN_5333&DEV_8811& SUBSYS_00000000&REV_00", 0, KEY_QUERY_VALUE, &rKey) == ERROR_SUCCESS) { RegCloseKey(rKey); // Мы под VirtualPC return true; } ...

Идентификатор процессора определяется с помощью команды cpuid. Благодаря ей можно получить много всякой полезной информации об установленном процессоре. Вид выдаваемой этой командой информации зависит от содержимого регистра EAX. Результат работы команды записывается в регистры EBX, ECX и EDX. Подробно про эту команду можно почитать в любой книге по программированию на ассемблере. Для наших целей мы будем использовать эту инструкцию, предварительно положив в регистр EAX значение 0x40000000:

... _asm { mov eax, 0x40000000 cpuid mov ID_1, ebx mov ID_2, ecx mov ID_3, edx } ...

После выполнения этого кода на 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-адреса сетевой карты определяют ее производителя. Производители виртуальных машин в этом плане не исключение:

VMware (VMware Workstation) 00:05:69 00:0c:29 00:1c:14 00:50:56 Microsoft (Virtual PC) 00:03:ff 00:0d:3a 00:50:f2 7c:1e:52 00:12:5a 00:15:5d 00:17:fa 28:18:78 7c:ed:8d 00:1d:d8 00:22:48 00:25:ae 60:45:bd Dc:b4:c4 Oracle (VirtualBox) 08:00:20 Parallels (Parallels Workstation) 00:1c:42

Вытащить эти первые три байта из MAC-адреса нам поможет API-функция GetAdaptersInfo:

// Подключаем либу, в которой // содержится нужная нам функция #include <iphlpapi.h> #pragma comment(lib, "IPHLPAPI.lib") ... // Определяем размер буфера под данные, // возвращаемые функцией GetAdaptersInfo(AdapterInfo, &OutBufLen); // Выделяем память под данные об адаптере AdapterInfo = (PIP_ADAPTER_INFO) new(char[OutBufLen]); // Получаем информацию об адаптере GetAdaptersInfo(AdapterInfo, &OutBufLen); // Сравниваем первые три байта MAC-адреса // с 00:1c:42 (Parallels Workstation) if (((BYTE)AdapterInfo->Address[0] == 0x00) && ((BYTE)AdapterInfo->Address[1] == 0x1c) && ((BYTE)AdapterInfo->Address[2] == 0x42)) { delete(AdapterInfo); // Мы под Parallels Workstation return true; } else { delete(AdapterInfo); return false; } ...

Вспомогательные процессы, окна и другие «подозрительные» объекты

Для нормальной работы практически все виртуальные машины требуют установки дополнений к гостевой операционной системе, например VBoxGuestAddition для VirtualBox или Parallels Tools для Parallels Workstation. Без этих дополнений работа с виртуальной машиной несколько затруднительна (ни тебе нормального разрешения экрана и полноэкранного режима, ни взаимодействия с USB-девайсами, ни нормальной настройки сетевых подключений). В общем, все производители виртуалок не рекомендуют использовать их без этих дополнений. А эти самые дополнения оставляют очень заметный след в виде запущенных процессов:

VirtualBox VBoxTray.exe VBoxService.exe Parallels Workstation prl_cc.exe prl_tools.exe SharedIntApp.exe Virtual PC vmusrvc.exe vmsrvc.exe VMware Workstation vmtoolsd.exe

Для поиска процесса по имени мы воспользуемся функциями CreateToolhelp32Snapshot, Process32First и Process32Next:

#include <Tlhelp32.h> ... ... // К примеру, ищем процесс vmtoolsd.exe wchar_t VMwareProcessName[] = {L"vmtoolsd.exe"}; PROCESSENTRY32 pe; HANDLE hSnapShot; hSnapShot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0); ZeroMemory (&pe, sizeof(PROCESSENTRY32W)); pe.dwSize = sizeof(PROCESSENTRY32W);  Process32First(hSnapShot, &pe); do { if (memcmp(pe.szExeFile, VMwareProcessName, 24) == 0) // Мы под VMware return true; } while (Process32Next(hSnapShot, &pe)); ...

Помимо непосредственно самих процессов, демаскирующим признаком могут стать окна, открытые этими процессами. Окон в каждой из рассматриваемых виртуальных машин может быть довольно много, и все их мы перечислять не будем, а ограничимся одним или двумя. Итак:

VirtualBox VBoxTrayToolWndClass Parallels Workstation CPInterceptor DesktopUtilites Virtual PC {0843FD01-1D28-44a3-B11D-E3A93A85EA96} VMware Workstation VMSwitchUserControlClass
Открытые окна для VMware (красным выделено окно класса VMSwitchUserControlClass)
Открытые окна для VMware (красным выделено окно класса VMSwitchUserControlClass)

Найти окно по имени класса очень просто — для этого есть функция FindWindow:

... // К примеру, ищем окно для VMware HWND VMwareWindow = FindWindowA("VMSwitchUserControlClass", NULL); if(VMwareWindow != NULL) // Мы под VMware Workstation return true; ...

Помимо процессов и окон, указывающих на наличие ВМ, можно найти и другие «подозрительные» объекты — например, если покопаться в гостевой ОС виртуальной машины утилитой WinObj или какой-нибудь аналогичной, то можно найти вот такие объекты:

VirtualBox DeviceVBoxMiniRdrDN DeviceVBoxGuest Parallels Workstation Deviceprl_pv Deviceprl_tg Deviceprl_time DevicePrlMemDev DevicePrlMemDevPci DevicePrlMemDev Virtual PC DeviceVirtualMachineServices
«Подозрительные» объекты в VirtualBox
«Подозрительные» объекты в VirtualBox

Проверить наличие «подозрительного» объекта очень просто, достаточно попытаться открыть его с помощью CreateFile:

... // К примеру, проверяем VirtualBox if ((CreateFile(L"\\.\VBoxMiniRdrDN", 0,0,0,OPEN_EXISTING,0,0) !=INVALID_HANDLE_VALUE)|| (CreateFile(L"\\.\VBoxGuest", 0,0,0,OPEN_EXISTING,0,0) !=INVALID_HANDLE_VALUE)) // Мы под VirtualBox return true; ...

Что еще «подозрительного» можно найти в реестре?

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

VirtualBox VBOX__ Parallels Workstation PRLS__ Virtual PC AMIBI VMware Workstation PTLTD__

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

Ключ PRLS__ в реестре Parallels Workstation
Ключ PRLS__ в реестре Parallels Workstation

Возможности, заложенные производителем

Некоторые производители (в частности, VMware и Microsoft) специально реализуют возможности управления своими продуктами, которые можно использовать для наших целей.

В Virtual PC используются инвалидные (не «инвалидные», а «альтернативно одаренные». И вообще-то они «недействительные». — Прим. ред.) команды процессора с опкодами 0x0F, 0x3F, 0x07 и 0x0B, попытка выполнения которых на реальном процессоре вызовет исключение, в то время как на Virtual PC все пройдет нормально. С помощью этих команд можно достаточно просто задетектить виртуалку от Microsoft:

... __try { __asm { xor ebx, ebx mov eax, 1 __emit(0x0F) __emit(0x3F) __emit(0x07) __emit(0x0B) } // Мы под Virtual PC return true; } __except(EXCEPTION_EXECUTE_HANDLER)  return false; ...

В VMware Workstation для взаимодействия гостевой и основной ОС реализован небольшой бэкдор в виде порта с номером 0x5658. Для его использования необходимо в EAX положить «магическое» число 0x564d5868 (в символьном представлении — «VMXh»), а в ECX записать одну из команд взаимодействия гостевой и основной ОС (например, команда 0x0A возвращает версию установленной VMware Workstation). Короче, выглядит все это приблизительно так:

... __try { __asm { mov eax, 0x564d5868 mov ecx, 0x0A mov edx, 0x5658 in eax, dx  } // Мы под VMware return true; } __except(EXCEPTION_EXECUTE_HANDLER)  return false; ...

Заключение

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

WWW

www.symantec.com/avcenter/reference/Virtual_Machine_Threats.pdf — очень хорошая статья про детект виртуалок. Единственный ее недостаток — она на английском языке.

1 комментарий

  1. 03.07.2014 at 17:43

Оставить мнение

Check Also

Конкурс хаков: пишем на PowerShell скрипт, который уведомляет о днях рождения пользователей Active Directory

В компаниях часто встречается задача уведомлять сотрудников о приближающихся днях рождения…