Категория > Новости > Крепость эльфов. Как распаковать исполняемый файл Linux, накрытый UPX - «Новости»
Крепость эльфов. Как распаковать исполняемый файл Linux, накрытый UPX - «Новости»11-06-2021, 07:18. Автор: Данила |
Эта статья написана по мотивам доклада с новогодней сходки, организованной сообществом SPbCTF в декабре 2020 года. Запись сходки со всеми докладами доступна на канале SPbCTF в YouTube. Зачем это все?На страничке одного хорошего пакера можно прочесть, что основная цель его существования — уменьшить размер исполняемого файла с вытекающей отсюда экономией дискового пространства и сетевого трафика. Например, UPX позволяет сжать исполняемый файл на 50–70%, и он останется полностью самодостаточным, потому что код, выполняющий распаковку в память, добавляется к получившемуся бинарю. Для этого же упаковка используется и в PyInstaller при сборке кода на Python в независимые исполняемые файлы PE или ELF (при этом он может работать и в тандеме с UPX).
Однако, как ты догадываешься, пакеры отлично подходят еще и для того, чтобы немного подпортить жизнь реверс‑инженеру. В этом ключе родственны им крипторы. В мире Linux, однако, это скорее проекты, больше похожие на proof-of-concept, либо же просто что‑то старое и, прямо скажем, в живой природе почти не встречающееся. Из упаковщиков для файлов ELF в настоящее время наиболее популярен UPX (в частности, среди вирусописателей), поскольку остальные пакеры либо поддерживают полторы архитектуры, либо уже очень давно не обновлялись (оценить количество канувших в Лету проектов можно, полистав давний обзор упаковщиков для Linux/BSD от Криса Касперски). Принцип работы среднестатистического пакераКонцептуально упаковщик работает так. Код и данные программы сжимаются без потерь каким‑либо алгоритмом (с использованием lzma, zlib или чего‑либо еще), добавляется код, выполняющий распаковку того, что получилось, затем добавляются собственные заголовки, и вуаля — у нас сжатый бинарь. Схематично процесс упаковки представлен ниже. Упаковка исполняемого файла При запуске такого файла начнет выполняться загрузчик, отвечающий за распаковку сжатого кода и данных в память, после чего он передает управление в оригинальную точку входа. Грубо говоря, получается самораспаковывающийся архив. Детали реализации в разных пакерах могут различаться — например, PyInstaller при создании exe-файла помещает упакованные данные в оверлей, а загрузчик находится перед упакованными данными, а не после, как на схеме. Более изощренные упаковщики (скорее больше напоминающие крипторы), могут еще больше усложнять этот процесс, но в целом это не меняет сути. Как правило, полученный после упаковки файл может затем распаковываться двумя способами: либо при запуске — в память загрузчиком, либо же без его запуска — путем статической распаковки. Результаты распаковки при этом будут несколько различаться, потому что исполняемый файл, загруженный в память, уже не тот, что исходный файл на диске. The Ultimate Packer for eXecutablesЭтот проект празднует в нынешнем году 25-летие. Его исходники доступны на Гитхабе, он написан на плюсах с примесью ассемблера, поэтому разбираться в коде довольно‑таки весело. UPX поддерживает множество типов файлов и архитектур — не зря он такой популярный. Более того, PyInstaller имеет опцию Как происходит упаковка программы в UPX? Вначале определяется ее формат — PE, ELF, образ ядра Linux или что‑либо еще (говорят, в UPX можно паковать даже sh-скрипты!). После этого нужно определить архитектуру, под которую скомпилирован файл. Это связано с тем, что загрузчик, который называется в коде UPX stub loader («заглушка», стаб), платформенно зависим. Его код написан на языке ассемблера, потому что при сжатии разработчики стараются экономить буквально каждый байт и добиться максимальной компрессии. В связи с этим бывает и так, что сам загрузчик тоже частично упакован. Так что UPX поддерживает те архитектуры, под которые у него есть реализация стаба. Оценить список архитектур и типов файлов можно, взглянув на содержимое директории src/stub в сорцах UPX. Итак, если архитектура упаковываемого файла поддерживается UPX, то для него формируется загрузчик, сам файл сжимается, и в целом наш сжатый бинарь готов. Для возможности статической распаковки, которая выполняется командой Заголовки UPXДобавляются четыре заголовка, три из которых можно увидеть в начале исполняемого файла:
Если тебе хочется лучше понять процесс статической распаковки UPX, то наравне с чтением сорцов ты можешь воспользоваться дебажным флагом. Тогда ты увидишь, что на основе имеющихся в бинаре заголовков упаковщик выбирает функцию, которой будет распаковывать файл. Здесь наблюдается то же ограничение, что и для stub loader: для каких форматов и аппаратных платформ они существуют, те и поддерживаются. Список весьма внушителен. UPX перебирает все возможные функции упаковки перед тем, как сдаться Распаковываем себяЧто же происходит, когда бинарь распаковывается не командой В первом случае, который нас интересует больше в рамках данной статьи, загрузчик при помощи хитрой магии выделения памяти и назначения выделенным областям памяти нужных прав подготавливает области для сегментов оригинального исполняемого файла — блоков кода, данных, кучи, стека и при необходимости динамических библиотек. В идеале все должно выглядеть так, чтобы запакованный файл и не понял, что был упакован, и работал целиком и полностью так же, как до распаковки. Перейти обратно к новости |