Главная » Статьи » Распаковка

Распаковка вручную
Снятие упаковщиков приложений

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

Разведка боем

Начнем, как обычно, с начала и продолжим до конца :). А сначала опишу принцип работы упаковщиков и его общие особенности. Упаковщик - программа для сжатия исполняемого файла, которая отличается от архиватора лишь тем, что добавляет код распаковщика в тело программы. При запуске управление передается этому коду вплоть до полной распаковки программы в память. Определение грубое, но как, ни крути, общий смысл выразил. Задача крэкера - слить дамп памяти (не пугайся слов – еще успею объяснить их) распакованной программы, а затем, конечно, восстановить импорт, но все по порядку. Итак, поехали.

Как ты, наверное, уже предполагаешь, а может быть, давно знаешь, существует множество различных пакеров. Естественно, способы распаковки у каждого особенные. Сначала нам предстоит разведка, то есть определение упаковщика, которым сжата программа. Хочу сразу отметить, что это не столь важная деталь, как может показаться. Действительно, когда распакуешь вручную парочку пакеров, тебе уже будет безразлично то, как называется пакер, ранее бывший неизвестным для тебя. Принцип их действия, по сути, одинаков: если узнал, как работает один, значит, ты понял, как работают все. Распознавание языка, на котором написана программа, также то, каким упаковщиком она ужата, осуществляется сигнатурным способом, который так полюбился нашим антивирусным конторам. На эту тему существуют замечательные статьи Alex'а на не менее замечательном портале cracklab.ru. Конечно, если ты сейчас с интересом читаешь эту статью, значит, ты еще многого не знаешь, и специально для новичков изобретена отличная в своем роде программа-распознаватель PEiD, которая не только покажет, чем упакован наш файл, но и расскажет еще много интересного о жертве.

Обзор всех инструментах, необходимых реверсеру, также написано в этом номере, поэтому время поговорить о программах еще будет. А сейчас мы окунемся в прошлое и предадимся классике. Внимание! Распаковываем блокнот, упакованный UPX. И что бы там нам ни говорили, что якобы веселее распаковывать FSG, блокнот и UXP - это классика, от которой никуда не деться. И наш урок начнется именно с нахождения Оригинальной точки входа в сжатую программу.

Для общего разъяснения скажу, что после запуска программы с физического носителя операционная система проецирует образ программы в оперативную память (точнее, в определенное адресное пространство памяти). В этом случае сам код располагается в памяти с началом по адресу так называемого значения Image base (то есть Image Base - адрес в памяти, начиная с которого идет код программы). Однако начало проекции совсем не означает начало исполняемого кода. И вот уже в проекции существует Точка входа (EP - Entry Point) в программу - то место, откуда начинается выполнение исходника. Поскольку я уже упоминал, что упаковщик добавляет свой код в тело программы, то на сцене появляется понятие Оригинальная тока входа. Как уже можно было догадаться, это точка входа в изначальную программу, если бы она не была запакована. Естественно, нам необходимо найти это место, снять дамп памяти (dump - снимок/образ памяти) исходной программы и сохранить его на диск. Мне не хочется делать из тебя бездумного робота-шаблонщика, поэтому попытаюсь донести до глубин твоего сознания суть процесса.

Нахождение OEP. Теория

Как мы уже знаем, код распаковщика предшествует коду ужатой программы, следовательно, нам необходимо первым шагом найти переход на OEP. Если ты знаком с языком ассемблер (на первое время достаточно азов), то тут будет понятнее. Для перехода на OEP в стандартных пакерах используются, как правило, несколько способов: безусловный переход на OEP, который используется в большинстве простых упаковщиках, предназначенных для простого сжатия программ, но никак не защита от взлома. Например, наш UPX делает именно вот так и никак не иначе.


0101185E > 61 POPAD

0101185F .-E9 BC4BFFFF JMP NOTEPAD.01006420

01011864 00 DB 00

01011865 00 DB 00

01011866 00 DB 00

01011867 00 DB 00

В ASPack'e от нашего соотечественника используется несколько другой метод.

010103AF 61 POPAD

010103B0 75 08 JNZ SHORT NOTEPAD1.010103BA

010103B2 B8 01000000 MOV EAX,1

010103B7 C2 0C00 RETN 0C

010103BA 68 20640001 PUSH NOTEPAD1.01006420

010103BF C3 RETN

Как видишь, адрес ОЕР кладется в стек PUSH NOTEPAD1.01006420, а затем командой RET производится по адресу в стеке. Что и требовалось доказать, друзья.

Теперь поподробнее: как я нашел переходы и почему в большинстве случаев перед вызовом ОЕР находится команда POPAD, что заметили все начинающие реверсеры, хотя мало кто из них придал значение этому факту.

Дело в том, что для корректной работы программы важным и необходимым условием является гармония адресного пространства. После прохождения распаковки и до передачи управления исходной программы код пакера должен вернуть значения регистров, а главное - стека в первоначальное состояние. Именно поэтому в начале кода распаковщика всегда стоит (как правило!) команда PUSHAD, которая указывает процессору поместить значения регистров в стек. Команда POPAD существует для противоположной операции. Это можно наглядно посмотреть в программе, упакованной UPX: после возвращения стека в первозданное состояние происходит прыжок на ОЕР. Если просто смотреть листинг дизассемблера, то после перехода на ОЕР мы увидим непонятные наборы символов (назвать их кодом сложновато) либо лишь пустую область.

Однако этот способ действует только на очень простых пакерах наподобие UPX. В случае с ASPack'ом, как мы видим, такое уже не подойдет. Поэтому лучше перейдем к практике распаковки простейших упаковщиков. Нам понадобится установленный SoftIce. Если у тебя по какой-либо причине нет вышеприведенного отладчика, то можешь пользоваться любым другим - ход действий остается таким же. Мне SoftIce ближе к сердцу, и советую пользоваться именно им или OllyDbg, которым я пользовался, как ты уже понял, только ради скриншотов и наглядности кода.

Нахождение OEP. Практика

Итак приступим. Для тех, кто работает с OllyDbg, остановиться на EP программы не составит труда - нужно просто открыть файл в отладчике. Но поскольку мы работаем с SoftIce'ом, тут необходимо знать некоторые тонкости. Итак, сейчас мы научимся прерываться на точке входа в SoftIce с помощью великолепной программы PETools, о которой можно прочесть в другой статье этого номера. Этот способ широко известен всем: лично я научился ему после прочтения статьи хорошо известного в своих кругах MozgC (TSRh), за что ему отдельное спасибо. Теперь следим за рукой: запускаем PETools, далее в меню Tools выбираем Break&Enter и указываем нужный нам файл. Затем появляется простое окошко с сообщением, что необходимо запустить SoftIce, ввести команду bpint3, после ее прохождения нажать кнопку ОК в окошке и ввести следующую команду типа e eip 0xХХ. На самом деле все намного проще, чем кажется. При использовании Break&Enter PETools заменяет первый байт точки входа на байт CCh (то есть специальное отладочное прерывание int3, которое генерирует исключение), мы уже ставим в отладчике бряк (breakpoint - точка остановки) на это прерывание (bpint3), то есть, грубо говоря, мы помечаем по-своему точку входа. Когда отладчик останавливает программу на этом месте, команда "eb eip 0xХХ" (где ХХ - определенные байты, которые были на точке входа) записывает оригинальные байты точки входа, тем самым приводя все в первоначальный вариант. Остается только снять все бряки командой "bc *" и работать дальше.

EP (Entry Point) - точка входа в программу, располагающуюся в адресном пространстве.


OEP (Original Entry Point) - точка входа в исходную программу, логично только когда программа упакована. Тогда ЕР - точка входа в код упаковщика.

Image Base - адрес начала размещения программы в памяти. Не путать с точкой входа! Точка входа - это адрес старта программы, а Image Base - адрес начала всего кода.

RVA (Relative Virtual Address)
- относительный виртуальный адрес, адрес смещения относительно Image Base.

VA (Virtual Address) - виртуальный адрес, адрес в памяти, который нам показывает отладчик.

А дальше мы должны рассуждать, как найти ОЕР. Если думать трезво и вспомнить уроки учителей, то мы узнаем, что после исполнения своего долга упаковщик, как истинный мужчина, восстанавливает все в первоначальное состояние. При стандартном начале Windows-программ указатель на верхнюю часть стека один и тот же и равен значению регистра esp. Когда пакер передает управление первоначальной программе, указатель восстанавливается, но перед этим, как правило, упаковщик считывает из стека значение esp-4. Поскольку 98% простых упаковщиков одинаковы в своих принципах, мы имеем право утверждать, что перед переходом на ОЕР будет взято значение из стека, равное esp-4. Следовательно, вызываем отладчик и пишем bpm esp-4. После нажатия в случае с UPX мы сразу же прерываемся на команде POPAD и последующем переходе на ОЕР. Не всегда первая остановка будет верна: тут необходимо смотреть переходы, и если они есть, значит, мы пришли правильно. И вот мы на заветном прыжке на ОЕР. Это означает то, что сейчас в памяти лежит распакованная программа с точкой входа, которую мы только что нашли. Остается только слить дамп на диск и восстановить импорт. Наверное, все уже поняли, что для того чтобы нормально слить дамп, нужно остановить программу на месте прыжка на ОЕР.

Снятие дампа

