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

Видеокурс программиста и крэкера 5D 2O17
(актуальность: декабрь 2O17)
Свежие инструменты, новые видеоуроки!

  • 400+ видеоуроков
  • 800 инструментов
  • 100+ свежих книг и статей

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


Распаковка ASProtect 2.3 SKE, часть 3




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

Виртуальная машина - Stolen Code


Давайте рассмотрим, что такое виртуальная машина, которую почему-то считают самой сложной частью Asprotect. Автор Asprotect Алексей Солодовников, решил значительно усложнить процесс распаковки программ, упакованным этим пакером, еще одной опцией - он перенес выполнение части кода программы в специальную область памяти, создаваемую пакером во время распаковки программы в память машины. Причем в этой специальной области памяти выполняется не только код, расположенный возле Entry Point программы, но также, из секции кода программы идет довольно много прыжков в эту область памяти. Относительно несложно скопировать эту область памяти, которая называется виртуальной машиной (VM), и присоединить к изготовленному нами дампу памяти программы в виде отдельной секции EXE-файла. Довольно просто обеспечить загрузку этой секции EXE-файла по заданному адресу, установив нужное значение секции в параметре Virtual Offset. Но имеется одно НО! Код этой области памяти разорван огромным числом инструкций CALL XXXXXXXX, которые выполняют прыжок в другую область памяти программы. А если мы посмотрим структуру этих инструкций CALL XXXXXXXX, то там мы увидим еще кучу инструкций, которые перебрасывают программу в другие области памяти, созданные пакером. Поэтому, тупое копирование этой области памяти нам ничего не даст, и нам нужно сделать что-то такое, что позволило бы программе выполнять код только в одной области памяти.


Давайте, после этого небольшого вступления, рассмотрим нашу программу, для чего перезагружаем исходную программу, и, с помощью скрипта, проходим на VOEP:


ASProtect 2.3 SKE распаковка - часть три


Код заполнен мусором, а если мы немного прокрутим код вниз, то увидим инструкцию PUSH 1B80461, за которой идет инструкция CALL 01C30000. Эта пара инструкций является еще одним типом прыжков на область памяти, созданной asprotect при запуске программы.


Инструкция PUSH 1B80461, при выполнении кода, в данном случае обеспечивает выполнение прыжка на адрес, который указан в качестве параметра этой инструкции (1B80461). Значительно больший интерес для нас представляет инструкция CALL 01C30000. Давайте, выполним поиск всех инструкций CALL 01C30000:


ASProtect 2.3 SKE распаковка - часть три


И на каждый адрес, где находится инструкция CALL 01C30000, установим BP:


ASProtect 2.3 SKE распаковка - часть три


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


ASProtect 2.3 SKE распаковка - часть три


Заходим в CALL 01C30000 с F7:


ASProtect 2.3 SKE распаковка - часть три


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


ASProtect 2.3 SKE распаковка - часть три


Давайте установим BreakPoint на адрес этого CALL EBP, и нажмем клавишу F9. Программа останавливается на BP, а инструкция CALL ESI указывает на подпрограмму, которая выполняет идентификацию украденной инструкции. Удаляем BP, входим в CALL ESI по F7:


ASProtect 2.3 SKE распаковка - часть три


Эта подпрограмма не содержит мусорный код, и данной подпрограмме нас интересуют только две инструкции CALL:


ASProtect 2.3 SKE распаковка - часть три


На этом рисунке, я, для наглядности, замени мусорный код инструкцией NOP.




Инструкция CALL EDX (вместо регистра EDX может быть любой регистр) занимается извлечением и декодированием хэшей из таблицы хэшей. Когда хэш декодирован, производится сравнение полученного хэша с хэшем текущей функции. Если данный хэш не совпадает с хэшем текущий функции, то выполняется проверка, не является ли этот хэш последним хэшем в таблице. Если этот хэш является последним хэш в таблице, то прыжок JA SHORT 00A04D1B не выполняется, и нам показывается сообщение с ошибкой “Error: 111”.


Обработка найденного хэша может производится в CALL 00A045C4 или CALL 00A04748, поскольку обе эти инструкции содержат прыжок JMP DWORD PTR DS:[EAX+20], который необходим для указания адреса дальнейшей работы программы:


ASProtect 2.3 SKE распаковка - часть три

ASProtect 2.3 SKE распаковка - часть три


