Pull to refresh

Байткод

Reading time4 min
Views24K
Original author: Jasper St. Pierre
Размышляя о софтварном аде и прочих неурядицах с современными операционными системами я наткнулся на интересную статью о байткоде и о том в каких неожиданных местах он может использоваться. Мне кажется эта статья перекликается с вышеупомянутыми, поэтому решил сделать перевод и выложить здесь. Это мой первый перевод на хабре, так что не судите строго. Если есть какие-то замечания по переводу, ошибкам и т.п. просьба писать в личку.

Какой байткод является наиболее используемым в мире? Java (JVM bytecode)? .NET (CLI)? Flash (AVM1\AVM2)? Нет. Есть несколько байткодов которые вы используете каждый день, просто включив компьютер, или планшет, или даже телефон. Вам даже не нужно запускать приложения или посещать какую-нибудь страницу в сети Интернет.



ACPI

Пожалуй наиболее частым в использовании является байткод ACPI. Спецификация ACPI («Advanced Configuration and Power Interface» — «Усовершенствованный интерфейс управления конфигурацией и питанием») представляет собой гигантский документ объемом почти в 1000 страниц. И да, подразумевается, что операционные системы дожны следовать спецификации и реализовывать ее. Целиком и полностью. Часть, касающуяся байткода, можно найти в главе 20 «ACPI Machine Language», где описывается регистровая виртуальная машина с типичным набором команда типа Add, Substract, Multiply, Divide, операциями сравнения и такими замечательными командами как ToHexString или Mid (substring по сути). Читая дальше вы встретите полную объектную модель, спецификацию системных свойств, а также механизм асинхронных уведомлений, срабатывающий при изменении какого-либо из системных свойств.

Большинство устройств конечно должно следовать спецификации ACPI полностью, так что спецификация полностью реализована на уровне ядер операционных систем, вот например реализация в Linux. Весь этот код выполняется на начальном этапе загрузки операционной системы. По сложности реализации и выполнения все это соизмеримо с полной реализацией JavaScript и его окружения. Из-за того, что спецификация ACPI очень сложная, корпорация Intel создала платформенно-независимую реализацию спецификации — ACPICA и именно эта реализация используется в ядрах Linux и BSD (включая Mac OS), а так же в таких системах как ReactOS и HaikuOS. Я не знаю использует ли Windows эту реализацию, но т.к. название компании Microsoft содержится в спецификации, то я думаю, что их реализация была создана задолго до ACPICA.

Шрифты

Продолжим, хотите графический загрузчик? Для простого отображения шрифта в формате OpenType (только OpenType шрифтов с глифами CFF, сложность формата OpenType шрифтов — это отдельная тема для разговора) необходимо распарсивать данные в формате Type 2 Glyph, что так же включает в себя выполнение специализированного байткода для построения глифов. Этот байткод даже более интересный — это настояший стековый интерепретатор и у него есть даже команда «random», позволяющая строить глифы отображающиеся случайным образом во время исполнения. Я не могу представить себе хоть какое-либо полезное применение для этой возможности (это также было реализовано и в FreeType шрифтах), так что я могу только предположить, что это где-то реально используется. Интерпретатор этого байткода известен своей уязвимостью переполнения стека, которая позволяла сделать джейлбрек iPhone специально сформированным PDF файлом.

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

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

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

Фильтрация пакетов

Далее, если у вас есть желание перехватить какой-нибудь сетевой пакет при помощи tcpdump или libpcap (или при помощи утилит основанных на них, например Wireshark) то будет использоватся Berkeley Packet Filter в основе которого регистровый байткод. Производительность была очень критична для людей которые занимались отладкой сетей, поэтому так же был реализован простой JIT компилятор на уровне ядра Linux.

Для интересующихся историей — более ранняя реализация BPF была в составе кода, который учавствовал в судебных исках SCO против Linux хотя вообще BPF был частью кода из BSD4.3 и был просто скопирован в ядро Linux. Позднее BPF был заменен более новой реализацией известной как Linux Socket Filter.

Ну так к чему все это?

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

Четыре рассмотренных примера также показывают и кое что интересное — чрезвычайно различные ситуации в которых может использоваться байткод. В случае с ACPI это интересный взгляд на то, что мне представляется уродливой реализацией изначально декларативной спецификации, которая находится сейчас в полном беспорядке. Шрифтовые языки Type 2 Glyph и TrueType Hinting базовые стековые интерпретаторы, показывающие наследие PostScript. И BPF — регистровый интерепретатор, с довольно странным регистровым языком, который позволяет выполнять только довольно простые операции.

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

От переводчика
Интересные ссылки из комментариев к оригинальной статье:
байткод UEFI см. раздел 20
байткод Sqlite
байткод архиватора RAR
Tags:
Hubs:
Total votes 66: ↑61 and ↓5+56
Comments18

Articles