Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
Эта статья написана по мотивам доклада с новогодней сходки, организованной сообществом SPbCTF в декабре 2020 года. Запись сходки со всеми докладами доступна на канале SPbCTF в YouTube.
На страничке одного хорошего пакера можно прочесть, что основная цель его существования — уменьшить размер исполняемого файла с вытекающей отсюда экономией дискового пространства и сетевого трафика. Например, UPX позволяет сжать исполняемый файл на 50–70%, и он останется полностью самодостаточным, потому что код, выполняющий распаковку в память, добавляется к получившемуся бинарю. Для этого же упаковка используется и в PyInstaller при сборке кода на Python в независимые исполняемые файлы PE или ELF (при этом он может работать и в тандеме с UPX).
Читайте также: В интернет-магазине «Фармэкс» вы можете купить сушеные мухоморы в разной расфасовке. Все грибы собраны в экологически чистых лесах Владимирской области. В течение нескольких часов после сбора их сушат при температуре 35-40 градусов только у нас - мухомор купить по доступным ценам ...
Однако, как ты догадываешься, пакеры отлично подходят еще и для того, чтобы немного подпортить жизнь реверс‑инженеру. В этом ключе родственны им крипторы. В мире Linux, однако, это скорее проекты, больше похожие на proof-of-concept, либо же просто что‑то старое и, прямо скажем, в живой природе почти не встречающееся.
Из упаковщиков для файлов ELF в настоящее время наиболее популярен UPX (в частности, среди вирусописателей), поскольку остальные пакеры либо поддерживают полторы архитектуры, либо уже очень давно не обновлялись (оценить количество канувших в Лету проектов можно, полистав давний обзор упаковщиков для Linux/BSD от Криса Касперски).
Концептуально упаковщик работает так. Код и данные программы сжимаются без потерь каким‑либо алгоритмом (с использованием lzma, zlib или чего‑либо еще), добавляется код, выполняющий распаковку того, что получилось, затем добавляются собственные заголовки, и вуаля — у нас сжатый бинарь. Схематично процесс упаковки представлен ниже.
При запуске такого файла начнет выполняться загрузчик, отвечающий за распаковку сжатого кода и данных в память, после чего он передает управление в оригинальную точку входа. Грубо говоря, получается самораспаковывающийся архив. Детали реализации в разных пакерах могут различаться — например, PyInstaller при создании exe-файла помещает упакованные данные в оверлей, а загрузчик находится перед упакованными данными, а не после, как на схеме. Более изощренные упаковщики (скорее больше напоминающие крипторы), могут еще больше усложнять этот процесс, но в целом это не меняет сути.
Как правило, полученный после упаковки файл может затем распаковываться двумя способами: либо при запуске — в память загрузчиком, либо же без его запуска — путем статической распаковки. Результаты распаковки при этом будут несколько различаться, потому что исполняемый файл, загруженный в память, уже не тот, что исходный файл на диске.
Этот проект празднует в нынешнем году 25-летие. Его исходники доступны на Гитхабе, он написан на плюсах с примесью ассемблера, поэтому разбираться в коде довольно‑таки весело. UPX поддерживает множество типов файлов и архитектур — не зря он такой популярный. Более того, PyInstaller имеет опцию --upx-dir
, позволяющую при создании архива упаковать его при помощи UPX, тем самым уменьшая размер результирующего файла.
Как происходит упаковка программы в UPX? Вначале определяется ее формат — PE, ELF, образ ядра Linux или что‑либо еще (говорят, в UPX можно паковать даже sh-скрипты!). После этого нужно определить архитектуру, под которую скомпилирован файл. Это связано с тем, что загрузчик, который называется в коде UPX stub loader («заглушка», стаб), платформенно зависим. Его код написан на языке ассемблера, потому что при сжатии разработчики стараются экономить буквально каждый байт и добиться максимальной компрессии. В связи с этим бывает и так, что сам загрузчик тоже частично упакован. Так что UPX поддерживает те архитектуры, под которые у него есть реализация стаба. Оценить список архитектур и типов файлов можно, взглянув на содержимое директории src/stub в сорцах UPX.
Итак, если архитектура упаковываемого файла поддерживается UPX, то для него формируется загрузчик, сам файл сжимается, и в целом наш сжатый бинарь готов. Для возможности статической распаковки, которая выполняется командой upx
, UPX добавляет в файл собственные заголовки.
Добавляются четыре заголовка, три из которых можно увидеть в начале исполняемого файла:
l_info
) содержит контрольную сумму, магические сигнатуры «UPX!», размер и некоторые параметры загрузчика;p_info
). В этом заголовке находятся размеры неупакованного блока p_blocksize
и неупакованной (исходной) программы p_filesize
, которые, как правило, равны;block info (b_info
) предваряет каждый сжатый блок и содержит информацию о размерах до и после сжатия, алгоритме (методе), уровне сжатия блока и других параметрах. На рисунке ниже ты можешь видеть по смещению 0x110
сигнатуру ELF: это и есть начало упакованного файла.
packheader добавляется в конце файла, обычно занимая немного больше 0x24 байт в зависимости от выравнивания. Его выделяют две сигнатуры UPX!
, вторая из которых выровнена по четырехбайтовой границе. В packheader записывается информация, необходимая самому UPX, чтобы статически распаковать бинарь, не прибегая к коду загрузчика. В нее входят, в частности, уже названные данные — размер распакованного файла и некоторые параметры сжатия. В результате этого получается некоторая избыточность. Как видим, обведенное значение совпадает с хранящимися в p_info
размерами неупакованных блока и файла. Это может немного помочь нам в дальнейшем при рассмотрении способов защиты от статической распаковки.
Если тебе хочется лучше понять процесс статической распаковки UPX, то наравне с чтением сорцов ты можешь воспользоваться дебажным флагом. Тогда ты увидишь, что на основе имеющихся в бинаре заголовков упаковщик выбирает функцию, которой будет распаковывать файл. Здесь наблюдается то же ограничение, что и для stub loader: для каких форматов и аппаратных платформ они существуют, те и поддерживаются. Список весьма внушителен.
Что же происходит, когда бинарь распаковывается не командой upx
, а своим собственным кодом? В этом случае за происходящее отвечает загрузчик. Распаковывать он может либо сразу в память, либо с использованием временных файлов с последующим их запуском — это будет зависеть от конкретной реализации.
В первом случае, который нас интересует больше в рамках данной статьи, загрузчик при помощи хитрой магии выделения памяти и назначения выделенным областям памяти нужных прав подготавливает области для сегментов оригинального исполняемого файла — блоков кода, данных, кучи, стека и при необходимости динамических библиотек. В идеале все должно выглядеть так, чтобы запакованный файл и не понял, что был упакован, и работал целиком и полностью так же, как до распаковки.
|
|