Следует отметить, что в этой программе использовалась только инструкция CALL 00A045C4.


Устанавливаем BP на обе инструкции CALL 00A045C4 и CALL 00A04748, и нажимаем F9. Удаляем BP, и входим в CALL 00A045C4 или CALL 00A04748 по F7:


ASProtect 2.3 SKE распаковка - часть три


Чуть ниже находится селектор обработки типа украденной инструкции:


ASProtect 2.3 SKE распаковка - часть три


Инструкция CALL EDX определяет тип украденной инструкции:



AL = 0 - украден call

AL = 1 - украден jmp

AL = 2 - украден jcc (один из 16 прыжков)

AL = 3 - украдены cmp+jcc


И, в зависимости от полученного значения в регистре AL, будет выполнен один из условных прыжков, находящихся ниже инструкции SUB AL,2. Я не буду подробно описывать работу этого селектора, поскольку все это можно прочитать в прекрасной работе PE_Kill “Распаковка ASProtect v 2.xx (отрезание секций, восстановление скрамблерного кода, декомпиляция VM, восстановление импорта, инлайн-патч)”.


Но, какой бы прыжок не выполнялся, в любом случае, пакер приходит сюда:


ASProtect 2.3 SKE распаковка - часть три


Инструкция JMP DWORD PTR DS:[EAX+20] всегда выполняет прыжок на начало области памяти 01C40000 (в данной программе). Нажимаем клавишу F8, чтобы выполнить прыжок, и попадаем сюда:


ASProtect 2.3 SKE распаковка - часть три


Вся эта область памяти заполнена мусорным кодом. Но, если немного потрассировать этот код, то мы приходим сюда:


ASProtect 2.3 SKE распаковка - часть три



Здесь находится самый главный прыжок JMP DWORD PTR SS:[ESP-4], который определяет, с какого адреса необходимо продолжить выполнение программы. В окне справки отладчика мы видим, что пакер выполнит прыжок на адрес 01B80ABE, который находится в одной области памяти с VOEP. Записываем базовый адрес области памяти, в которой находится прыжок JMP DWORD PTR SS:[ESP-4], поскольку адрес этого прыжка меняется при каждой перезагрузке программы, и, по этой причине, в нашем скрипте, этот прыжок мы будем находить по его опкоду #FF6424FC#. Нажимаем клавишу F8, и попадаем сюда:


ASProtect 2.3 SKE распаковка - часть три


Давайте установим Hardware BreakPoint on execute на инструкцию JMP DWORD PTR SS:[ESP-4], и понажимаем несколько раз клавишу F9. На одном из прыжков мы попадаем сюда:


ASProtect 2.3 SKE распаковка - часть три


Теперь прыжок выполняется на область .text; т.е. мы выходим из виртуальной машины. Давайте подведем итоги проделанной работы. Инструкция CALL 01C30000 определяет адрес, с которого программа должна продолжить свое выполнение. Причем, в зависимости от хэша украденной инструкции, дальнейшее выполнение кода может осуществляться как в области памяти виртуальной машины, так и области кода самой программы. Если мы заменим инструкцию CALL 01C30000 прыжком на адрес, на который указывает инструкция JMP DWORD PTR SS:[ESP-4], то проблема с виртуальной машиной будет решена.


Итак, мы имеем следующее:


  1. Инструкции PUSH XXXXXXXX, которые указывают на адрес прыжка в области виртуальной машины;

  2. Инструкции CALL 01C30000, которые определяют адрес дальнейшего выполнения программы.



Далее у нас имеется два варианта решения этой задачи:


  1. Привести все инструкции CALL 01C30000 к инструкциям JMP XXXXXXXX, сдампировать всю область памяти виртуальной машины 01B80000, и прикрутить этот дамп в конец нашего файла dumped_.exe, указав Virtual Offset прирученной секции - 01B80000, чтобы обеспечить загрузку этой секции в области памяти машины по ее родному адресу.



  1. Найти свободное место в нашем файле dumped_.exe, сделать переадресацию всех инструкций PUSH XXXXXXXX и JMP XXXXXXXX, на это свободное место, затем скопировать область памяти виртуальной машины 01B80000, и вставить скопированные данные на свободное место.


Я решил использовать второй вариант, поскольку он позволяет уменьшить размер файла. А если мы не найдем достаточного количества свободного места, то тогда мы сможем создать дополнительную секцию в файле dumped_.exe, и сделать размер этой секции таким же, какой имеет эта область памяти виртуальной машины 01B80000. Проходим на вкладку Memory Map, и находим область памяти 01B80000:


ASProtect 2.3 SKE распаковка - часть три


Здесь мы видим размер области VM - 3000h байтов.


Теперь ищем свободное место в секциях файла dumped_.exe. Свободное место в 3000h байтов можно найти в секции .tls, поскольку она имеет размер A000h байтов:


ASProtect 2.3 SKE распаковка - часть три


Давайте проверим, используется ли эта секция файла при запуске программы, для чего проходим на VOEP, и устанавливаем BPM on access на секцию .tls. Нажимаем клавишу F9, и программа не останавливается на доступе к этой секции. Значит, восстановленный код VM мы сможем записать в секцию .tls. Однако проведем некоторую доработку этой секции; мы разделим ее на две части, одна часть будет иметь размер 7000h байтов, и будет содержать данные TLS Directory, а вторая часть будет иметь размер 3000h байтов, в которую мы запишем восстановленный код VM. Для этих действия мы опять будем использовать PE Tools v1.5 RC7, с помощью которого сначала удаляем секцию .rsrc, и затем корректируем размер секции .tls:




ASProtect 2.3 SKE распаковка - часть три


Затем добавляем новую секцию с размером 3000h байтов, и указываем ее имя .code, поскольку эта секция будет содержать восстановленный код VM:


ASProtect 2.3 SKE распаковка - часть три


ASProtect 2.3 SKE распаковка - часть три


И вставляем секцию .rsrc, как это мы делали ранее. Корректируем значение Virtual offset секций .code и .rsrc, чтобы они были одинаковыми со значениями Raw offset. И корректируем параметр Size of Image, как это мы делали ранее, при удалении секций, нажав три кнопки с вопросительными знаками. Проверяем нашу доработку, загружая файл dump_.exe в отладчик:


ASProtect 2.3 SKE распаковка - часть три


Базовый адрес секции .code - 0048C000, и этот адрес мы будем использовать в скрипте.



Нам надо определить значение инструкций PUSH для секции .code файла dumped_.exe. Возьмем, например, инструкцию PUSH 1B8063A. Значение 01B8063A для новой секции .code можно легко вычислить по следующей формуле:


Новое значение = PUSH - BASE + NEWBASE, где PUSH - это значение, записанное в инструкции PUSH (01B8063A); BASE - это базовый адрес данной области памяти (01B80000); NEWBASE - это базовый адрес секции .code файла dump_.exe (0048C000).


Следовательно, для этой инструкции PUSH, значение аргумента исправляем следующим образом:


Новое значение = 01B8063A - 01B80000 + 0048C000 = 0048C63A


Таким образом, инструкцию PUSH 1B8063A восстанавливаем как PUSH 0048C63A.


Теперь нам надо восстановить инструкции CALL 01C30000.


Ранее мы видели, что прыжок JMP DWORD PTR SS:[ESP-4] может выполняться как на область виртуальной машины (область памяти 01B80000), так и на область секции .text (это показано на предыдущих рисунках). Следовательно, при восстановлении инструкций CALL 01C30000 возможны два варианта.


1-й вариант. Прыжок JMP DWORD PTR SS:[ESP-4] выполняется на область памяти виртуальной машины, где находится VOEP (а мы видели, что первый прыжок был именно таким):


ASProtect 2.3 SKE распаковка - часть три


Адрес 01B80ABE находится в области виртуальной машины, где находится VOEP. Тогда, этот прыжок мы можем записать следующим образом:


Новый адрес = (Адрес назначения прыжка JMP DWORD PTR SS:[ESP-4]) - (Адрес, на котором расположена инструкция CALL 01C30000) - 5


В нашем случае, адрес первого прыжка будет таким:


Новый адрес = 01B80ABE - 01B8071A - 5 = 0000039F


Примечание: Такие вычисления я делаю в калькуляторе Windows, потому что иногда приходится из меньшего числа вычитать большее число, а выполнение таких вычислений в command bar дает неправильный результат.


Давайте пройдем в дамп на адрес 01B8071A нашей инструкции CALL 01C30000, и заменим инструкцию CALL (опкод E8) на инструкцию JMP (опкод E9), и после опкода E9 запишем полученный нами результат в обратном порядке:


ASProtect 2.3 SKE распаковка - часть три


Итак, мы получили первые инструкции переадресации:


  01B80715    68 3AC64800     PUSH 0048C63A	; адрес возврата (в добавленную нами секцию)
  01B8071A   E9 9F030000     JMP 01B80ABE	; переадресация CALL
  


Здесь мы видим, что замененная инструкция CALL 01C30000 показывает на адрес, который принадлежит области памяти VOEP! Но, об этом не нужно беспокоиться; когда мы скопируем эту область памяти в добавленную секцию файла dumped_.exe, то там все будет записано правильно.


2-й вариант. Прыжок JMP DWORD PTR SS:[ESP-4] выполняется на адрес, который находится в секции .text:


(Адрес, на котором расположена инструкция CALL 01C30000) - BASE + NEWBASE = NewAddres


Новый адрес = (Адрес назначения прыжка JMP DWORD PTR SS:[ESP-4]) - NewAddres - 5


Во втором случае, адрес второго прыжка будет таким:


NewAddres = 01B80AFE - 01B80000 + 0048C000 = 0048CAFE


Новый адрес = 00406AC4 - 0048CAFE - 5 = FFF79FC1


Таким образом, мы выполняем переадресацию всех CALL 01C30000 в добавленную нами секцию файла dump_.exe.


И, наконец, нам нужно найти последний CALL 01C30000, чтобы пометить его в скрипте, для чего выполняем команду Searh for → All commands.



ASProtect 2.3 SKE распаковка - часть три


В нашем случае, последняя инструкция CALL 01C30000 находится по адресу 01B80F0B.


Теперь у нас имеются все необходимые данные для разработки скрипта. Этот скрипт, когда программа остановлена на VOEP, будет искать все инструкции CALL 01C30000, затем будет искать адрес прыжка JMP DWORD PTR SS:[ESP-4], после чего установит Hardware BP на этот прыжок. Затем скрипт восстановит инструкции CALL 01C30000 и PUSH, и продолжит свою работу со следующей парой инструкций PUSH - CALL.

Поскольку инструкция JMP DWORD PTR SS:[ESP-4] находится в области памяти, создаваемой asprotect при каждой перезагрузке программы, то адрес этой инструкции будет все время меняться. Поэтому будем искать этот прыжок с помощью цепочки байтов его опкода #FF6424FC#. Запоминаем область памяти, где находится этот прыжок, в моем случае - это область памяти 01C40000.


Скрипт будет таким:



  //Разработчик скрипта: Ulaterck, доработка - vnekrilov.
  //Описание: Скрипт разработан для восстановления инструкций PUSH - CALL в области памяти VOEP
  //программы eactive MYCOP Cleaner v1.2, защищенной ASProtect v2.3 SKE build 06.26 Beta.
  //Цель: RMC.EXE
  //Условия применения: ODBGScript 1.48, Olly Advanced v1.26 Beta 12, PhantOm v1.04.
 
