Оригинальный DVD-ROM: eXeL@B DVD !
eXeL@B ВИДЕОКУРС !

ВИДЕОКУРС ВЗЛОМ
выпущен 10 декабря!


УЗНАТЬ БОЛЬШЕ >>
Домой | Статьи | RAR-cтатьи | FAQ | Форум | Скачать | Видеокурс
Новичку | Ссылки | Программирование | Интервью | Архив | Связь


Распаковка ExeCryptor 2.3.9 (на примере PrevedSMS 5.1), часть 2




Очень удобно, когда все крэкерские инструменты, книги и статьи в одном месте. Используйте сборник от EXELAB - вот тут.

Восстановление IAT ExeCryptor.


Теперь переходим к восстановлению таблицы IAT. Сначала нам надо найти начало и конец IAT. В программах, написанных на Delphi, доступ к таблице IAT выполняется в виде прыжка, имеющего первые байты опкода - FF 25. Давайте, выполним двоичный поиск этих байтов, чтобы найти прыжки в таблицу IAT:


Распаковка ExeCryptor 2.3.9: статья 2


Нажимаем кнопку ОК, и оказываемся здесь:


Распаковка ExeCryptor 2.3.9: статья 2


Как видим, таблица IAT расположена в секции, которая начинается по адресу 0062D000, и имеет размер 3000h байтов:


Распаковка ExeCryptor 2.3.9: статья 2


Проходим в окне dump отладчика на начало секции 0062D000, и смотрим, что там имеется. Начало секции импорта заполнено нолями, а затем идут какие-то адреса:




Распаковка ExeCryptor 2.3.9: статья 2


Мы видим несколько групп адресов, отделенных нолями друг от друга. Первая группа адресов очень похожа на переадресованные API. Давайте проверим наше предположение. Выделяем содержимого первого адреса 0062D190, и нажимаем клавиши Ctrl + R, выполняя тем самым поиск ссылок на адрес 0062D190:


Распаковка ExeCryptor 2.3.9: статья 2


Дважды щелкаем по ссылке, и попадаем сюда:


Распаковка ExeCryptor 2.3.9: статья 2


Здесь вызывается какая-то API, для получения реального адреса которой используется подпрограмма раскриптовки, которая находится по адресу 00AD2375. Поэтому мы можем сказать, что начало таблицы IAT находится по адресу 0062D190.


Теперь находим конец таблицы IAT. Для этого прокручиваем листинг вниз, пока не начнется мусорный код или ноли:


Распаковка ExeCryptor 2.3.9: статья 2


Если имеются какие-либо сомнения в правильности определения конца таблицы IAT, всегда нужно проверять наличие ссылок в области кода на эти адреса (как это мы делали выше). Конец таблицы IAT находится по адресу 0062D9AC.


Таким образом:

Начало IAT: 0062D190

Конец IAT: 0062D9AC


Размер IAT: 0062D9AC - 0062D190 = 81Сh байтов


Здесь мы получили необходимые данные для ImpREC.


Теперь наша задача заключается в поиске магического прыжка для восстановления IAT. Для этого нам нужно проанализировать процесс поиска и сохранения правильного адреса API в таблице IAT пакером ExeCryptor.


Но, перед этой работой, нам нужно получить некоторую дополнительную информацию. Как Вы помните, при поиске WOEP, мы сделали дамп памяти запущенной программы. При этом, естественно, были восстановлены и записаны в таблицу IAT какие-то фактические адреса API. Открываем дамп памяти во втором отладчике, и переходим на начало таблицы IAT:


Распаковка ExeCryptor 2.3.9: статья 2


Сопоставляя таблицу IAT упакованной оригинальной программы с дампом памяти программы, мы видим, что пакер восстановил и записал реальные адреса многих API, что выделено на верхнем рисунке красным прямоугольником.


Поэтому, нам надо найти, как ExeCryptor находит и сохраняет в таблице IAT правильное значение адреса API.


Перезагружаем программу, и проходим на WOEP, как это мы делали раньше. Как мы видим на верхнем рисунке, программа, при своем запуске, записывает реальное значение API VirtualFree по адресу 0062D1A0 (мы можем взять любую API, реальный адрес которой пакер записывает в таблицу IAT). Подавляем все потоки защиты, после чего устанавливаем MEMORY BREAKPOINT ON WRITE на адрес 0062D1A0, по которому будет записан реальный адрес API VirtualFree. Нажимаем клавишу F9, и программа останавливается здесь:


Распаковка ExeCryptor 2.3.9: статья 2


Здесь программа записывает реальный адрес API в соответствующий адрес таблицы IAT, и, если мы нажмем клавишу F8, то вместо переадресованного значения API мы получим ее реальный адрес:


Распаковка ExeCryptor 2.3.9: статья 2


Немного потрассируем подпрограмму раскриптовки API с помощью клавиши F7, чтобы посмотреть, что будет дальше делать пакер. После сохранения реального адреса API, программа записывает байт C3 по адресу 00B3D47F, который она загружает с помощью инструкции LEA EAX,DWORD PTR DS:[B3D47F]:


Распаковка ExeCryptor 2.3.9: статья 2


Эта инструкция RET указывает на адрес, куда должна прыгнуть подпрограмма раскриптовки:


Распаковка ExeCryptor 2.3.9: статья 2


В справочном окне OllyDbg мы видим адрес прыжка программы - 00B3D46F. Продолжаем трассирование программы с F7, и попадаем сюда:


Распаковка ExeCryptor 2.3.9: статья 2


Поскольку сам пакер написан на Delphi, то мы видим, как он выполняет прыжок на реальный адрес API - 7C809B14 (прыжок на API с опкодом FF25 используется программами, написанными на Delphi).


Возникает вопрос, зачем пакер заменяет исходную инструкцию JNZ 00B70667, которая находится по адресу 00B3D47F, инструкцией RETN:




Распаковка ExeCryptor 2.3.9: статья 2


Ответ очевиден! Он это делает для того, чтобы дважды не выполнять ту же самую подпрограмму раскриптовки, поскольку уже записан реальный адрес API в таблице IAT. В противном случае, работа программы очень бы сильно тормозилась многократным выполнением одних и тех же подпрограмм пакера.


Однако возникает следующий вопрос, как пакер определяет корректные значения адресов APIs? Давайте перезагрузим программу, подавим все потоки защиты, пройдем на WOEP, и установим BPM on access на всю область таблицы IAT:


Распаковка ExeCryptor 2.3.9: статья 2


Это позволит нам остановить программу при вызове любой API. Запускаем программу, нажав клавишу F9, и программа останавливается здесь:


Распаковка ExeCryptor 2.3.9: статья 2



Теперь пакер будет определять реальный адрес переадресованной API, находящейся по адресу 0062D294 таблицы IAT. Поскольку, как мы видели раньше, пакер производит запись в своей секции, заменяя инструкцию JNZ 00B70667 инструкцией RET, то давайте установим BPM ON WRITE на всю эту секцию, чтобы посмотреть, что в ней изменяется:


Распаковка ExeCryptor 2.3.9: статья 2


Также установим BP на адресе возврата из этой API, чтобы не запустилась программа (адрес возврата 00407521 мы подсмотрели из стека):


Распаковка ExeCryptor 2.3.9: статья 2


Нажимаем клавишу F9, и программа останавливается здесь:


Распаковка ExeCryptor 2.3.9: статья 2


Пакер записывает значение 75 (‘u’) по адресу 00B23C24. Нажимаем несколько раз клавишу F9, и видим следующее:



Распаковка ExeCryptor 2.3.9: статья 2


По адресу 00B23C24 пакер записал имя библиотеки user32.dll, в которой программа будет выполнять поиск API. В конце имени библиотеки, выполнив инструкцию MOV BYTE PTR DS:[EAX],0 по адресу 00B3F78F, пакер записал конечный 0, который завершает текстовую строку. Опять нажимаем клавишу F9, и программа останавливается здесь:


Распаковка ExeCryptor 2.3.9: статья 2


Теперь пакер записывает байт D5 вместо байта 75 (буква u), и это действие продолжается до тех пор, пока пакер полностью не затрет уже использованное имя библиотеки. Продолжаем нажимать клавишу F9:


Распаковка ExeCryptor 2.3.9: статья 2


Здесь программа, по адресу 00B23C04, сохраняет базовый адрес библиотеки user32.dll, который на моей машине равен 77D30000. Очевидно, что нет необходимости каждый раз определять базовый адрес нужной библиотеки; достаточно его определить только один раз, и, затем использовать полученный базовый адрес для поиска реального адреса всех остальных API, входящих в данную библиотеку.


Нажимаем опять клавишу F9, и программа останавливается здесь:


Распаковка ExeCryptor 2.3.9: статья 2


Чуть выше, мы видим инструкцию MOV DWORD PTR DS:[62D254],EAX, которая записала по адресу 0062D254 реальный адрес API GetKeyboardType. И программа остановилась на записи инструкции RETN вместо инструкции POP EDX, которая сейчас находится по адресу 00B1232C:


Распаковка ExeCryptor 2.3.9: статья 2


Т.е. происходят уже знакомые нам действия.


Подводим краткие итоги работы пакера по поиску и записи реального адреса API в таблицу IAT:


  1. Сначала пакер определяет базовый адрес библиотеки .dll, в которой находится нужная для работы программы API, и записывает его по указанному пакером адресу;

  2. Затем пакер определяет реальный адрес данной API, и записывает этот адрес в регистр EAX;

  3. После этого пакер записывает найденный реальный адрес API в таблицу IAT;

  4. После записи реального адреса API в таблицу IAT, пакер закрывает подпрограмму раскриптовки реального адреса данной API с помощью инструкции RETN


Теперь нам надо определиться, как работает пакер с теми API, реальный адрес которых он не записывает в таблицу IAT, а сохраняет переадресованный адрес. Можно в таблице IAT дампа памяти программы найти переадресованную API, но мы поступим проще. Будем нажимать клавишу F9, и смотреть, где будет останавливаться пакер. При первом же нажатии клавиши F9, программа останавливается здесь:


Распаковка ExeCryptor 2.3.9: статья 2


По адресу 00B04ED8, пакер записывает значение D4E2A577, являющееся хэшем API GetCommandLineA (адрес этой API можно получить при дальнейшем трассировании программы), которая входит в библиотеку kernel32.dll. API GetCommandLineA вызывается программой только один раз, поэтому мы здесь не увидим ничего интересного.


Нажимаем еще раз клавишу F9, и программа останавливается здесь:


Распаковка ExeCryptor 2.3.9: статья 2


По адресу 00B6F74C, пакер записывает значение 50D98217, являющееся хэшем API GetStartupInfoA(адрес этой API получаем при дальнейшем трассировании программы), которая также входит в библиотеку kernel32.dll. Но эта API программой вызывается два раза, что можно увидеть, выполнив в коде пакера поиск ссылок на константу 00B6F74C:


Распаковка ExeCryptor 2.3.9: статья 2


Зачем нужен этот адрес 00B6F74C? Давайте пройдем на адрес 00ADE69F, дважды щелкнув по нему. Здесь мы видим следующее:


Распаковка ExeCryptor 2.3.9: статья 2


При очередном вызове API GetStartupInfoA, пакер не будет выполнять все те действия, которые мы рассмотрели выше, а будет выполнять проверку содержимого адреса 00B6F74C на наличие ноля (инструкция OR EAX,EAX). Если по этому адресу будет записан ноль, то пакер не будет делать условный прыжок на адрес 00ADD391, а пройдет на безусловный прыжок на адрес 00AE0313. Именно по этому адресу начинается подпрограмма определения реального адреса API GetStartupInfoA. Если же по этому адресу будет записано какое-то значение, будет выполнен условный прыжок на адрес 00ADD391, где начинается подпрограмма раскриптовки реального адреса API GetStartupInfoA с помощью значения, записанного по адресу 00B6F74C.


Если немного потрассируем программу с F7, то попадем сюда:


Распаковка ExeCryptor 2.3.9: статья 2


После выполнения инструкции SUB EAX,ESI, в регистре EAX мы видим реальный адрес API GetStartupInfoA:


Распаковка ExeCryptor 2.3.9: статья 2


И здесь, как мы видим, нет записи инструкции RETN вместо какой-либо инструкции, поскольку реальный адрес API GetStartupInfoA пакер не записывает в таблицу IAT.


Давайте подведем краткие итоги работы пакера по поиску реального адреса API, без его записи в таблицу IAT:


  1. Сначала пакер определяет базовый адрес библиотеки .dll, в которой находится нужная для работы программы API, и записывает его по указанному пакером адресу;

  2. Затем пакер определяет наличие значение по адресу, выделенному пакером для данной API. Если по этому адресу записан ноль, условный прыжок не выполняется, и пакер переходит на подпрограмму вычисления хэша для этой API;

  3. Далее пакер определяет хэш этой API, и записывает его по указанному адресу;

  4. Затем пакер, с помощью записанного хэша, выполняет вычисление реального адреса этой API;

  5. При последующих вызовах этой API, пакер проверяет наличие ноля в адресе, выделенном для записи хэша API. И, т.к. в проверяемом адресе записано значение хэша API, пакер выполняет прыжок на подпрограмму вычисления, с помощью записанного хэша, реального адреса данной API.


Я постарался подробно описать механизм работы пакера по определению реального адреса переадресованных APIs.


Но остался самый главный вопрос - мы еще не нашли место в пакере, где определяются реальные адреса всех переадресованных API, которое выше мы назвали магическим прыжком. Где же производится запись в регистр EAX реального адреса API?


Как мы видели ранее, при выполнении инструкции MOV DWORD PTR DS:[ESI],EAX, пакер записывал по адресу 0062D1A0 реальный адрес API VirtualFree. Причем запись этого адреса выполнялась здесь:


Распаковка ExeCryptor 2.3.9: статья 2


Поскольку программа у нас остановлена на OEP, переходим на вызов инструкции API VirtualFree, на который устанавливаем BP:


Распаковка ExeCryptor 2.3.9: статья 2


Запускаем программу, нажав клавишу F9, и программа останавливается на BP. Переходим на адрес 00650B6E, на котором находится инструкция записи реального адреса API в таблицу IAT, и устанавливаем на него BP. Нажимаем клавиши Ctrl + F11. Трассирование этой подпрограммы пакера занимает довольно продолжительное время (на моей машине потребовалось около 8 минут, поэтому можно спокойно попить кофе), и программа останавливается на BP. Переходим в окно трассирования, нажав кнопку ‘…’, и прокручиваем полученный листинг вниз, до самого конца:


Распаковка ExeCryptor 2.3.9: статья 2


И немного выше последней инструкции, на которую мы установили BP, видим нужную для нас инструкцию MOV EAX,DWORD PTR SS:[EBP-C], которая находится по адресу 0064349B, при выполнении которой, в регистр EAX записывается реальный адрес API. Устанавливаем на эту инструкцию Hardware Breakpoint, чтобы посмотреть, выполняется ли эта инструкция для остальных элементов IAT. Немного поэкспериментировав с разными вызовами API (с теми, которые записываются в таблицу IAT, и с теми, которые не записываются в таблицу IAT), мы видим, что эта инструкция всегда выполняется, и здесь в регистр EAX записывается реальный адрес API.


Теперь нам осталось применить полученные сведения для восстановления таблицы IAT. Естественно, что мы хотим автоматизировать процесс восстановления IAT с помощью скрипта.


Ранее, в своей статье “ExeCryptor v2.2.4 - Распаковка по vnekrilov (RAR Repair Tool)”, которую можно скачать в разделе RAR-статьи сайта CRACKL@B, я подробно описал процесс разработки скриптов для восстановления таблицы IAT. В этой статье я не буду повторять все то, что я написал в предыдущей статье, а приведу только конечный скрипт, снабдив его подробными примечаниями.



  // Разработчик скрипта: vnekrilov.
  // Описание: Скрипт разработан для восстановления реальных адресов API в  
  // таблице IAT для программы PrevedSMS v5.1, защищенной  
  // ExeCryptor v2.3.9.
  // Цель: preved.exe
  // Условия применения: ODBGScript 1.48, Olly Advanced v1.26 Beta 12, PhantOm v1.15
  // Программа должна быть остановлена на OEP, все BP должны быть удалены
 
var table // Переменная для хранения адресов таблицы IAT var containing // Переменная для хранения содержимого таблицы IAT var Temp_EAX // Переменные для хранения значения регистров, когда var Temp_ECX // программа остановлена на OEP var Temp_EDX var Temp_EBX var Temp_ESP var Temp_EBP var Temp_ESI var Temp_EDI var Temp_EIP
mov Temp_EAX,eax // Сохраняем в переменных значения регистровЮ когда mov Temp_ECX,ecx // программа остановлена на OEP mov Temp_EDX,edx mov Temp_EBX,ebx mov Temp_ESP,esp mov Temp_EBP,ebp mov Temp_ESI,esi mov Temp_EDI,edi mov Temp_EIP,eip
mov table,0062D190 // Указываем начало таблицы IAT
start: cmp table,0062D9AC // Проверяем конец таблицы IAT ja finish // Если конец таблицы IAT, прыгаем на завершение скрипта cmp [table],9000000 // Проверяем содержимое IAT на наличие реальных адресов API ja jumped // Если в IAT записан реальный адрес API, переходим на // следующий элемент таблицы IAT
mov containing,[table] // Копируем содержимое элемента IAT cmp containing,0 // Проверяем на разделительные нули между dll je jumped // Если имеются нули, переходим на // следующий элемент таблицы IAT log table // регистрируем адрес, и его содержимое log containing
mov eip,containing // Устанавливаем eip на адрес таблицы IAT bphws 0064349E, "x" // Устанавливаем HBP на адрес, после получения API cob restoring run // Запускаем программу
restoring: bphwc 0064349E // Удаляем HBP log eax // Регистрируем реальный адрес API mov [table],eax // Записываем в IAT реальный адрес API
mov eip,Temp_EIP // Восстанавливаем оригинальные значения регистров mov edi,Temp_EDI mov esi,Temp_ESI mov ebp,Temp_EBP mov esp,Temp_ESP mov ebx,Temp_EBX mov edx,Temp_EDX mov ecx,Temp_ECX mov eax,Temp_EAX
jumped: add table,4 // Переходим на следующий элемент IAT jmp start // Повторяем цикл
finish: ret // Завершаем работу скрипта


Перезагружаем программу, проходим на WOEP, и запускаем скрипт. Через несколько минут, скрипт заканчивает свою работу:


Распаковка ExeCryptor 2.3.9: статья 2


Давайте, на всякий случай, сохраним восстановленную таблицу IAT, для чего переходим в окно dump отладчика, выделяем всю таблицу IAT, и выполняем команду бинарного копирования:


Распаковка ExeCryptor 2.3.9: статья 2


Скопированные байты сохраняем в файле с помощью Hex Workshop v4.23, для чего выбираем команду Файл → Новый:


Распаковка ExeCryptor 2.3.9: статья 2



Теперь выбираем команду Правка → Специальная вставка:


Распаковка ExeCryptor 2.3.9: статья 2


Появляется диалоговое окно:


Распаковка ExeCryptor 2.3.9: статья 2


Нажимаем кнопку ‘Вставить’, и получаем файл, заполненный байтами нашей таблицы IAT:


Распаковка ExeCryptor 2.3.9: статья 2


Сохраняем этот файл с именем Recovery_IAT.



3. Получение дампа распакованной программы.


Теперь можно сдампировать память программы. Для этой цели я использую plugin OllyDbg PE Dumper v3.03 by FKMA:


Распаковка ExeCryptor 2.3.9: статья 2


Сохраняем полученный дамп памяти с любым именем, (я его сохранил с именем dumped).


Не закрывая Olly, запускаем утилиту ImpREC, выбираем процесс, и вводим следующие данные:


OEP : 00407510 - 00400000 = 00007510

RVA: 0062D190 - 00400000 = 0022D190

SIZE: 0062D9AC - 0062D190 = 81Сh байтов


Нажимаем кнопку “Get Imports”, и в окне “Imported Functions Found” видим:


Распаковка ExeCryptor 2.3.9: статья 2


Все API были распознаны!!!


Теперь нам надо настроить опции восстановления таблицы IAT утилитой ImpREC, для чего нажимаем кнопку “Options”. По умолчанию, утилита ImpREC имеет следующие настройки для восстановления таблицы IAT:

Распаковка ExeCryptor 2.3.9: статья 2


Первая опция “Import all by Ordinal” предусматривает запись в таблице IAT порядковых номеров API в соответствующих библиотеках, вместо их названий. Это позволяет значительно уменьшить размер таблицы IAT, но такой дамп нельзя будет запустить на других операционных системах (например, Windows 9xx). Для таких случаев, лучше отключить эту опцию, и записывать API в таблицу IAT по их именам.


Но, отключение этой опции увеличивает таблицу IAT до размера, который превышает размер нашей секции IAT:


Распаковка ExeCryptor 2.3.9: статья 2


Как видно на рисунке, размер таблицы IAT увеличивается с 12C6h байтов до 34EEh байтов; а секция таблицы IAT занимает 3000h байтов. Поэтому удаляем флажок из опции “Rebuild Original FT”, и видим, что размер таблицы IAT уменьшается до2CCEh байтов:


Распаковка ExeCryptor 2.3.9: статья 2


И теперь нам надо восстановить таблицу IAT в файле dumped.exe. В этой программе будем вставлять таблицу IAT с начала секции, по адресу 0062D000, но, предварительно, нужно очистить данную секцию в файле dumped.exe от имеющегося там кода, поскольку, при записи поверх этого кода, утилита ImpREC записывает неправильные адреса таблицы IAT.


Чтобы открыть файл dumped.exe в отладчике OllyDbg, необходимо, с помощью утилиты PE Tools v1.5 RC7, удалить данные из параметра Import Directory:





Распаковка ExeCryptor 2.3.9: статья 2


Сохраняем эти изменения, и открываем файл dumped.exe во втором отладчике OllyDbg (первый отладчик с загруженной оригинальной программой мы не закрываем, поскольку он нужен для восстановления таблицы IAT в файле dumped.exe). Проходим на область памяти, которая начинается с адреса 0062D000 (здесь у нас расположена таблица IAT), нажимаем клавишу Shift, и выделяем всю область памяти, до ее конца. Щелкаем правой кнопкой мыши по выделенному коду, и выбираем следующую команду:



Распаковка ExeCryptor 2.3.9: статья 2


Сохраняем сделанные изменения в файле dumped.exe, и закрываем второй отладчик.


Переходим в ImpREC, удаляем флажок с опции “Add new section”, и в окно RVA вводим значение начала таблицы IAT - 0022D000:


Распаковка ExeCryptor 2.3.9: статья 2


Нажимаем кнопку “Fix Dump”, выбираем файл dumped.exe, и через пару минут, утилита ImpREC завершает свою работу:


Распаковка ExeCryptor 2.3.9: статья 2


Пробуем загрузить наш дамп в отладчик, и он нормально загружается. При этом программа сначала останавливается на TLS Callback, а затем - на WOEP программы:




Распаковка ExeCryptor 2.3.9: статья 2


Однако запускать программу еще нельзя, поскольку у нас не восстановлен код OEP, и не сделаны некоторые изменения в PE-header программы.


4. Восстановление OEP в дампе распакованной программы.


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


Как мы уже знаем, стандартная Entry Point программ Delphi имеет следующий вид:



  PUSH 	EBP	← OEP  
  MOV 	EBP,ESP
  ADD 	ESP,-const	← Это - константа для смещения стека.  
  MOV 	EAX,address	← Это - первая инструкция при запуске программы.   
  CALL 	Sysinit::initexe Procedure ← Это - первая процедура ЛЮБОЙ программы Delphi  
  


Загружаем оригинальную упакованную программу в отладчик, проходим на Entry Point пакера, и подавляем два потока защиты, установленных API CreateThread. Устанавливаем Hardware BreakPoint on access на ранее найденный адрес WOEP - 00407510.


Когда программа остановлена на Entry Point отладчика, ее стек имеет следующий вид:


Распаковка ExeCryptor 2.3.9: статья 2


А регистры имеют следующие значения:


Распаковка ExeCryptor 2.3.9: статья 2


В соответствии с кодом стандартной Entry Point, первой выполняемой инструкцией является инструкция PUSH EBP; т.е., при выполнении этой инструкции, в стек должно быть записано значение регистра EBP = 0012FFF0, и оно должно быть записано по адресу 0012FFC0, который должен стать вершиной стека при выполнении инструкции PUSH EBP.


Поэтому, в окне dump отладчика, переходим на адрес 0012FFC0, устанавливаем на нем Hardware BreakPoint on write → Dword, и нажимаем клавишу F9. Наблюдаем за записываемыми значениями по этому адресу. Значение 0012FFF0 записывается по адресу 0012FFC0 дважды. Первая запись нас не интересует, поскольку программа не останавливается на установленной нами Hardware BreakPoint on access. И, только после второй записи значения 0012FFF0, при очередном нажатии на клавишу F9, программа останавливается на установленной нами Hardware BreakPoint on access по адресу WOEP - 00407510. В стеке мы видим адрес возврата - 006168D9:


Распаковка ExeCryptor 2.3.9: статья 2


Давайте посмотрим, что имеется в этой части кода программы:


Распаковка ExeCryptor 2.3.9: статья 2


Здесь мы видим очень интересный код. Давайте перезагрузим программу, и, когда программа остановится на второй записи значение 0012FFF0 по адресу 0012FFC0, перейдем на вышеприведенный участок кода, на который установим BPM on access:




Распаковка ExeCryptor 2.3.9: статья 2


Нажимаем клавишу F9, и программа останавливается здесь:


Распаковка ExeCryptor 2.3.9: статья 2


В окне стека мы видим следующее:


Распаковка ExeCryptor 2.3.9: статья 2


А в окне регистров:




Распаковка ExeCryptor 2.3.9: статья 2


Здесь четко видно, что пакер, до остановки программы на адресе 006168C4, в своем коде выполнил только две инструкции - PUSH EBP и MOV EBP,ESP. Эти инструкции, в нашем дампе мы можем записать выше адреса 006168C4, на месте мусорного кода:


Распаковка ExeCryptor 2.3.9: статья 2


Сохраняем эти изменения в файле dumped_.exe.


Теперь нам нужно изменить значение Entry Point в нашем файле dumped_.exe на адрес 006168C1 (006168C1 - 00400000 = 002168C1).


Открываем файл dumped_.exe в утилите PE Tools v1.5 RC7, и проходим на вкладку “Optional Header”, на которой корректируем значение Entry Point на 002168C1. Заодно корректируем и значение параметра Base of Code на 00001000:


Распаковка ExeCryptor 2.3.9: статья 2


Пробуем запустить программу без отладчика, но программа не запускается.


Для дальнейшей работы, давайте немного облагородим наш файл dumped_.exe, и сделаем понятными имена секций файла, для дальнейших ссылок. Я решил это сделать таким образом:


Распаковка ExeCryptor 2.3.9: статья 2


Загружаем файл dumped_.exe в отладчик. Он сначала останавливается на TLS Callback, а уже потом останавливается на Entry Point программы. Причина заключается в том, что у нас остался старый адрес TLS Directory с параметрами, которые записал пакер ExeCryptor. Протектор ExeCryptor, при упаковке оригинальной программы, записывает свои параметры TLS Directory, которые и вызывает остановку программы на TLS Callbacks. Но, при этом, пакер сохраняет оригинальные данные TLS Directory в секции .rdata, поэтому, нам достаточно только восстановить родной адрес TLS Directory, и эта проблема - решена. Поэтому, вместо имеющегося адреса 00781110, нам надо в окне RVA параметра TLS Directory записать значение Virtual Offset секции .rdata:



Распаковка ExeCryptor 2.3.9: статья 2


Проверяем данные по этому адресу, для чего нажимаем кнопку “…”:


Распаковка ExeCryptor 2.3.9: статья 2


Видим, что у нас все получилось нормально. Пробуем загрузить файл в отладчик, и он нормально загружается, останавливаясь на адресе OEP:


Распаковка ExeCryptor 2.3.9: статья 2


Пытаемся запустить программу под отладчиком, но она спокойно закрывается:


Распаковка ExeCryptor 2.3.9: статья 2


В чем причина? Она оказалась банально простой! Если мы сопоставим значения регистров оригинальной упакованной программы и нашего дампа памяти, то увидим следующее:


Распаковка ExeCryptor 2.3.9: статья 2

Распаковка ExeCryptor 2.3.9: статья 2


Источником проблемы является значение регистра ECX = 0012FFB0. При запуске программы выполняется небольшой цикл с уменьшением значения регистра ECX на 1 (инструкция DEC ECX, которая находится по адресу 006168C8). В оригинальной упакованной программе, регистр ECX имеет значение - 7, которое обеспечивает нормальное выполнение цикла, и запуск программы. Значит, нам надо откорректировать код OEP программы следующим образом:


Распаковка ExeCryptor 2.3.9: статья 2


Сохраняем эти изменения в файле, и корректируем значение Entry Point программы в утилите PE Tools v1.5 RC7 на - 002168BC.


Примечание: Здесь нужно быть очень осторожными при корректировке кода. В программах, написанных на Delphi, Entry Point расположена непосредственно под таблицей инициализации INIT. Код, который находится выше Entry Point, кажется мусорным кодом, однако он содержит адреса таблицы INIT. Если мы повредим этот код, программа не будет работать.


Кстати, если мы посмотрим на это место кода в упакованной программе, то увидим следующее:


Распаковка ExeCryptor 2.3.9: статья 2


Мы видим прыжок на адрес 00B70C0A. Если мы посмотрим, что находится по этому адресу, то увидим:


Распаковка ExeCryptor 2.3.9: статья 2


Здесь находится Виртуальная OEP (VOEP) упакованной программы. Отсюда начинается выполнение эмулированного кода первых инструкций Entry Point программы.


Пытаемся запустить программу в отладчике, она сначала зависает, а потом закрывается вместе с отладчиком. Причиной этого является создание пакером нескольких потоков защиты, что можно увидеть в окне “Thread” отладчика:


Распаковка ExeCryptor 2.3.9: статья 2


Протектор продолжает создавать потоки защиты. Значит, наступило время удалить эти потоки защиты.


Следует отметить, что в данной версии протектора значительно усилена анти-отладочная защита, по сравнению с предыдущими версиями ExeCryptor. Пакер обнаруживает установку обычных BP на API CreateThread и API WaitForSingleObject, и вызывает сбой при запуске программы. Поэтому необходимо на API CreateThread и API WaitForSingleObject устанавливать Hardware BreakPoint on access. При этом, поскольку данные API работают в паре, сначала нужно установить Hardware BreakPoint on access на API CreateThread, а после остановки программы на этой API, необходимо устанавливать Hardware BreakPoint on access на API WaitForSingleObject. Это обусловлено тем, что пакер определяет время процесса, и, если время процесса не соответствует контрольному времени, также вызывает сбой при запуске программы.


Перезагружаем файл dumped_.exe в отладчике, и устанавливаем Hardware BreakPoint on access на API CreateThread. Нажимаем клавишу F9, чтобы запустить программу, и программа останавливается на начале API CreateThread:


Распаковка ExeCryptor 2.3.9: статья 2


Итак, программа остановилась на API CreateThread. Для нас интерес представляет параметр ThreadFunction = 00641A4A. В справочнике по API об этом параметре записано следующее:


lpStartAddress


The starting address of the new thread. This is typically the address of a function declared with the WINAPI calling convention that accepts a single 32-bit pointer as an argument and returns a 32-bit exit code. Its prototype is:


