Улучшение степени сжатия применяемого в UPX

Под «маской» находится перевод небольшого, но очень полезного текстового файла «%UPX_SOURCE%filter.txt». В указанном пути UPX_SOURCE относится к пути к файлу исходного кода UPX версии 3.91.

Ниша дропшиппинга. Полный курс по Дропшиппингу
8 часов назад
Ниша дропшиппинга. Полный курс по Дропшиппингу
7 часов назад

В документе описывается очень важный аспект UPX, который называется «фильтрация», и при анализе упакованных файлов UPX очень важно понимать, как он работает. Все, что было описано про UPX, относится и к другим упаковщикам.

Основная цель этого перевода — попытаться помочь тем программистам, которые пишут статические распаковщики для исполняемых файлов. Другими словами, эта информация будет полезна реверс-инженерам. Под статическим распаковщиком я подразумеваю программу, которая принимает упакованный или защищенный исполняемый файл в качестве входных данных и создает выходной файл, как если бы он был создан каким-либо компилятором. Особенность этого типа экстрактора в том, что он работает исключительно на основе знания защиты или структуры упаковки файла, т.е. без использования «дампа дампа», «восстановления импорта» и других видов «читерства».

Понимание процесса фильтрации помогает, например, при проверке упакованных файлов с помощью UPX, RLPack и т. д. В упакованных файлах можно найти некоторые «магические» операции с байтами 0xE8, 0xE9 и другие инструкции перехода. Эта «магия» — «фильтрация». Он направлен на улучшение сжатия исполняемого файла.

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

Этот документ объясняет концепцию «фильтрации» в системе UPX. По сути, фильтрация — это предварительная обработка данных, которая может улучшить степень сжатия файлов UPX.

В настоящее время фильтры UPX используют метод, основанный на одном очень конкретном алгоритме. Это хорошо согласуется с двоичными файлами в архитектуре i386. В UPX это известно как «наивная» реализация. Есть еще «хитрый» метод, подходящий только для 32-битных бинарников, впервые реализованный в UPX.

Давайте возьмем пример и посмотрим на фрагмент кода (вот где находится 32-битный файл):

Возможно, вы заметили два оператора CALL, вызывающих «FatalError» в приведенном выше фрагменте кода. Вы, вероятно, можете догадаться, что степень сжатия будет лучше, если «движок» компрессора найдет больше последовательностей повторяющихся строк. В нашем случае двигатель имеет следующие два байта последовательности:

Таким образом, он может найти 3-байтовые совпадения.

Теперь давайте применим трюк. Для архитектуры i386 вызовы close кодируются как 0xE8, за которым следует 32-битное относительное смещение адреса перехода. Теперь посмотрим, что произойдет, если значение позиции вызова будет добавлено к значению смещения:


«Движок» компрессора теперь находит 5-байтовое совпадение. Благодаря этому мы сохраняем 2 байта сжатых данных. Неплохо.

Это основная идея «наивного» метода реализации. Вам просто нужно использовать метод «фильтр» перед сжатием и «разблокировать» после распаковки. Просто перейдите в память, найдите 0xE8 байт и обработайте следующие 4 байта, как сказано выше.

Конечно, есть несколько возможностей, где эту схему можно было бы улучшить. Во-первых, не только CALL могут обрабатываться, но и близкие к jmps (0xE9 + 32-битное смещение) работают точно так же.

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

ДЕНЕЖНЫЙ БОТ - Заработок до 100 000 в авторежиме
7 часов назад
Ежедневный заработок от 918 рублей на поиске информации
9 часов назад

Другое улучшение было бы, если бы мы изменили порядок байтов в 32-битном смещении. Почему? Вот еще один CALL, который следует во фрагменте выше:


Вы можете заметить, что эти две функции довольно близки друг к другу, но компрессор не может использовать эту информацию (2-байтовые совпадения обычно не используются), если порядок байтов смещения меняется. В таком случае:


Таким образом, «движок» компрессора также ищет такие 3-байтовые совпадения. Это приятное улучшение, теперь «движок» также использует совпадения с близким смещением.

Это хорошо, но что происходит, когда мы находим «фальшивое» ПРИГЛАШЕНИЕ? Другими словами, 0xE8, который является частью другой инструкции? Например этот:

Тогда в этом случае эти большие байты 0x00 будут перезаписаны чуть менее сжимаемыми данными. Это невыгодная «наивная» реализация.

Давайте поумнеем и попробуем обнаруживать и обрабатывать только «важные» СОЕДИНЕНИЯ. UPX использует простой метод для поиска этих вызовов. Мы просто проверяем цель этих вызовов в некоторой степени, как и вызовы (поэтому приведенный выше код является ложным срабатыванием, но в целом помогает). Лучшим способом было бы разобрать код, помощь приветствуется 🙂

Но это только часть работы. Мы не можем просто обработать один ЗВОНОК, а затем добавить другой, процессу фильтрации нужна некоторая информация, чтобы иметь возможность отменить фильтрацию.

UPX использует следующую идею, которая хорошо работает. Во-первых, мы предполагаем, что размер области фильтра меньше 16 МБ. Затем UPX сканирует эту область и записывает байты, следующие за байтами 0xE8. Если нам повезет, мы найдем байты, которые не следуют за другим 0xE8. Эти байты являются нашими кандидатами на использование в качестве токенов.

Помните, мы предполагали, что размер области сканирования меньше 16 МБ? Ну, это означает, что мы обрабатываем настоящий CALL, и результатом также будет смещение меньше 0x00FFFFFF. Поэтому MSB всегда равен 0x00. Какое прекрасное место для хранения нашего чипа. Разумеется, в полученном смещении мы меняем порядок байтов, чтобы этот токен стоял только после байта 0xE8, а не через 4 байта после него.

Это оно! Просто работайте с областью памяти, идентифицирующей «настоящие» ВЫЗОВЫ, и используйте этот метод, чтобы пометить их. Тогда задача фильтрации довольно проста, просто найдите строку 0xE8 + маркера и парад, если вы ее найдете. Это умно, не так ли? 🙂

По правде говоря, в UPX не все так просто. Вы можете использовать необязательный параметр («add_value»), который немного усложняет дело (например, токен может считаться бесполезным, потому что он переполняется при добавлении).

А алгоритм в целом оптимизирован для легкой фильтрации (короткой и быстрой, см. заглушку/макросы.ash), что делает процесс фильтрации менее сложным (fcto_ml.ch, fcto_ml2.ch, filteri.cpp).

Как вы можете видеть в filteri.cpp, существует множество вариаций этих реализаций фильтрации: — нативные/умные, вызовы/переходы/вызовы и переходы, с поворотом смещения/без него — всего около 18 различных фильтров (и 9 других вариантов для 16 -битные программы).

Вы можете выбрать один из них с помощью параметра командной строки «—filter =» или протестировать большинство из них с помощью «—all-filters». Или просто позвольте UPX использовать один из наших исполняемых форматов по умолчанию.

От переводчика:

Рад рассмотреть сообщения об ошибках в моем личном списке сообщений:

* Перевод, правописание или грамматика;

* Технические ошибки в переводе, убедитесь в правильности оригинала!

ПАРТНЕРСКИЕ АВТОВОРОНКИ. Серьезные комиссии на полуавтомате.
7 часов назад
Crypto News: заработок на размещении микро-новостей от 100 000 рублей.
6 часов назад

Читайте также