var OPCode // Переменная для хранения опкода инструкций var VOEP_BASE // Переменная для хранения базового адреса области VOEP var ESP_4 // Переменная для хранения значения инструкции JMP [ESP-4] var Address_CALL // Переменная для хранения адреса, где имеется CALL 01C30000 var Field_Asprotect // Переменная для хранения области памяти CALL 01C30000 var Temp_Field_Asprotect // Переменная для временного хранения памяти CALL 01C30000 var Start_scan // Переменная для хранения адреса начала сканирования var End_scan // Переменная для хранения адреса конца сканирования var Address_JMP // Переменная для хранения найденного адреса JMP [ESP-4] var Field_JMP // Переменная для хранения адреса области памяти JMP [ESP-4] var Address_Opcode_Call // Переменная для хранения найденного опкода инструкции CALL var Temp_EIP // Переменная для хранения текущего значения EIP var OEP // Переменная для хранения адреса OEP var NEWBASE // Переменная для хранения параметра NEWBASE var Temp_ESP // Переменная для хранения текущего значения ESP var Field_ESP_4 // Переменная для хранения области памяти ESP-4 var Flag // Переменная для хранения значения флажка
preparation: // Подготовка скрипта к работе mov OEP,eip // Сохраняем значение VOEP в переменной OEP. mov Start_scan,01B80000 // Указываем адрес начала поиска CALL 01C30000 mov VBASE,01B80000 // Сохраняем базовый адрес области памяти VOEP mov End_scan,01B80F0B // Указываем адрес последнего CALL 01C30000 mov Field_Asprotect,01C30000 // Указываем значение, которое имеет CALL 01C30000. mov Temp_Field_Asprotect,01C30000 // Сохраняем значение CALL 01C30000в переменной mov Field_JMP,01C40000 // Указываем область памяти, где находится JMP [ESP-4] mov NEWBASE,0048C000 // Сохраняем начало созданной секции в файле dump_.exe
SearchJMP: // Поиск адреса прыжка JMP [ESP-4] findop Field_JMP,#FF6424FC# // Ищем опкод прыжка JMP [ESP-4] mov Address_JMP,$RESULT // Сохраняем найденный адрес прыжка JMP [ESP-4].
SearchCall: // ** Процедура поиска CALL 01C30000 ** findop Start_scan,#E8# // Ищем опкод инструкции Call (E8) add Start_scan,1 // Прибавляем к найденному значению 1 mov Address_Opcode_Call,$RESULT // Сохраняем адрес найденного опкода инструкции call sub Field_Asprotect,Address_Opcode_Call // Вычитаем из 01C30000 адрес, где находится CALL sub Field_Asprotect,5 // Из полученного значения вычитаем число 5 add $RESULT,1 // К полученному результату добавляем 1 cmp [$RESULT],Field_Asprotect // Полученный результат сравниваем с 01C30000 mov Field_Asprotect,Temp_Field_Asprotect // Восстанавливаем значение в переменной jne SearchCall // Если это не CALL 01C30000, то продолжаем поиск mov Address_CALL,Address_Opcode_Call // Сохраняем адрес найденного опкода Call 01C30000 mov eip,Address_CALL // Изменяем EIP на адрес найденной инструкции Call 01C30000 mov Temp_EIP,Address_CALL // Сохраняем, на всякий случай, адрес Call 01C30000. log Address_CALL // Регистрируем адрес, на котором находится Call 01C30000
JMPESP_4: bp Address_JMP // Устанавливаем breakpoint на инструкции JMP [ESP-4] run // Запускаем программу bc Address_JMP // Удаляем breakpoint на инструкции JMP [ESP-4] mov Temp_ESP,esp // Сохраняем значение регистра ESP в переменной Temp_ESP sub Temp_ESP,4 // Вычитаем 4, чтобы перейти на (JMP [ESP-4]) mov ESP_4,[Temp_ESP] // Сохраняем полученный результат (адрес) в переменной ESP_4 log ESP_4 // Регистрируем значение ESP_4 mov eip,Address_CALL // Снова переходим на Call 01C30000 sti // Выполняем F7 sub Temp_EIP,5 // Вычитаем 5, и переходим на адрес инструкции PUSH mov OPCode,[Temp_EIP] // Сохраняем значение инструкции PUSH XXXXX and OPCode,000000ff // Удаляем ненужный opcod инструкции PUSH cmp OPCode,00000068 // Проверяем, находимся ли мы на инструкции PUSH je EditPush // Если Да (инструкция push), прыгаем на редактирование. mov Flag,1 // Если Нет, то редактируем только Call 01C30000 jmp section
EditPush: // Процедура для редактирования PUSH mov Flag,0 // Записываем в переменную Flag 0 add Temp_EIP,1 // К значению Temp_EIP прибавляем 1. mov OPCode,[Temp_EIP] // Сохраняем полученное значение адреса в OPCode sub OPCode,VBASE // Вычитаем из адреса PUSH базовый адрес области памяти VOEP. add OPCode,NEWBASE // Прибавляем базовый адрес новой секции файла dump_.exe mov [Temp_EIP],OPCode // Сохраняем полученное значение восстановленной PUSH
section: cmp Flag,1 // Записываем в переменную Flag 1 jne inspection // Прыгаем на метку inspection
increment: add Temp_EIP,1 // Увеличиваем значение Temp_EIP на 1
inspection: add Temp_EIP,4 // Снова переходим на Call 01C30000 mov Field_ESP_4,ESP_4 // Сохраняем в переменной содержимое переменной ESP_4 and Field_ESP_4,ffff0000 // Имеется ли адрес области памяти ESP-4 cmp Field_ESP_4,VBASE // Проверяем, находится ли адрес ESP-4 в области VOEP je EditCall1 // Если Да, прыгаем на EditCall1 (1-й вариант расчета) // Если Нет, переходим на EditCall2 (2-й вариант расчета)
EditCall2: // Редактирование Call 01C30000, если ESP-4 не в области VOEP mov OPCode,Temp_EIP // Записываем адрес Call 01C30000 в переменную OPCode sub OPCode,VBASE // Вычитаем из значения OPCode значение VBASE add OPCode,NEWBASE // Складываем значение OPCode со значением NEWBASE sub ESP_4,5 // Из значения ESP_4 вычитаем 5 sub ESP_4,OPCode // К полученному результату прибавляем значение OPCode mov [Temp_EIP],E9 // Записываем опкод прыжка (E9) вместо опкода Call (E8) add Temp_EIP,1 // Переходим на следующий байт после байта E9 mov [Temp_EIP],ESP_4 // Записываем значение прыжка JMP XXXXXX
check: cmp Address_CALL,End_scan // Проверяем, не последний ли это Call 01C30000 je Message // Если это последний Call 01C30000, прыгаем на message jmp SearchCall // Если нет, ищем следующий Call 01C30000
EditCall1: // Редактирование Call 01C30000, если ESP-4 в области VOEP sub ESP_4,5 // Из значения ESP_4 вычитаем 5 sub ESP_4,Temp_EIP // Из полученного результата вычитаем адрес инструкции Call mov [Temp_EIP],E9 // Записываем опкод прыжка (E9) вместо опкода Call (E8) add Temp_EIP,1 // Переходим на следующий байт после байта E9 mov [Temp_EIP],ESP_4 // Записываем значение прыжка JMP XXXXXX jmp check // Прыгаем на метку check, для проверки, не последний ли Call
Message: MSG "Восстановление CALL и JMP успешно завершено." endscript: mov eip,OEP ret // Завершаем работу скрипта.


Я постарался подробно объяснить код скрипта. Выделенные желтым цветом адреса нужно откорректировать в соответствии с адресами Вашей машины.

Итак, перезагружаем программу, проверяем, нет ли у нас установленных BP, проходим на VOEP с помощью скрипта, после чего запускаем наш скрипт, который должен восстановить инструкции PUSH и CALL, я его назвал - Recovery_PUSH_CALL_VOEP.osc. Скрипт нормально отработал, без каких-либо сбоев.


Нажимаем клавиши Alt+C, и переходим на VOEP. Попытаемся найти инструкции CALL 01C30000, которые могли быть не восстановленными, но их нет. Скрипт восстановил все инструкции PUSH и CALL.


Мы видим изменения в коде, выполнена переадресация инструкции PUSH, а инструкция CALL 01C30000 заменена инструкцией JMP на нашу новую секцию. Поэтому можно сказать, что мы закончили работу с виртуальной машиной asprotect, но нужно иметь в виду, что мы не пытаемся очистить код от мусорных инструкций, типа прыжков и каких-то непонятных инструкций, потому что это очень тяжелая и нудная работа. Проще всего выполнить переадресацию PUSH и CALL, скопировать всю эту область памяти VOEP, и вставить ее в dumped_.exe.


Выделяем всю область памяти виртуальной машины, и выполняем бинарное копирование выделенной области памяти:


ASProtect 2.3 SKE распаковка - часть три


Сохраняем скопированные данные в Hex Workshop v4.23, как это мы делали при сохранении восстановленных таблицы INIT. Сохраняем файл с именем Recovery_VM.




В отладчике OllyDbg открываем файл dumped_.exe, а в HexWorkshop - файл Recovery_VM. В окне dump отладчика нажимаем клавиши Ctrl+G, и вводим адрес созданной нами секции .code - 0048C000, куда мы будем вставлять восстановленный код VM.


Переходим на этот адрес, и выделяем всю выбранную секцию файла. В HexWorkshop выделяем все байты файла Recovery_VM:


ASProtect 2.3 SKE распаковка - часть три


Копируем все байты, вставляем их в наш файл dumped_.exe:


ASProtect 2.3 SKE распаковка - часть три


Сохраняем изменения.


Теперь нам надо изменить OEP файла dumped_.exe, в который мы вставили байты из скопированной области памяти программы. Для этого нужно изменить прыжок на новую секцию, которая теперь содержит OEP. VOEP в оригинальной программе была по адресу 01B802A0, поэтому OEP будет находиться, в новой секции, по адресу:


01B802A0 - 01B80000 + 0048C000 = 0048C2A0


Открываем dumped_.exe в Olly, останавливаемся на Entry Point, и, записываем прыжок на адрес 0048C2A0 (JMP 0048C2A0):


ASProtect 2.3 SKE распаковка - часть три


Сохраняем изменения, перезагружаем dumped_.exe, и видим, что мы восстановили виртуальную машину asprotect. Если мы нажмем клавишу F7, то попадаем на адрес реальной OEP.


ASProtect 2.3 SKE распаковка - часть три


8. Доработка полученного дампа, и запуск программы


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


ASProtect 2.3 SKE распаковка - часть три


В таких случаях, учитывая, что при восстановлении адресов виртуальной машины VM, может быть записан неверный адрес назначения прыжка JMP, которым мы заменяем инструкцию CALL 01C30000, я всегда запускаю одновременно оригинальную упакованную программу и полученный дамп в двух отладчиках OllyDbg. В первом отладчике я устанавливаю BP на все инструкции CALL 01C30000, а во втором отладчике устанавливаю BP на все инструкции JMP, которым заменили инструкции CALL 01C30000. Также, в оригинальной упакованной программе устанавливаем BP на инструкцию JMP DWORD PTR SS:[ESP-4], и проверяем правильность назначения прыжка в оригинальной упакованной программе и полученном дампе.


И, почти сразу, находим первую ошибку; при выполнении инструкции CALL 01C30000, расположенной по адресу 01B804BF, мы видим:


ASProtect 2.3 SKE распаковка - часть три



А в нашем дампе по этому адресу мы видим следующее:


ASProtect 2.3 SKE распаковка - часть три


Поэтому корректируем адрес назначения прыжка на 0048CB41:


ASProtect 2.3 SKE распаковка - часть три


Сохраняем сделанные изменения в файле, перезагружаем программу, нажимаем клавишу F9, и получаем следующее сообщение об ошибке:


ASProtect 2.3 SKE распаковка - часть три


Идет ссылка на адрес 01BE0000, т.е. на область памяти, которая была создана пакером в оригинальной программе. Давайте посмотрим, откуда вызывается этот адрес. Переходим в секцию кода, и выбираем команду Search for → Constant. В появившемся диалоговом окне вбиваем значение 01BE0000:


ASProtect 2.3 SKE распаковка - часть три


Нажимаем кнопку ОК, и попадаем сюда:


ASProtect 2.3 SKE распаковка - часть три



Эта пара инструкций означает прыжок на область памяти 01BE0000, которой нет в нашем файле.


Смотрим в оригинальной программе, что у нас имеется по адресу 01BE0000:


ASProtect 2.3 SKE распаковка - часть три


Размер кода здесь небольшой, всего 92h байтов, поэтому мы копируем этот код, и вставляем его на свободное место секции кода нашего дампа, по адресу 0047D3D0:


ASProtect 2.3 SKE распаковка - часть три


Теперь корректируем инструкцию по адресу 004089FC:


ASProtect 2.3 SKE распаковка - часть три


Сохраняем сделанные изменения в файле, перезагружаем программу, нажимаем клавишу F9, и получаем следующее сообщение об ошибке:


ASProtect 2.3 SKE распаковка - часть три


Опять ссылка на область памяти, созданной AsProtect. Место этой ссылки находим тем же образом, как и в предыдущий раз:


ASProtect 2.3 SKE распаковка - часть три


Переходим на адрес 01BF0000 в оригинальной программе, и видим, что размер этой области памяти равен 8Bh байтов:


ASProtect 2.3 SKE распаковка - часть три


Копируем эти байты, и вставляем их в наш дамп на свободное место в секции кода по адресу 0047D463:


ASProtect 2.3 SKE распаковка - часть три


Корректируем ссылку на этот адрес:


ASProtect 2.3 SKE распаковка - часть три


Сохраняем сделанные изменения в файле, перезагружаем программу, нажимаем клавишу F9, и получаем следующее сообщение об ошибке:


ASProtect 2.3 SKE распаковка - часть три


А в окне LOG мы видим следующее:


ASProtect 2.3 SKE распаковка - часть три


