На что способна ада. Делаем утилиту для детекта гипервизора на полузабытом языке

Лучше всего познавать язык на реальном проекте, поэтому, когда я решил поэкспериментировать с адой, я поставил себе реальную и интересную задачу: написать утилиту для детекта работы в гипервизоре. Это само по себе занятно, а новый язык программирования позволит вывести развлечение на новый уровень.

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

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

Про сборку программ на аде было рассказано в моей предыдущей статье, но если пропустил — не страшно, ничего сложного в этом нет. Нужно поставить GNAT — он входит в состав GCC и всегда есть в репозиториях, — сохранить код в файл something.adb и выполнить gnatmake something.adb.

Желательно, чтобы на месте something в имени файла было имя основной процедуры, иначе компилятор выдаст предупреждение. Исполняемый файл gnatmake автоматически назовет по имени файла с кодом, а не a.out.

По ряду причин я решил написать утилиты для определения гипервизора, в котором работает виртуальная машина. В VyOS мы включаем эту информацию в вывод команды show version. Для получения самой информации исторически использовалась самописная утилита на довольно грязном C, которая не поддерживала некоторые менее популярные гипервизоры, и у меня давно было желание ее чем-нибудь заменить.

Существующие решения, такие как virt-what, вызывают у меня смешанные чувства. Смесь C и скриптовых языков, на мой взгляд, выглядит неэстетично. Эстетика — вещь субъективная, но есть и объективные проблемы, например поддержка только GNU/Linux и отказ работать без прав суперпользователя.

Мне хотелось, чтобы замена старому коду принесла пользу не только мне и пользователям VyOS, поэтому я поставил следующие требования:

  • поддержка как минимум GNU/Linux и FreeBSD;
  • возможность работы с правами обычного пользователя;
  • по крайней мере техническая возможность работы на разных архитектурах;
  • простая и доступная незнакомому с адой пользователю процедура сборки.

Задача мне показалась вполне подходящей для тестирования нового языка. В случае провала я всегда мог бы переписать код на Rust или взять одну из существующих утилит. Эксперимент завершился, на мой взгляд, успешно, результат был назван hvinfo и уже давно используется в VyOS. Исходный код можно найти по адресу github.com/dmbaturin.

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

 

Способы определения гипервизора

Как, собственно, определить, работает ли система в виртуальной машине, и если да, то на каком гипервизоре? На платформе x86 все системы виртуализации с этой точки зрения можно поделить на две группы: одни поддерживают общий стандарт de facto — передачу информации через вызов инструкции cpuid, другие не поддерживают.

 

Определение через cpuid

К первой группе относятся Xen в режиме аппаратной виртуализации, KVM, bhyve, VMware и Hyper-V. Я не уверен, кто из них ввел этот механизм первым, но работает он у всех одинаково.

Инструкция cpuid была впервые реализована компанией Intel и с тех пор присутствует во всех процессорах x86. Стоит отметить, что для совместимости она использует 32-разрядные регистры даже в 64-разрядном режиме. Вид возвращаемой информации зависит от значения в регистре eax.

Гипервизоры из первой группы перехватывают вызовы cpuid и обладают дополнительными возможностями. Для передачи информации о самом факте работы ОС в виртуальной машине применяется разряд 31-го регистра ecx. На физических машинах он всегда установлен на ноль согласно документации Intel, а гипервизоры устанавливают его на единицу.

Получить название гипервизора можно, вызвав cpuid со значением 0x40000000 в регистре eax. Название передается в виде строки длиной до двенадцати символов в регистрах ebx, ecx и edx. К примеру, Xen использует строку XenVMMXenVMM, а VMware — VMwareVMware.

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

 

Прочие способы

Некоторые гипервизоры не используют сложившийся интерфейс cpuid, несмотря на полную виртуализацию, например VirtualBox в режиме двоичной трансляции. Паравиртуальный Xen просто не может его использовать.

В этих случаях приходится применять другие способы, такие как проверка названия производителя из SMBIOS или наличия специфичных устройств PCI, вроде видеокарты innotek Gmbh в VirtualBox.

Эти способы, в отличие от cpuid, не так универсальны, и на разных ОС их придется реализовать по-разному. Чтобы это сделать, нам придется использовать интерфейс с libc и работу с файлами.

 

Работа с машинным кодом и данными

 

Ассемблерные вставки

Ада проектировалась как язык системного программирования, а какое системное программирование совсем без машинного кода? Все возможности для этого присутствуют.

Прежде всего нам потребуются беззнаковые целочисленные типы. Пакет Interfaces предоставляет все распространенные типы, включая нужный нам для работы с 32-разрядными регистрами Unsigned_32.

Источник: xakep.ru

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *