Следующая новость
Предыдущая новость

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

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

Содержание статьи

  • Способы определения гипервизора
  • Определение через cpuid
  • Прочие способы
  • Работа с машинным кодом и данными
  • Ассемблерные вставки
  • Двоичная арифметика
  • Вызов функций из библиотек на C
  • Чтение файлов
  • Условная компиляция и макросы
  • Сборка проекта
  • Заключение

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

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

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

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

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

INFO

Один из моих проектов — VyOS — дистрибутив GNU/Linux для маршрутизаторов. Над ним работала команда специалистов, в которой я выступал координатором.

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

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

INFO

Название производителя процессора передается таким же способом. Именно поэтому используются строки вроде GenuineIntel и AuthenticAMD — не чтобы убедить пользователя, что процессор не поддельный, а чтобы строка укладывалась в три 32-разрядных регистра без дополнения нулями.

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

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

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

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

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

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

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

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

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

Продолжение доступно только подписчикам

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

Подпишись на «Хакер» по выгодной цене!

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

1 год

7190 р.

Экономия 1400 рублей!

1 месяц

720 р.

25-30 статей в месяц

Уже подписан?

Источник

Последние новости