Для тех, кто пользуется OllyDbg, никакого труда не составит поставить breakpoint на команде перехода (всего лишь нажав ) и запустить программу до срабатывания точки останова. Любителям SoftIce придется немного поколдовать. Тут мы уже просто зациклим программу на этом месте путем замены байт, да SoftIce и это может :). Итак, двигаемся на команду прыжка и вводим команду "a", что означает ввод ассемблерных команд, а затем пишем jmp eip. Применяем магическим 'ом, и теперь наша команда делает бесконечные прыжки на себя. Как видишь, ничего сложного нет. Вообще самое сложное в распаковке упаковщиков - это нахождение ОЕР.

Отладчики запущены, программы висят на прыжках на точку входа, остается только снимать дамп. Вопрос чем? Конечно, сейчас есть уйма плагинов как для SoftIce, так и для OllyDbg, однако ими я не пользуюсь, ибо все это от лукавого. Существуют две замечательные программы PETools и LordPE, первую сегодня мы уже использовали, значит, снимать дамп будем второй. Вообще принципиальной разницы в этих программах нет, но нужно учиться всему и знакомиться со всеми. После запуска LordPE находим в списке наш зацикленный процесс, правый клик на нем и full dump - что может быть проще? Сохраняем дамп на диск. Ура! Наконец-то наш файл готов... но что это? Он не запускается и выдает ошибку. А это потому, что наш новоявленный блокнот не знает, какие же функции ему использовать. Нам предстоит еще восстановить таблицу импорта. Кстати, если мы уже сдампили программу, то можно закрывать отладчики. Для тех, у кого отладчик ядра в LordPE, выбираем программу и в контекстном меню нажимаем burn process.

Восстановление импорта


Импорт мы восстановим с помощью еще одной замечательной программы Import Recontructor. Запустим упакованный блокнот, затем найдем его в листе процессов ImpRec'а. Теперь нам необходимо указать RVA OEP (в ImpRec он просто OEP). Не пугайся слов: на врезке есть объяснение всему. Вот формула "RVA OEP = VA OEP - ImageBase". Ее нужно запомнить крепко. Image Base мы сможем найти нажав на кнопку PE Editor в LordPE. В нашем случае RVA = 01006420 – 01000000 = 6420. Вводим это значение в поле OEP и жмем автопоиск, то есть IAT AutoSearch. После этого нажимаем Get Imports. Мы должны увидеть строки с функциями и надписью YES напротив. Если все так, а так и должно быть, то кнопка Fix Dump открывает нам дверь на путь истинный. Останется только указать наш дамп и после нажатия на заветную кнопку ОК наслаждаться распакованным блокнотом.

Однако не всегда все так просто и не всегда автопоиск спасает. Поэтому напоследок упомяну некоторую хитрость, которая уже давно используется у реверсеров. Если мы попробуем таким же образом поступить с файлом, упакованным упаковщиком с небольшой степенью защиты, то функции мы не обнаружим, в этом случае придется искать RVA самостоятельно. Как известно, функции в исполняемом файле находятся в виде адресов. Упаковщик сохраняет всю таблицу. Следовательно, по адресам в простом HEX-редакторе мы сможем обнаружить таблицу импорта в упакованном файле. Но для этого нам необходимо знать хотя бы одну функцию, которая присутствует в файле, и ее адрес. GetModuleHandleA встречается практически во всех Windows-программах, а адрес функции легко узнается с помощью отладчика. Достаточно ввести в SoftIce "exp GetModuleHandleA" и получить ответ 77E87D93 (или в OllyDbg просто поставить бряк на эту функцию, дойти до нее и прочитать внизу DS:[01001094]=77E87D93 (KERNEL32.GetModuleHandleA) =)). При поиске в НЕХ-редакторе не забывай, что все адреса хранятся в обратном порядке, то есть искать мы будем 937DE877. У тебя, возможно, будет другое число. Уверяю, что ты точно не пропустишь место таблицы импорта. Запускаем опять PE Editor, выбираем наш дамп и жмем FLC. Затем вычисляем RVA, для этого нажимаем на кнопку offset и вводим найденный нами адрес. После нажатия кнопки DO в поле RVA будет то, что мы искали. На глаз прикинем размер таблицы импорта. Теперь все так же, только без автопоиска. Если мы были правы (а мы были правы!), то появится уйма функций. Удалим те, что с надписью NO, и фиксируем снимок (Fix Dump). Наслаждаемся и читаем врезки.

Вместо заключения

Надеюсь, чтение этой статьи утяжелило твой багаж знаний, и ты встал на истинный путь реверсера. Может быть, ты, наоборот, вспомнил молодость, когда еще стоял на распутье, и теперь с ностальгией читаешь слова заключения, а на твоем винте уже лежат рабочие дампы армы.
Категория: Распаковка | Добавил: -=Hellsing=- (17.06.2009) | Автор: Shturmovik ХакерСПЕЦ №57
Просмотров: 4927 | Рейтинг: 5.0/1
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]