DWORD WINAPI ThreadFunc( LPVOID );


В переводе на русский язык это выглядит так:


Начальный адрес нового потока. Обычно - это адрес функции, объявленной соглашением о вызовах WINAPI, который принимает единственный 32-bit указатель в качестве параметра, и возвращает 32-bit код завершения.


Это означает, что началом нового потока защиты в нашей программе является адрес 00641A4A. Давайте посмотрим, что у нас находится по этому адресу:


Распаковка ExeCryptor 2.3.9: статья 2


Здесь находится инструкция PUSH EBP. После выполнения этой инструкции выполняется весь следующий код. Чтобы не создавать здесь поток защиты, необходимо прервать выполнение этого кода инструкцией RETN:


Распаковка ExeCryptor 2.3.9: статья 2


Сохраняем эти изменения, перезагружаем, файл в отладчике OllyDbg, и нажимаем клавишу F9. Программа сначала останавливается на API CreateThread. Теперь устанавливаем Hardware BreakPoint on access на API WaitForSingleObject, и нажимаем клавишу F9. Программа останавливается на API WaitForSingleObject:


Распаковка ExeCryptor 2.3.9: статья 2


В этой API нам интересен параметр Timeout, который имеет значение INFINITE. Это значение означает, что сейчас API WaitForSingleObject будет бесконечное время ожидать сообщение об активности созданного потока защиты. Естественно, что запуск программы останавливается. Для решения этой проблемы, нам нужно установить значение параметра Timeout = 0. В каком же месте по адресу 0012FF38 записывается значение FFFFFFFF? Перезагружаем программу в отладчике, удаляем Hardware BreakPoint on access на API WaitForSingleObject (Hardware BreakPoint on access на API CreateThread остается установленной), и нажимаем клавишу F9. После остановки программы на API CreateThread, в окне dump переходим на адрес 0012FF38, и устанавливаем на него Hardware BreakPoint on write → Dword:


Распаковка ExeCryptor 2.3.9: статья 2


Нажимаем клавишу F9 до тех пор, пока по адресу 0012FF38 не запишется значение FFFFFFFF:


Распаковка ExeCryptor 2.3.9: статья 2


Если мы заменим инструкцию PUSH -1, которая находится по адресу 0064D713, инструкцией PUSH 0, то тогда параметр Timeout будет равен 0, и мы тем самым устраним ожидание API WaitForSingleObject подтверждения активности потока защиты, созданного API CreateThread. Сохраняем эти изменения в файле, перезагружаем программу в отладчике, и нажимаем клавишу F9:


Распаковка ExeCryptor 2.3.9: статья 2


Программа нормально запустилась под отладчиком. Попытаемся запустить программу без отладчика, и она также нормально работает.


Теперь давайте попробуем удалить ненужные секции пакера.


На всякий случай, давайте сделаем резервную копию нашего файла dumped_exe:


Распаковка ExeCryptor 2.3.9: статья 2




Удаление секций пакера будет производить методом “проб и ошибок”. Будем удалять по одной секции, начиная с последней секции, и проверять запуск программы. Для удаления секций пакера будем использовать утилиту PE Tools v1.5 RC7 by NEO. Сначала попытаемся удалить последнюю секцию .prot4:


Распаковка ExeCryptor 2.3.9: статья 2


После удаления этой секции, проходим на вкладку Optional Header, и корректируем параметры, возле которых находится кнопка с вопросительным знаком:





Распаковка ExeCryptor 2.3.9: статья 2


Запускаем программу, и программа нормально запускается.


Теперь попытаемся удалить секцию .prot3, и посмотрим, что у нас получится. Повторяем все те же самые действия, которые мы делали с секцией .prot4. Запускаем программу, и программа опять нормально запускается.


Удалять секцию протектора prot2 нельзя, поскольку эта секция протектора используется при работе программы. Секция кода программы имеет очень много ссылок на эту секцию, и восстанавливать этот код вручную не имеет никакого смысла.


На этом мы закончили рассмотрение второй части туториала по распаковке программы, запакованной ExeCryptor. В 3-й части мы рассмотрим доработку дампа памяти программы для его запуска на разных операционных системах, и механизм работы ExeCryptor при выполнении украденных инструкций.


До встречи в 3-й части туториала.


vnekrilov



Скачать статью "Распаковка ExeCryptor 2.3.9 (на примере PrevedSMS 5.1), часть 2" в авторском оформление + файлы.
пароль архива на картинке



Оригинальный DVD-ROM: eXeL@B DVD !


Вы находитесь на EXELAB.rU
Проект ReactOS