Программа генерирует исключение 0EEDFADE по адресу 7C81EB33. Давайте посмотрим, откуда генерируется это исключение, для чего, перезагружаем программу, проходим на адрес 7C81EB33, и прокручиваем код на начало этой подпрограммы, где устанавливаем BP:


ASProtect 2.3 SKE распаковка - часть три


Отключаем все установленные BP, кроме BP, установленной на адресе 7C81EAE1, и запускаем программу, нажав клавишу F9. Когда программа остановилась на BP, смотрим в окно стека:


ASProtect 2.3 SKE распаковка - часть три


RaiseException вызывается из 00474744. Переходим на этот адрес в отладчике, и устанавливаем на него BP:


ASProtect 2.3 SKE распаковка - часть три


Перезагружаем программу, нажимаем клавишу F9, и останавливаемся на BP. Входим в инструкцию CALL 0040289C с F7:


ASProtect 2.3 SKE распаковка - часть три


Если мы дойдем в дампе до прыжка JNZ SHORT 004028B1, то этот прыжок выполняется, приводит нас к генерации исключения. В оригинальной упакованной программе, этот прыжок не выполняется, и программа нормально загружается:


ASProtect 2.3 SKE распаковка - часть три


Причиной выполнения прыжка является то, что содержимое адреса 00153134 равно 8, а не 0, как в оригинальной упакованной программе. Причем значение 8 записывается при загрузке программы в память машины. Чтобы не усложнять нам жизнь, просто заменим инструкцию JNZ инструкцией NOP, и сохраним эти изменения:


ASProtect 2.3 SKE распаковка - часть три


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


ASProtect 2.3 SKE распаковка - часть три


Попытаемся закрыть программу, и нам появляется следующее сообщение об ошибке:


ASProtect 2.3 SKE распаковка - часть три


Почему же появилось это сообщение? Видимо, опять где-то записан неверный адрес прыжка JMP, которым мы заменили инструкцию CALL 01C30000. Поэтому продолжим работу в двух отладчиках; в одном отладчике загружена оригинальная упакованная программа, а во втором отладчике - файл dumped_.exe. Запускаем обе программы в отладчике, после чего закрываем программы, и смотрим адреса назначения прыжков JMP в оригинальной упакованной программе, и дампе dumped_.exe. Находим еще 4 прыжка JMP с неправильным адресом назначения:


ASProtect 2.3 SKE распаковка - часть три


ASProtect 2.3 SKE распаковка - часть три


ASProtect 2.3 SKE распаковка - часть три


ASProtect 2.3 SKE распаковка - часть три


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


9. Заключение


Этот туториал я написал довольно быстро, поскольку протектор ASProtect v2.3 SKE build 06.26 Beta оказался аналогичным протектору ASProtect v1.35 build 06.26. Кроме того, в протекторе ASProtect v2.3 SKE build 06.26 Beta не оказалось защиты от дампирования путем выполнения прыжков в секцию виртуальной машины, которые применяются в протекторе ASProtect v1.35 build 06.26. Я было подумал не публиковать эту статью, поскольку она во многом аналогична моей предыдущей статье по распаковке ASProtect v1.35 build 06.26. Но, после некоторого размышления, решил эту статью все-таки опубликовать, поскольку в Internet я не нашел ни одной статьи, в которой бы была описана распаковка протектора ASProtect v2.3 SKE build 06.26 Beta. Поэтому прошу читателей не ругать меня за практически идентичную статью. Я думаю, что изложенный здесь материал окажется полезным как для начинающих cracker’s, так и для профессионалов в области реверсинга программ.

Я хотел бы поблагодарить Gideon Vi, за предоставленную им упакованную программу, на базе которой я написал этот туториал. Также мне хочется выразить признательность bronco, за его моральную поддержку и пожелания в написании туториалов. Хотелось бы выразить признательность PE_Kill за его прекрасную работу “Распаковка ASProtect v 2.xx (отрезание секций, восстановление скрамблерного кода, декомпиляция VM, восстановление импорта, инлайн-патч)”, из которой я почерпнул много полезной для себя информации, и использовал его прекрасный скрипт для восстановления адресов из секции кода в область расположения виртуальной машины.

И мне хотелось бы пожелать всяческих успехов всем читателям CRACKL@B, которые занимаются очень интересным процессом реверсинга программ.


До встречи в следующем туториале.


vnekrilov



Скачать статью "Распаковка ASProtect 2.3 SKE, часть 3" в авторском оформление + файлы.



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


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