Что такое упаковщики exe и com файлов
Давным-давном, когда Windows XP еще не было, в поисках информации о пакерах мы с Волком забирались в самые дебри исходников тогда еще молодого UPX. Но то ли ацетилхолина у нас в мозгах синтезировалось меньше нужного, то ли UPX уже тогда был очень занудным — в общем, мы почти ничего из тех сорцов не извлекли. Мэтт Питрек, и тот помог больше. Сейчас с инфой значительно проще стало. Почти всё есть. Даже сорцы вполне себе нормального банковского троя можно скачать (Zeus 2.0.8.9, bit.ly/v3EiYP). Да чего уж там, сорцы винды уже давно в паблике (Windows 2000, bit.ly/rBZlCy).
Об упаковщиках информация тоже есть, но в основном исследовательская, непосредственно разработки касающаяся не с той стороны, с которой нам бы хотелось. Отличным примером тому является статья »Об упаковщиках в последний раз» (bit.ly/vRPCxZ, bit.ly/tSUxT7) в двух частях, написанная небезызвестными гуру Volodya и NEOx.
Мы, в свою очередь, постараемся максимально конкретно и последовательно дать информацию именно о разработке простейшего, но легко модифицируемого под любые свои нужды PE-пакера.
Ссылки
Wikimedia Foundation . 2010 .
См. также
Упаковщик исполнимых файлов
Упаковщик исполнимых файлов — программа для уменьшения размера исполнимых файлов. В упакованный файл записывается сжатая копия оригинального файла и программа для распаковки.
После запуска сжатого файла, распаковщик извлекает оригинальный код программы из архива (обычно напрямую в память, хотя существуют и упаковщики, записывающие файл на диск), и передает ему управление.
Упакованный исполнимый файл является разновидностью самораспаковывающегося архива (SFX), в котором сжимаемая информация и программа для распаковки находится в одном файле.
Уменьшение размера загрузчика
Если наш загрузчик скомпилировать и собрать в VS 2010 с флагами по умолчанию, то мы получим не двухкилобайтную программку-носитель, а монстра размером более 10 Кб. Студия встроит туда целую кучу лишнего, а нам надо всё это оттуда выгрести.
Поэтому в свойствах компиляции проекта загрузчика (вкладка С/C++) мы делаем следующее:
- В разделе «Оптимизация» выбираем «Минимальный размер (/O1)», чтобы компилятор старался сделать все функции более компактными.
- Там же указываем приоритет размера над скоростью (флаг /Os).
- В разделе «Создание кода» выключаем исключения С++, мы их не используем.
- Проверка переполнения буфера нам тоже не нужна (/GS-). Это штука хорошая, но не в нашем случае.
В свойствах линкера (компоновщика):
- Отключаем к чертям «Манифест». Он большой, и из-за него в загрузчике создается секция .rsrc, которая нам совершенно не нужна. Вообще, каждая лишняя секция в PE-файле — это минимум 512 совершенно ненужных байт, спасибо выравниванию.
- Отключаем создание отладочной информации.
- Лезем во вкладку «Дополнительно». Выключаем «Внесение случайности в базовый адрес» (/DYNAMICBASE:NO), иначе линкер создаст секцию релоков (.reloc).
- Указываем базовый адрес. Выберем какой-нибудь нестандартный повыше, например 0x02000000. Именно это значение будет возвращать GetModuleHandle(NULL) в загрузчике. Можно его даже захардкодить.
- Указываем нашу точку входа, а не CRT-шную: /ENTRY:WinMain. Вообще, мы привыкли это делать директивой pragma прямо из кода, но раз уж залезли в свойства, то можно и тут.
Остальные настройки для линкера задаем непосредственно из кода:
Здесь мы объединили секцию .rdata, в которой содержатся данные, доступные только для чтения (строки, таблица импорта и т. п.), с секцией кода .text. Если бы мы использовали глобальные переменные, то нам также надо было бы объединить с кодом секцию .data.
Всего перечисленного хватит, чтобы получить лоадер размером в 1,5 Кб.
WARNING
Вся информация предоставлена исключительно в ознакомительных целях. Ни редакция, ни автор не несут ответственности за любой возможный вред, причиненный материалами данной статьи.
Сага о протекторах и упаковщиках
Один из излюбленных приемов зловредописателей — использование упаковщиков (packers) и протекторов (protectors) исполняемых файлов (хотя это также относится и к DLL). Изначально эти инструменты считались весьма банальными и были призваны, по сути, уменьшать размер скомпилированного файла, а в случае протекторов — позволять модифицировать авторам свои программы, превращая их, к примеру, в demo- или trial-версию, и не заморачиваться с защитой в основном коде. Но позднее вирусописатели приспособили эти инструменты в корыстных целях.
Создатели вредоносов успешно стали применять их, чтобы усложнить антивирусный и эвристический анализ, защитить свои детища от запуска в виртуальной среде, отладки, дизассемблирования и последующего анализа. Поэтому с тех пор навыки и умения распаковывать исполняемые файлы вошли в обязательные требования как для начинающего, так и для опытного реверс-инженера. Наиболее популярные сегодня упаковщики — UPX, ASPack, FSG, PeShield, VMProtect. Это, так сказать, джентльменский набор, с которым аналитику приходится сталкиваться каждый день.
Протекторы, в отличие от упаковщиков, призваны защитить исходный файл от обратной разработки, соответственно, при этом они используют более изощренные методы, чем просто упаковщики: обфускацию, шифрование с использованием самописного либо популярного криптоалгоритма, такого, например, как RSA-1024, встраивание антиотладочных функций.
Как мы понимаем, чтобы добраться до нужного нам кода, который мы будем анализировать, сначала требуется распаковать файл, то есть снять все навесные защиты, восстановить оригинальную OEP и таблицу импорта, это как минимум. Частенько распаковка — это задача, укладывающаяся в стандартный набор действий, но иногда она становится творческой и выливается в целое хакерское исследование — с ящиками пива, блоками сигарет и сантиметрами сожженных нервных волокон :).
Распаковщики
Точная распаковка файлов, как и обратная разработка в целом, зачастую бывает затруднена или невозможна.
Многие распаковщики (например, procdump или PEiD) запускают файл и создают распакованный вариант файла из образа, загруженного в память. Однако в случае, если этот файл содержал вирус, система может быть повреждена. Кроме того, у упаковщиков существует ряд приёмов борьбы с динамической распаковкой: например, расшифровывать код не полностью, а лишь по мере исполнения, или, например, расшифровывать и запускать вирус целиком только в определённый день недели.
Статические распаковщики — которые пытаются распаковать файл, не запуская его (например, CUP386 или UNP) — оказываются бесполезны, если алгоритм упаковки требует запуска файла.
Выводы
Если сравнивать эффективность сжатия нашего упаковщика с UPX на примере notepad.exe — мы выигрываем примерно 1 Кб: 46 592 байта у нас против 48 128 у UPX. Однако наш пакер далеко не совершенен. И это очень заметно. Дело в том, что мы сознательно проигнорировали такую важную вещь, как перенос ресурсов. Полученный в результате сжатия файл потеряет иконку! Реализовать недостающую функцию предстоит тебе самому. Благодаря полученным из этого материала знаниям, никаких сложностей у тебя с этим делом не возникнет.
Упаковщик исполняемых файлов — программа для уменьшения размера исполняемых файлов. В упакованный файл записывается сжатая копия оригинального файла и программа для распаковки.
После запуска сжатого файла, распаковщик извлекает оригинальный код программы из архива (обычно напрямую в память, хотя существуют и упаковщики, записывающие файл на диск), и передает ему управление.
Упакованный исполняемый файл является разновидностью самораспаковывающегося архива (SFX), в котором сжимаемая информация и программа для распаковки находится в одном исполняемом файле.
Шифрование кода
При анализе различных защит нередко приходится определять, какой алгоритм был использован для шифрования данных. Часто зловредописатели не изобретают велосипедов, а используют уже готовые алгоритмы шифрования. К примеру, если алгоритмы стандартные, то их можно идентифицировать по некоторым характерным константам-полиномам, таблицам преобразований или по последовательности выполняемых операций. Для поиска криптоалгоритмов в исполняемых файлах созданы специальные программы, которые можно посмотреть и скачать тут.
Наиболее популярен плагин Krypto ANALyzer для PEiD. Найденные значения можно просто посмотреть или экспортировать в скрипт для дизассемблера IDA Pro.
Примеры упаковщиков
Помимо алгоритмов упаковки, встроенных в некоторые компиляторы и средства разработки (например, Visual C++ и т. п.), существует ряд инструментов для более специфических задач, например:
Несколько слов о breakpoints (точках останова)
Точки останова — часто используемый и незаменимый прием любого реверс-аналитика. Основные режимы — это:
- останов при чтении;
- останов при записи;
- выполнение памяти по заданному адресу.
Команда CALL $+5 POP REG характерна для защитных механизмов, к примеру копирующих себя на стек. А часто возникающая инструкция PUSHFD присутствует в самотрассирующихся программах и антиотладочных защитных механизмах.
Алгоритм
Вот есть у нас, например, notepad.exe. В обычном своем 32-битном виде он весит где-нибудь 60 Кб. Мы хотим его существенно уменьшить, сохранив при этом всю его функциональность. Какими должны быть наши действия? Ну, для начала мы наш файлик от первого до последнего байтика прочтем в массив. Теперь мы можем делать с ним всё что угодно. А нам угодно его сжать. Берем его и отдаем какому-нибудь простому компрессору, в результате чего получаем массив уже не в 60 Кб, а, например, в 20 Кб. Это круто, но в сжатом виде образ нашего «Блокнота» — это просто набор байтов с высокой энтропией, это не экзешник, и его нельзя запустить, записав в файл и кликнув. Для массива со сжатым образом нам нужен носитель (загрузчик), очень маленький исполняемый файл, к которому мы прицепим наш массив и который его разожмет и запустит. Пишем носитель, компилируем, а затем дописываем к нему в конец наш сжатый «Блокнот». Соответственно, если полученный в результате всех действий файл (размер которого немного больше, чем у просто сжатого «Блокнота») запустить, он найдет в себе упакованный образ, распакует, распарсит его структуру и запустит.
Как видишь, нам предстоит автоматизировать не слишком сложный процесс. Нужно будет просто написать две программы, загрузчик и, собственно, упаковщик.
Алгоритм работы упаковщика:
- считать PE-файл в массив;
- сжать массив каким-нибудь алгоритмом сжатия без потерь;
- в соответствии с форматом PE дописать сжатый массив к шаблону-загрузчику.
Алгоритм работы загрузчика:
- найти в конце себя массив со сжатым PE-файлом;
- разжать его;
- распарсить заголовки PE-файла, расставить все права, выделить память и в итоге запустить.
Начнем разработку с загрузчика, так как именно им впоследствии будет манипулировать упаковщик.
Учимся скрывать присутствие отладчика и обходить методы противодействия
В одной из статей нашего журнала были описаны наиболее интересные плагины для OllyDbg. Нам обязательно понадобятся:
- OllyExt — содержит опции Anti-AntiDebug;
- OllyDumpEx — отличный дампер процессов;
- swordfish — быстрая установка точек останова;
- uberstealth — фича Anti-AntiDebug, основанная на коде IDA Stealth.
Все самые нужные плагины OllyDbg 2.xx Plugins можно забрать с файлового архива Tuts4you тут и тут. Набор плагинов для IDA Pro с подробным описанием доступен на GitHub или на Tuts4you. Тем же, кто готов написать свой плагин, могу рекомендовать интересную статью.
Полезное
Упаковщик
Нам остается разработать консольную утилиту, которая будет сжимать отданные ей файлы и прицеплять к лоадеру. Первое, что она должна делать по описанному в начале статьи алгоритму, — это считывать файл в массив. Задача, с которой справится и школьник:
Далее наш пакер должен сжать полученный файл. Мы не будем проверять, действительно ли это PE-файл, корректные ли у него заголовки и т. п., — всё оставляем на совести пользователя, сразу сжимаем. Для этого воспользуемся функциями RtlCompressBuffer и RtlGetCompressionWorkSpaceSize. Первую мы уже описали выше — она сжимает буфер, вторая же нужна, чтобы вычислить объем памяти, необходимой для работы сжимающего движка. Будем считать, что обе функции мы уже динамически подключили (как и в загрузчике), остается только их запустить:
В результате у нас есть сжатый буфер и его размер, можно прикрутить их к загрузчику. Чтобы это сделать, нужно для начала скомпилированный код нашего загрузчика встроить в упаковщик. Самый удобный способ засунуть его в программу — это воспользоваться утилитой bin2h. Она конвертнет любой бинарник в удобный сишный хедер, все данные в нем будут выглядеть как-то так:
Скармливаем ей файл с нашим лоадером и получаем всё необходимое для дальнейших извращений. Теперь, если придерживаться алгоритма, описанного в начале статьи, мы должны прицепить к загрузчику сжатый образ. Здесь нам с Волком придется вспомнить 90-е и свое вирмейкерское прошлое. Дело в том, что внедрение данных или кода в сторонний PE-файл — это чисто вирусная тема. Организуется внедрение большим количеством разных способов, но наиболее тривиальные и популярные — это расширение последней секции или добавление своей собственной. Добавление, на наш взгляд, чревато потерями при выравнивании, поэтому, чтобы встроить сжатый образ в наш загрузчик, мы расширим ему (загрузчику) последнюю секцию. Вернее, единственную секцию — мы же избавились от всего лишнего. 😉
Создание хедера с помощью bin2h можно автоматизировать
Алгоритм действий будет такой:
- Находим единственную секцию (.text) в загрузчике.
- Изменяем ее физический размер, то есть размер на диске (SizeOfRawData). Он должен быть равен сумме старого размера и размера сжатого образа и при этом выравнен в соответствии с файловым выравниванием (FileAlignment).
- Изменяем виртуальный размер памяти (Misc.VirtualSize), прибавляя к нему размер сжатого образа.
- Изменяем размер всего образа загрузчика (OptionalHeader.SizeOfImage) по древней формуле [виртуальный размер последней секции] + [виртуальный адрес последней секции], не забывая выравнивать значение по FileAlignment.
- Копируем сжатый образ в конец секции.
Тут есть небольшая хитрость. Дело в том, что наша студия делает виртуальный размер (Misc.VirtualSize) секции с кодом (.text) равным реальному невыравненному размеру кода, то есть указывает размер меньше физического. А значит, есть шанс сэкономить до 511 байт.
То есть так бы мы записали данные после кучи выравнивающих нулей, а зная фишку, можно записаться поверх этих нулей.
Кусок кода парсера таблицы импорта
Вот как все наши мысли будут выглядеть в коде:
О, мы едва не забыли заменить метки 0xDEADBEEF и 0xBEEFCACE, оставленные в загрузчике, на реальные значения! 0xBEEFCACE у нас меняется на размер сжатого образа, а 0xDEADBEEF — на его абсолютный адрес. Адрес образа вычисляется по формуле [адрес образа] + [виртуальный адрес секции] + [смещение образа относительно начала секции]. Следует отметить, что замену надо производить еще до обновления значения Misc.VirtualSize, иначе полученный в результате файл не заработает.
Ищем и заменяем метки с помощью очень простого цикла:
Вот, собственно, и всё. Теперь в памяти у нас есть упакованный и готовый к работе файл, достаточно сохранить его на диске с помощью функций CreateFile/WriteFile.
Наш упаковщик сжал notepad.exe лучше, чем UPX!
Смотреть что такое "Упаковка исполняемых файлов" в других словарях:
Упаковщик исполняемых файлов — программа для уменьшения размера исполняемых файлов. В упакованный файл записывается сжатая копия оригинального файла и программа для распаковки. После запуска сжатого файла, распаковщик извлекает оригинальный код программы из архива (обычно… … Википедия
PECompact — Скри … Википедия
The Ultimate Packer for eXecutables — UPX the Ultimate Packer for eXecutables Тип Упаковка исполняемых файлов Написана на C++, Ассемблер ОС Microsoft Windows, DOS, 27 апреля 2008 Лицензия GNU GPL Сайт … Википедия
Mpress — Тип Упаковка исполняемых файлов Разработчик Matcode Software Операционная система Microsoft Windows, Mac OS Последняя версия 2.18 Лицензия Freeware Сайт … Википедия
UPX — the Ultimate Packer for eXecutables Тип Упаковка исполняемых файлов Написана на C++, Ассемблер Операционная система Microsoft Windows, Linux, MacOS, DOS, Atari Первый выпуск 26 мая 1998 год … Википедия
APLib — Тип Упаковка исполняемых файлов Разработчик Joergen Ibsen Написана на С Операционная система Microsoft Windows Языки интерфейса Английский Последняя версия 1.01 ( … Википедия
JCALG1 — Тип Упаковка исполняемых файлов Разработчик Bitsum Technologies Jeremy Collake Написана на Ассемблер Операционная система Microsoft Windows Языки интерфейса Английский … Википедия
ASPack — Вид главного окна Тип Упаковка исполняемых файлов Разработчик Алексей Солодовников (ASPack Software) … Википедия
EXPressor — Тип Упаковка исполняемых файлов Разработчик CGSoftLabs Написана на … Википедия
RePack — (рус. переупаковка, пересборка) архив, содержащий какую либо лицензионную или пиратскую версию компьютерной игры, с собственной программой установки/распаковки. Термин появился, когда начали выпускать игры объёмом более 4 5 ГБ. Суть… … Википедия
Программы-упаковщики - это разновидность архиваторов, которые сжимают исполняемые файлы (.exe), динамически подсоединяемые библиотеки (.dll) и др. с сохранением их полной работоспособности в упакованном состоянии. Другими словами, если вы запакуете "экзешник", то он уменьшится в размере и, что немаловажно, не перестанет быть "экзешником". Т.е. его можно будет запускать на выполнение, как если бы он вообще не подвергался действиям подобных утилит.
Упаковщики работают так же, как и архиваторы, за одним лишь исключением - они помещают процедуру распаковки (decompression procedure) в начало программы, которую только что сжали.
Обычно упакованная программа загружается быстрее, чем неупакованная, что объясняется уменьшением ее размера. Однако многие алгоритмы декомпрессии требуют немало оперативной памяти. Если ее в системе не хватает, то запускаемая программа может быть помещена в файл подкачки. В этом случае сжатое приложение будет открываться дольше.
Где находят применение упаковщики, или, как их часто называют, пакеры? Конечно, их используют программисты, чтобы уменьшить размер написанных файлов, ускорить их запуск и защитить от взлома. Но и крекеры (взломщики) не обходят эти программы стороной, создавая декомпрессоры для популярных упаковщиков и внося, таким образом, свою лепту в развитие технологий защиты данных. :) Что касается обычных пользователей, то упаковщик всегда поможет переслать исполняемый файл по почте, разместить в интернете или записать на информационный носитель небольшой емкости, например, дискету. Процедура распаковки занимает считанные байты, поэтому упакованные exe-файлы имеют почти такой же размер, что и заархивированные, однако, как вы понимаете, не требуют для запуска дополнительного ПО, что тоже удобно.
Чтобы определить, в каких случаях и какими программами лучше пользоваться, я предлагаю провести сравнительный анализ. Итак, в сегодняшнем шоу принимают участие следующие известные пакеры.
PECompact v2.76
Размер: 1,3 Mb
Распространение: shareware (14 дней опробования)
Утилита PECompact предназначена для сжатия файлов .exe, .dll, .scr с помощью многочисленных алгоритмов, которые доступны в меню Файл -> Изменить установки. -> Выбрать кодеки. В программе предусмотрена возможность выбора компонентов файлов, которых не следует подвергать компрессии (иконки, курсоры, шрифты и др.), а также функция оптимизации структуры файла, которая позволит без сжатия уменьшить его размер (опция "Trim Only"). Кроме того, PECompact имеет русскоязычный интерфейс и консольную версию - pec2.exe. Также позволяет работать с несколькими файлами сразу.
ASPack v2.12
Размер: 300 Kb
Распространение: shareware (30 дней опробования)
Упаковщик ASPack прост в использовании и, благодаря мощному алгоритму, позволяет добиться 40-70% сжатия для 32-битных приложений Windows. Поддерживаемые файлы: .exe, .dll, .ocx, .dpl, .bpl (файлы библиотек Delphi). Программа может проверить перед окончательной упаковкой функциональность exe-файла и, при нарушении его нормальной работы, отменить сжатие. Для любознательных: ASPack был написан в Borland Delphi 2.0. Русский язык поддерживается.
UpxVis v1.8
Размер: 350 Kb
Распространение: freeware
UPX (the Ultimate Packer for eXecutables) - быстрый упаковщик, работающий в консольном режиме и позволяющий достичь высоких коэффициентов сжатия. Также может выполнять декомпрессию. Поддерживаемые форматы файлов: exe, sys, com, pe (Win32), 386 (Linux) и др. Для UPX написано множество оболочек, значительно повышающих удобство работы с утилитой. Одной из них является UpxVis v1.8. В отличие от классического UPX, программа позволяет устанавливать защиту от декомпрессии и умеет упаковывать dll. Русский язык присутствует.
Подготовка к тестированию
Для качественного сравнения работы программ-упаковщиков были выбраны (случайным образом) исполняемые файлы (.exe) и динамически подсоединяемые библиотеки (.dll) известных приложений, а также мои программы, написанные в Delphi и Visual C++. Сравнение будет проходить по следующим критериям: размер упакованного файла, время компрессии и время декомпрессии, работоспособность файла после сжатия.
Во время тестирования преследуется цель максимально уменьшить размер выходного файла без увеличения времени его распаковки. Для этого в PECompact был выбран алгоритм FFCE, обеспечивающий хорошее сжатие и малое время запуска упакованного файла и установлен максимальный уровень сжатия (9). Остальные настройки - по умолчанию. В настройках ASPack были включены пункты "Сжимать ресурсы" и "Максимальное сжатие", а в UpxVis вместе с упаковкой ресурсов была установлена максимальная степень компрессии (10). Помните, что в ASPack и PECompact параметры упаковки нужно задавать для каждого файла в отдельности
Результаты
По итогам тестирования можно заключить, что все упаковщики справились с предложенной задачей достойно. Самый большой коэффициент сжатия продемонстрировал UPX.
Упаковщики также были опробованы и на обычных установочных файлах, которые каждый из нас запускает по несколько штук в день. В основном, попадались "экзешники", содержащие экстра данные с жестко заданным смещением (оверлей). UPX с такими файлами работать отказался, а для остальных упаковщиков в настройках опять была включена опция "Сохранять оверлей". PECompact с компрессией справился нормально: программы загружались, но степень сжатия была незначительная. ASPack, напротив, только угробил "экзешники". Вывод: инсталляционные файлы программ упаковывать нет смысла, т.к. они, во-первых, плохо поддаются упаковке и, во-вторых, уже сжаты разработчиками.
Выводы
Упаковщик UPX с оболочкой UpxVis показал наилучшее сжатие исполняемых файлов (.exe). Учитывая то, что программа распространяется бесплатно, можно сказать, что для упаковки "экзешников" целесообразнее применять именно ее. Если UPX что-то не сможет сделать хорошо, то он вам об этом непременно сообщит. Для сжатия dll и файлов с оверлеем (не установочных!) лучше использовать ASPack, т.к. он работает надежнее и быстрее. Я, скорее всего, выберу именно его. А PECompact подкупает лишь возможностью выбора кодеков, с которыми можно поэкспериментировать на досуге. Он сжимает почти так же, как и ASPack, только иногда тратит на это больше времени.
Когда наш журнал был бумажным, мы считали не очень хорошей идеей делать серии зависимых друг от друга статей, ведь, чтобы освежить воспоминания месячной и двухмесячной давности, читателю пришлось бы поднимать подшивку. А теперь все просто :). Мы делаем цикл по реверсу малвари, две статьи уже вышло — вот нулевая (если кто не заметил, там офигенные ссылки, почитай, не пожалеешь. — Прим. ред.), вот первая. Если ты что-то забыл — вспоминай, а если нет — готовься узнать о том, как работают упаковщики и протекторы, для чего их используют и как с ними можно бороться.
Смотреть что такое "Упаковщик исполняемых файлов" в других словарях:
Упаковка исполняемых файлов — Упаковка исполнимых файлов состоит в сжатии исполнимого файла и прикреплении к нему кода, необходимого для распаковывания и исполнения содержимого файла. Упаковка производится по ряду причин: Упакованный файл занимает меньше места на… … Википедия
The Ultimate Packer for eXecutables — UPX the Ultimate Packer for eXecutables Тип Упаковка исполняемых файлов Написана на C++, Ассемблер ОС Microsoft Windows, DOS, 27 апреля 2008 Лицензия GNU GPL Сайт … Википедия
UPX — the Ultimate Packer for eXecutables Тип Упаковка исполняемых файлов Написана на C++, Ассемблер Операционная система Microsoft Windows, Linux, MacOS, DOS, Atari Первый выпуск 26 мая 1998 год … Википедия
Farbrausch — Информация в этой статье или некоторых её разделах устарела. Вы можете помочь проекту, обновив её и убрав после этого данный шаблон … Википедия
Kkrunchy — kkrunchy упаковщик исполняемых файлов Windows. Написан членом группы farbrausch Fabian «ryg» Giesen. Нацелен на сжатие 64 kb интро. Вначале был модифицированной версией UPX, затем стал независимой разработкой. Пользователи Большинство… … Википедия
Беллар, Фабрис — Фабрис Беллар Fabrice Bellard программист Дата рождения: 1972 год(1972) Место рождения … Википедия
Беллар — Беллар, Фабрис Фабрис Беллар Fabrice Bellard программист Дата рождения … Википедия
Фабрис Беллар — Fabrice Bellard программист Дата рождения: 1972 год … Википедия
Mpress — Тип Упаковка исполняемых файлов Разработчик Matcode Software Операционная система Microsoft Windows, Mac OS Последняя версия 2.18 Лицензия Freeware Сайт … Википедия
EXPressor — Тип Упаковка исполняемых файлов Разработчик CGSoftLabs Написана на … Википедия
Упако́вка исполни́мых фа́йлов — состоит в сжатии исполнимого файла и прикреплении к нему кода, необходимого для распаковывания и исполнения содержимого файла. Упаковка производится по ряду причин:
- Упакованный файл занимает меньше места на жёстком диске, что помогает ускорить его загрузку в память
- Некоторые виды упаковки совмещены с шифрованием содержимого файла для того, чтобы предотвратить обратную разработку программы
- Также упаковка с шифрованием может использоваться для вирусописания — для того, чтобы зашифровать и видоизменить код вируса в попытке предотвратить обнаружение этого вируса системами, основанными на сигнатурах (антивирусами, СОВ, и т. п.)
Краткое руководство по анализу
Типовой набор действий банален: определение сигнатуры упаковщика, поиск OEP, дамп программы на диск, восстановление таблицы импорта, восстановление релоков, пересборка. А если же файл не просто был запакован, а еще и обработан протектором, то могут потребоваться дополнительные действия, такие, например, как удаление мусорных инструкций, обход антиотладочных приемов, изоляции функций проверки целостности кода CRC.
Несколько слов о динамических библиотеках. Распаковка DLL практически не отличается от распаковки EXE-файла. У DLL, как и у EXE, есть точка входа в код программы — Entry Point, созданная пакером, и оригинальная OEP. Таким образом, нужно остановиться на DLL в Entry Point, распарсить и оттуда идти к единственно верной OEP нашей DLL. Дальше можно стандартно дампить. И еще пара коротких абзацев из матчасти, которая сегодня нам пригодится.
Ликбез по теории
Итак, как мы понимаем, использование упаковщиков/протекторов/крипторов значительно усложняет реверсинг. Помимо этого, писатели зловредов могут использовать многократную упаковку (так называемый послойный пак), применять малоизвестные или вовсе самописные тулзы (для тех, кто хочет накодить что-то свое, небольшой ликбез), сигнатуры которых будут отсутствовать, к примеру, в том же PEiD. Интересно, что любой пакер, не созданный специально для шифрования малвари, оставляет свою уникальную сигнатуру в бинарнике, а соответственно, умея пользоваться Hex-редакторами, можно определить его сигнатуру и без PE-анализатора.
Общий принцип рассматриваемых инструментов упаковки/защиты таков: после клика на EXE-файле и его запуска выполнение основного кода программы начинается с так называемой точки входа (Entry Point) — адреса, по которому передается управление после загрузки программы в оперативную память. Когда программа запакована, алгоритм работы несколько изменится. Упаковщик запоминает точку входа EP, потом, используя алгоритмы архивирования, сжимает содержимое файла (как правило, это секция кода и данных), после чего дописывает свою сигнатуру после либо до сжатого кода программы и перенаправляет ее не в основной код программы, а в код упаковщика (точнее сказать — распаковщика). Сам же код распаковщика, находящийся теперь внутри файла, получает управление первым и распаковывает упакованные секции кода/данных в памяти! На диске исходный файл остается нетронутым, то есть упакованным, неизменным. После того как код и данные программы распакованы, код распаковщика восстанавливает таблицу импорта и передает управление основному коду программы, на бывшую точку входа, которая в упакованных программах называется оригинальной точкой входа (Original Entry Point). Если кратко, то это все основные моменты.
Схема упаковки исполняемого файла
Другие статьи в выпуске:
Сжатие данных (упаковка) основывается на свойстве энтропии информации, а алгоритмы по своей сути очень схожи с теми, что применяются в архиваторах, только в отличие от первых упаковщики для исполняемых файлов распаковывают данные в оперативную память.
Протекторы, как и некоторые упаковщики, используют ряд приемов борьбы с динамической распаковкой, например расшифровывают код не полностью, а лишь по мере исполнения или создают образ и распаковывают его в память только на момент запуска. Протекторы, используя API-функции, могут определять, что их код запущен под отладчиком, после чего прекращают свою работу. Причиной тому — результат вызова функции API IsDebuggerPresent(), которая определяет, отлаживается программа или нет. Помимо этого, протекторы внедряют процедуры проверки целостности исходного файла, шифруют таблицу импорта, запрещают снятие дампа с определенных адресов виртуальной памяти и иногда используют малодокументированные и недокументированные API-функции, защищающие от трассировки и установки аппаратных точек останова.
Содержание
Загрузчик
Итак, первое, что должен сделать наш загрузчик, — это найти в своем теле адрес массива со сжатым образом PE-файла. Способы поиска зависят от того, как упаковщик имплантировал этот массив в загрузчик.
Например, если бы он просто добавил новую секцию с данными, то поиск выглядел бы так:
Но, на наш с Волком взгляд, этим кодом в загрузчике можно пожертвовать. Вообще, всё, что может сделать упаковщик, пусть он и только он и делает. Адрес образа в адресном пространстве загрузчика можно вычислить заранее, при упаковке, а потом просто вписать в нужное место. Для этого оставляем в нашей программе две метки:
Когда упаковщик будет имплантировать в загрузчик массив со сжатым образом, он пройдется сигнатурным поиском по телу загрузчика и заменит 0xDEADBEEF на адрес массива, а 0xBEEFCACE — на его размер.
Теперь, когда мы определились, как искать адрес, можно выбрать готовую реализацию алгоритма сжатия для использования в нашем упаковщике.
Неплохой вариант — использовать aplib, маленькую библиотеку с аккуратным и очень компактным кодом, реализующую сжатие на базе алгоритма Лемпеля-Зива (LZ). И мы обязательно его выбрали бы в любой другой день, однако сегодня у нас настроение для еще более простого и компактного решения — встроенных в Windows функций!
Начиная с XP, наша любимая ntdll.dll начала экспортировать две прекрасные функции:
Названия их говорят сами за себя — одна функция для компрессии, другая для декомпрессии. Конечно, если бы мы разрабатывали действительно серьезный продукт, мы бы эти функции не трогали, ведь остались еще компьютеры и с Windows 2000, и даже с NT 4.0, 😉 но для наших скромных целей RtlCompressBuffer\RtlDecompressBuffer вполне подойдут.
В хедерах Platform SDK этих функций нет, статически мы их прилинковать не сможем, поэтому придется воспользоваться GetProcAddress:
Когда есть чем распаковать и есть что распаковать, можно уже, наконец, это сделать. Для этого мы выделим память с запасом (так как не знаем объем распакованного файла) и запустим определенную выше функцию:
Параметр COMPRESSION_FORMAT_LZNT1 означает, что мы хотим использовать классическое LZ-сжатие. Функция умеет сжимать и другими алгоритмами, но нам хватит и этого.
Теперь у нас в памяти (pbImage) есть сырой образ PE-файла. Чтобы его запустить, нужно провести ряд манипуляций, которые обычно делает нативный PE-загрузчик Windows. Мы сократим список до самых-самых необходимых:
- Разместить начало образа (хедеры) по адресу, указанному в поле Image Base опционального заголовка (OPTIONAL_HEADER).
- Разместить секции PE-файла по адресам, указанным в таблице секций.
- Распарсить таблицу импорта, найти все адреса функций и вписать в соответствующие им ячейки.
Естественно, стандартный PE-загрузчик выполняет целую кучу других действий, и тем, что мы их отметаем, мы ограничиваем совместимость нашего упаковщика с некоторыми PE-файлами. Но для абсолютного большинства хватит и этих действий — можно не фиксить релоки, фиксапы и прочую редкую и противную фигню.
Если вдруг тебе захочется серьезной совместимости, ты или сам напишешь крутой PE-лоадер, или найдешь в Сети наиболее полную его реализацию — нам с Волком было лень писать свою, и мы воспользовались трудами gr8 из hellknights выкинув из нее всё, что не поняли. 😉 Даже в урезанном виде функция PE-лоадера — это строчек сто, не меньше, поэтому здесь мы приведем только ее прототип (полный код лежит на диске):
Она принимает указатель на наш распакованный образ и возвращает дескриптор загруженного модуля (эквивалент адреса, по которому загружен PE-файл) и адрес точки входа (по указателю AddressOfEntryPoint). Эта функция делает всё, чтобы правильно разместить образ в памяти, но не всё, чтобы можно было, наконец, передать туда управление.
После выполнения функции LoadExecutable в загрузчике неплохо было бы освободить память, выделенную для распаковки, — она нам больше не пригодится.
Дело в том, что система по-прежнему ничего не знает о загруженном нами модуле. Если мы прямо сейчас вызовем точку входа, с которой сжатая программа начнет выполнение, то может возникнуть ряд проблем. Работать программа будет, но криво.
Например, GetModuleHandle(NULL) будет возвращать Image Base модуля загрузчика, а не распакованной программы. Функции FindResource и LoadResource будут рыться в нашем загрузчике, в котором никаких ресурсов нет и в помине. Могут быть и более специфические глюки. Чтобы всего этого не происходило, нужно в системных структурах процесса по возможности везде обновить информацию, заменив адреса модуля загрузчика на адреса загруженного модуля.
В первую очередь нужно пофиксить PEB (Process Enviroment Block), в котором указан старый Image Base. Адрес PEB очень легко получить, в юзермоде он всегда лежит по смещению 0x30 в сегменте FS.
Также не помешает пофиксить списки модулей в структуре LDR_DATA, на которую ссылается PEB. Всего там три списка:
- InLoadOrderModuleList — cписок модулей в порядке загрузки;
- InMemoryOrderModuleList — cписок модулей в порядке расположения в памяти;
- InInitializationOrderModuleList — cписок модулей в порядке инициализации.
Нам надо найти в каждом списке адрес нашего загрузчика и заменить его на адрес загруженного модуля. Как-нибудь так:
Вот теперь можно смело вызывать точку входа загруженного модуля. Он будет функционировать так, словно был вызван самым обычным образом.
AddressOfEntryPoint — это относительный виртуальный адрес (RVA, Relative Virtual Address) точки входа, взятый из optional header в функции LoadExecutable. Для получения абсолютного адреса мы просто прибавили к RVA адрес базы (то есть свежезагруженного модуля).
Процесс отладки бажного файла в OllyDbg
Другие статьи в выпуске:
См. также
Wikimedia Foundation . 2010 .
Переделываем в криптор
Собственно, от криптора наш пакет отличает совсем немногое: отсутствие функции шифрования и противоэмуляционных приемов. Самое простое, что можно с ходу сделать, — это добавить xor всего образа сразу после распаковки в загрузчике. Но, чтобы эмуляторы антивирусов подавились, этого недостаточно. Нужно как-то усложнить задачу. Например, не прописывать ключ xor’а в теле загрузчика. То есть загрузчик не будет знать, каким ключом ему надо расшифровывать код, он будет его перебирать в определенных нами рамках. Это может занять какое-то время, которое есть у пользователя, в отличие от антивируса.
Также ключ можно сделать зависимым от какой-нибудь неэмулируемой функции или структуры. Только их еще найти надо.
Чтобы код загрузчика не палился сигнатурно, можно прикрутить к упаковщику какие-нибудь продвинутые вирусные движки для генерации мусора и всяческого видоизменения кода, благо их в Сети навалом.
Ручная и автоматическая распаковка
С большой долей вероятности все рабочие экземпляры малвари будут запакованы тем или иным упаковщиком/протектором. Но чтобы все-таки убедиться, что файл запакован, запускаем PEiD или любой другой PE-анализатор. В 90% случаев этого будет достаточно, PEiD имеет большую базу данных сигнатур и плагинов, что позволяет обойтись без лишних хлопот.
Дальнейшим шагом станет распаковка файла (восстановление) в его исходный (wild source) вид. И тут есть несколько сценариев действий. Первый — это использовать автораспаковщики, тулзы, специально заточенные под автоматическую распаковку файла, основываясь на уже известном алгоритме упаковщика/протектора. Например, UN-PACK — это анпакер для UPX, ACKiller — для программ, защищенных протектором ACProtect, Stripper — для файлов, запакованных ASProtect, ASPack unp — для накрытых упаковщиком ASPack.
Второй вариант — использовать универсальные распаковщики, например QuickUnpack, RL!dePacker или Dr.Web FLY-CODE Unpacker, основанный на движке FLY-CODE антивируса Dr.Web. Фича программ в том, что они сами автоматически анализируют файл и ищут в нем ОЕР, а после дампят программу (в том числе и импорт восстанавливают). Однако часты случаи, когда сдампленный файл оказывается неработоспособным из-за некорректности его обработки универсальным распаковщиком или из-за изменения алгоритма пакера, который несовместим с тем, что использует универсальный распаковщик. Но есть и плюс: иногда, если файл не удается распаковать до рабочего состояния, секция кода в любом случае получается распакованной, а этого вполне достаточно для анализа.
И третий сценарий, более длительный, но в перспективе более успешный, — ручная пошаговая распаковка с помощью OllyDbg. Если файл запакован чем-то неизвестным, это легко определить по наличию в таблице импорта защищаемого приложения WinAPI-функций из библиотеки kernel, таких как GetProcAddressA, LoadLibraryA или GetModuleHandle.
Рекомендую прочесть статью с подробным описанием всех существующих на сегодня анализаторов, в ней можно ознакомиться с кратким описанием каждого и даже их скачать.
А вот аналогичная страница, но только на этот раз про распаковщики (на всякий случай зеркало тут).
Авторы вредоносного ПО широко используют упаковщики и протекторы для усложнения его детектирования и для противодействия анализу. Большинство из них анализируются стандартным арсеналом инструментов реверс-аналитика, но некоторые требуют нестандартного подхода и глубокого знания PE-архитектуры.
Читайте также: