Свежий tmp.0ut Volume 5 выглядит примерно так, будто группе людей дали ELF, ассемблер, ядро Linux и команду «посмотрим, насколько далеко можно зайти». В оглавлении 21 позиция, включая вступление и музыкальный микс. Между ними 19 содержательных материалов про экстремально маленькие ELF, метаморфный код, необычное управление потоком, полиглоты, загрузчики, защиту программ, руткиты, Java-байткод и перестановку функций в памяти.
Я бы не называл номер просто журналом про вредоносный код. Самые интересные статьи исследуют границу между спецификацией и реальным поведением системы. Что ядро Linux действительно проверяет при execve, сколько структуры ELF можно выбросить, можно ли обнаружить вмешательство в системные вызовы без доверия самому ядру, насколько далеко реально переместить функции при загрузке программы. Именно за такие эксперименты tmp.0ut и стоит читать.
Дисклеймер. В выпуске есть материалы о вирусах, руткитах, шелл-коде и модификации ядра. Разбирать такие техники имеет смысл для исследований, обучения и защиты собственных систем. Не применяйте описанные подходы для несанкционированного доступа, скрытого наблюдения, взлома чужой инфраструктуры или нарушения правил сервисов. Учитывайте законодательство своей страны, включая требования российского законодательства.
Пять материалов, с которых я бы начал
Первое место я бы отдал Inside and Outside a 57-Byte x86-64 Linux ELF. Автор методично разбирает загрузчик ELF в Linux и выясняет, насколько мало данных нужно ядру, чтобы признать файл исполняемым. Получается 57-байтовый ELF64, который проходит execve и становится процессом. Правда, такой минимальный вариант сразу падает, потому что в нём почти нечего исполнять.
Дальше начинается самая красивая часть эксперимента. Автор увеличивает файл до 60 байт и выносит машинный код вообще за пределы самого ELF, используя имя запускаемого файла, которое Linux кладёт в пользовательский стек. Получается почти абсурдная конструкция, где имя файла фактически становится хранилищем инструкций. Практическим бинарником назвать конструкцию нельзя. Для демонстрации приходится учитывать исполняемый стек и бороться с ASLR, причём автор прямо признаёт, что элегантного решения для рандомизации адреса стека не нашёл.
Материал хорошо дополняет наш разбор о том, как неожиданные проверки загрузчика влияют на реальные ELF-файлы. Например, ранее на SecurityLab я разбирал случай, когда старая проверка Linux десятилетиями ограничивала загрузку ELF на некоторых конфигурациях.
Второй материал, который я бы точно не пропускал, Detecting syscall hooks with side-channels. PinkNoize предлагает обнаруживать перехват системных вызовов по изменениям в кэше инструкций процессора. Идея особенно интересна из-за модели угроз. Если руткит уже работает в ядре, спрашивать ядро, всё ли с ним хорошо, довольно бессмысленно. Автор пытается получить сигнал снизу, от аппаратного поведения процессора.
Эксперимент использует особенности L1-кэша инструкций и строит отпечатки выполнения системных вызовов. После вмешательства дополнительный код меняет след в кэше, что позволяет заметить перехват без прав суперпользователя. Автор выпустил инструмент l1thography и показал эксперимент на Raspberry Pi 5.
У метода есть серьёзные ограничения. Нужен процессор с подходящей когерентностью кэша, известная архитектура кэша, память с нужными правами и эталонный профиль для конкретного процессора, ядра и программы измерения. Обновление ядра обычно потребует нового эталона, а очень маленький перехват может остаться незамеченным. То есть перед нами не универсальный сканер руткитов, а интересный новый измерительный канал.
Третьим я поставил бы Overview of code virtualization. Это длинный учебник по виртуализации кода. Автор начинает с мутаций инструкций, сокрытия констант и запутывания графа управления, после чего переходит к собственной виртуальной машине. Исходные x86-64 инструкции превращаются в пользовательский байткод, а внутри программы появляется интерпретатор с виртуальными регистрами, стеком, указателем инструкции, диспетчером и обработчиками команд.
Ценность статьи не столько в новизне, сколько в последовательности. После архитектуры автор показывает обратный процесс, как аналитик находит инициализацию виртуальной машины, определяет диспетчер, разбирает обработчики, восстанавливает значения команд и пишет дизассемблер пользовательского байткода. Для первого серьёзного знакомства с защитами класса VMProtect или Themida материал получился удачным.
Но один тезис в статье уже не выдерживает проверки. Автор пишет, что Denuvo фактически не получала полноценного взлома после старой волны атак. К моменту выхода номера ситуация изменилась. Появились новые гипервизорные обходы, а затем и свежие взломы защищённых игр. О резком изменении положения Denuvo писали, например, Tom's Hardware и другие профильные издания. Архитектурная часть статьи от этого слабее не становится, а историческая оценка DRM уже устарела.
Четвёртое место занимает не одна статья, а трилогия TMZ. В halfexec автор практически заново собирает часть поведения execve в пользовательском пространстве. Загрузчик написан на x86-64-ассемблере и умеет работать с ET_EXEC, ET_DYN, PIE, статическими и динамическими программами, PT_INTERP и TLS. Идея проста только на словах. До передачи управления в _start системе приходится подготовить отображения памяти, стек, аргументы, переменные окружения, вспомогательный вектор и другие детали, которые обычный программист почти никогда не видит.
В halfshelf область задачи намеренно сужается до статического PIE с одним PT_LOAD. Затем автор спрашивает, сколько сведений нужно сохранить, если удалить заголовок ELF и таблицу программных заголовков. Финальный phork упаковывает такой обезглавленный образ вместе с метаданными и небольшим загрузчиком обратно в один обычный ELF. При запуске загрузчик читает собственный файл, восстанавливает образ в памяти и передаёт управление полезной нагрузке без временного файла.
Пятое место я бы разделил между двумя работами про изменение исполняемого кода. В Fine grained load time ASLR elfmaster развивает проект Shiva и перемещает отдельные функции ELF по новым адресам во время загрузки. Получается более мелкая рандомизация, чем обычный ASLR, который в основном двигает крупные области целиком. Пока это исследовательский прототип. Нужны сохранённые текстовые релокации, программа должна учитывать большую модель адресации, поддерживаются не все типы релокаций, а сам автор перечисляет дальнейшую работу.
Рядом стоит Static Kernel Patching Redux. bah возвращается к старой идее патчинга образа ядра Linux на диске и переносит её на современные x86-64, UEFI и BIOS. Автор сообщает о тестах на широком диапазоне ядер и использует kSHELF как загружаемую полезную нагрузку. Но граница исследования тоже видна хорошо. Secure Boot, Unified Kernel Images и другие современные варианты загрузки остаются направлениями дальнейшей работы, поэтому универсальным способом модификации любого современного Linux подход считать нельзя.
Весь выпуск по порядку
| Материал | О чём речь | Моя оценка |
|---|---|---|
| Interview with Doug McIlroy | Большое интервью с Дугласом Макилроем о Bell Labs, Multics, раннем Unix, C, конвейерах, отладке и программировании при жёстких ограничениях памяти. | Обязательно читать. История Unix из первых рук. Особенно хорош рассказ о появлении конвейеров и философии маленьких программ. |
| 57-Byte ELF | Минимальный ELF64, реальные требования загрузчика Linux и перенос исполняемого кода в имя файла. | Один из лучших текстов номера. Здесь отлично видно расхождение спецификации и реализации. |
| 440-BYTE METAMORPHIC ELF-64 VIRUS | Миниатюрная программа, которая меняет собственное бинарное представление перестановкой независимых инструкций, сохраняя поведение. | Красивый бинарный гольф, но не модель современного вредоноса. У автора также есть спорный подсчёт количества возможных форм. Независимые перестановки блоков в общем случае комбинируются произведением, а не простым сложением вариантов. |
| PERL Stuff | Набор экспериментов с мутацией и перестройкой Perl-кода. | Самый слабый редакционно материал. Кода много, объяснений почти нет. Скорее заметка для своих, чем законченная статья. |
| XLAT is All You Need | 79-байтовый позиционно-независимый декодер, где редкая инструкция XLATB заменяет привычное арифметическое преобразование таблицей подстановки. | Хороший пример идеи «логика хранится в данных». Практическая ценность ограничена, исследовательская ценность заметно выше. |
| Control Flow That Isn't There | Состояние хранится во флагах процессора, выбор пути зависит от времени, а один набор байтов допускает вход с разных смещений. | Очень tmp.0ut. Полезно для понимания того, почему статический дизассемблер иногда строит убедительный, но неверный граф управления. |
| Brainfuck as a ROP Compiler | Brainfuck компилируется непосредственно в цепочку ROP-гаджетов. Стековый указатель фактически становится счётчиком команд. | Одна из самых забавных идей выпуска. Хорошо показывает ROP не как отдельный трюк эксплуатации, а как полноценную вычислительную модель. |
| How the Linux kernel loads executable files | Разбор реального пути загрузки исполняемых файлов ядром Linux с акцентом на x86, ELF и различия между вариантами ABI. | Базовый технический материал номера. Его лучше читать перед статьёй про полиглоты. |
| Polyglot ELF | Один файл одновременно строится так, чтобы представлять ELF32 и ELF64, причём разные анализаторы могут интерпретировать структуру по-разному. | Интересен прежде всего криминалистам и разработчикам анализаторов файлов. Простая проверка поля EI_CLASS не всегда отражает всю реальность. |
| Detecting syscall hooks with side-channels | Поиск перехватов системных вызовов по аппаратному следу в L1-кэше инструкций. | Самая интересная защитная работа выпуска. Ограничений много, но направление сильное. |
| Overview of code virtualization | Большой учебник по обфускации через собственные виртуальные машины и обратному анализу такого кода. | Полезно начинающему реверсеру. Исторический тезис про стойкость Denuvo нужно читать с поправкой на свежие события. |
| halfexec | Пользовательский ELF-загрузчик на ассемблере, воспроизводящий значительную часть подготовки процесса. | Отличный способ понять, что на самом деле скрывается за запуском программы. |
| halfshelf | Загрузка узкого класса статических PIE и эксперимент с удалением заголовков ELF. | Менее универсально, зато глубже. Хороший второй акт трилогии. |
| phork | Самозагружаемый упаковщик, который хранит урезанный образ внутри обычного исполняемого файла. | На мой вкус, самая красивая часть трилогии TMZ. |
| Self-Extraction Using Reachability Analysis | Эксперимент с новым стандартным Class-File API Java. Автор анализирует достижимые методы, переносит нужный байткод и модифицирует существующий class-файл. | Неожиданно современная тема для такого журнала. Хорошо показывает, что удобный официальный API для трансформации байткода полезен не только обычным инструментам разработки. |
| RDOFF Virus | Netspooky оживляет давно заброшенный формат RDOFF из истории NASM и строит для него экспериментальный файловый вирус. | Практической угрозы почти нет именно потому, что RDOFF фактически никому не нужен. Зато археология форматов великолепная. |
| BGGP6 Recap | Итоги Binary Golf Grand Prix. В соревнование поступило 36 работ, среди них 15 полиглотов, 11 самовоспроизводящихся файлов и десятки других миниатюрных конструкций. | Хороший источник идей для тех, кому нравится бинарный гольф и намеренное злоупотребление форматами файлов. |
| Fine grained load time ASLR | Перемещение отдельных функций ELF во время загрузки через Shiva вместо рандомизации только крупных областей процесса. | Серьёзное исследование, но пока прототип. До прозрачной защиты произвольных Linux-программ ещё далеко. |
| Static Kernel Patching Redux | Модификация современных x86-образов ядра Linux на диске, работа с UEFI, BIOS, PE и ELF. | Самый тяжёлый материал номера. Читать после остальных, иначе загрузочная кухня Linux быстро превращается в набор непонятных смещений. |
| tmp.0ut 5 mixtape | Музыкальный микс от blotter. | Да, у технического журнала про ELF снова есть собственный саундтрек. Формально бесполезно, культурно абсолютно уместно. |
Где выпуск особенно хорош, а где нужно держать голову включённой
Главная сильная сторона номера заключается в постоянном переходе от документации к эксперименту. Авторы не ограничиваются фразой «ELF устроен так». Они спрашивают, какие поля ядро действительно читает, какие значения можно подделать, какие структуры можно удалить и что случится после этого. Такой подход полезен далеко за пределами вирусологии. Разработчики песочниц, EDR, анализаторов бинарных файлов, упаковщиков и средств цифровой криминалистики сталкиваются с теми же расхождениями между форматом «по стандарту» и форматом «который реально принимает система».
Вторая сильная линия связана с доверием к наблюдателю. Полиглот заставляет сомневаться в файловом анализаторе. Руткит заставляет сомневаться в ядре. Виртуализация кода заставляет сомневаться в дизассемблере. Перекрывающиеся инструкции заставляют сомневаться в графе управления. В результате через весь номер проходит один хороший вопрос. Что именно доказал инструмент, когда показал нам структуру программы?
Слабая сторона у tmp.0ut традиционная. Некоторые тексты демонстрируют эффект гораздо убедительнее, чем его практическую значимость. 57-байтовый ELF великолепен как исследование загрузчика, но не как полезный исполняемый формат. RDOFF-вирус работает с практически мёртвым форматом. 440-байтовый метаморфный пример намеренно примитивен и легко обнаруживается. gASLR пока требует специально подготовленного бинарника. Кэшевый детектор руткитов зависит от конкретного процессора и эталонного профиля.
И это скорее плюс, если читать журнал правильно. tmp.0ut не продаёт готовый «продукт для защиты Linux». Журнал показывает необычные свойства вычислительных систем и доводит идеи до работающего доказательства концепции. Переносить выводы на реальную инфраструктуру нужно уже самостоятельно.
Отдельно замечу, что анонсированный конкурс Linux-руткитов пока не завершился публикацией результатов. Во вступлении редакция пишет, что результаты объявят позднее. Поэтому воспринимать пятый номер как окончательно закрытый выпуск пока рано.
Что читать в первую очередь
Если времени мало, я бы прошёл выпуск по цепочке 57-byte ELF, затем загрузчик ядра Linux, полиглот ELF, обнаружение перехватов по кэшу и трилогию halfexec. После такой базы уже имеет смысл идти в виртуализацию кода, gASLR и патчинг ядра.
В целом пятый tmp.0ut получился сильнее как журнал об устройстве исполняемых программ, чем как журнал о вирусах. Вирусы и руткиты здесь скорее инструменты для исследования границ системы. Главный урок выпуска я сформулировал бы проще. Формат файла, дизассемблер, загрузчик, ядро и процессор могут смотреть на одни и те же байты совершенно по-разному. Чем ниже спускаешься по стеку, тем меньше работают привычные предположения. Ради такого эффекта номер и стоит прочитать целиком.