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

ВИДЕОКУРС ВЗЛОМ
выпущен 1 марта!


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


Взлом Asprotect 2.2 SKE, часть 3




Хорошая подборка видеоуроков, инструментов крэкера, книг и статей - здесь.



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


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


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


Код заполнен мусором, а ниже VOEP мы видим инструкцию PUSH 3D083B, за которой идет инструкция CALL 00DD0000. Эта пара инструкций является еще одним типом прыжков на область памяти, созданной asprotect при запуске программы. Поскольку мы должны восстановить Stolen Bytes, то давайте рассмотрим, что делает эта часть кода, для чего начнем трассирование программы с F8, перемещаясь через огромное число мусорных прыжков:


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


Трассируем до тех пор, пока мы не придем до первой пары инструкций PUSH - CALL:


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




При выполнении первых инструкций, которые мы прошли, запускается виртуальная машина, которая направляет нас в область памяти, созданную asprotect, поэтому давайте посмотрим, сколько у нас имеется таких CALL 00DD0000. Для этого выполняем команду Search for → All commands:


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


Мы видим много CALL на эту область памяти asprotect. Теперь давайте рассмотрим назначение инструкции PUSH 3D04CD, которая находится непосредственно перед инструкцией CALL 00DD0000.


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


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


Окно стека нам показывает информацию о том, что из CALL 00DD0000 программа вернется на адрес 003D04CD. Поэтому, можно относительно легко восстановить украденный пакером код; для чего достаточно поместить в каком-то месте восстановленный украденный код, и направить к этому коду все прыжки и CALL, которые идут в данную область памяти asprotect. Все, что нам нужно для этого сделать, это поместить код данной области памяти Asprotect на свободном месте какой-либо секции файла dump_.exe. А если мы не найдем достаточного количества свободного места, то тогда мы должны создать дополнительную секцию в файле dump_.exe, и сделать размер этой секции таким же, какой имеет эта область памяти. Проходим на вкладку Memory Map, и находим область памяти 003D0000, куда нас отправляет инструкция PUSH:


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


Здесь мы видим размер этой области памяти - 3000; ниже этой области памяти мы видим еще одну область памяти того же самого размера. Но нам, как правило, потребуется только первая область памяти.


Такое свободное место находится в секциях файла dump_.exe, которые мы назвали .bss и .reloc. Первая секция имеет размер 5000 байтов, а вторая секция - 24000 байтов:


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


Необходимо проверить, используются ли эти секции файла при запуске программы, для чего мы переходим на OEP, и устанавливаем BPM on access на секцию .bss. Нажимаем клавишу F9, и программа останавливается на записи данных в эту секцию. Значит, эта секция отпадает. Теперь устанавливаем BPM on access на секцию .reloc, и программа здесь не останавливается. Значит, восстановленный код VM мы сможем записать в секцию .reloc. Однако проведем некоторую доработку этой секции; мы разделим ее на две части, одна часть будет иметь размер 21000 байтов, и будет содержать данные Base Relocation Table, а вторая часть будет иметь размер 3000 байтов, в которую мы запишем восстановленный код VM. Для этих действия мы опять будем использовать PE Tools v1.5 RC7, с помощью которого сначала удаляем секцию .rsrc, и затем корректируем размер секции .reloc:


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


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


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


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


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


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


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


Сначала, чтобы Вы имели представление о дальнейшей работе, я расскажу о том, что мы будем делать. Область памяти, в которой находится VOEP, заполнена кодом, который украл пакер. В предыдущих версиях asprotect были Stolen bytes, которые можно было найти, скопировать, и вставить на то место, где должна быть OEP. В новых версиях asprotect, все обстоит уже далеко не так; здесь asprotect ворует большое число байтов кода, а не несколько байтов, что значительно усложняет работу по их восстановлению. Поэтому здесь мы поступим иначе; мы скопируем области памяти, в которой находится украденный код, и вставим его в секцию .code файла dump_.exe, начиная с адреса 00670000. Протектор ASProtect крадет код, который находится, главным образом, в области OEP, и помещает его в область памяти, где находится VOEP, но плохо здесь то, что весь этот код разорван множеством переадресаций к другим областям памяти программы, с помощью инструкций PUSH - CALL. Зачем так сделал автор Asprotect? Этим действием он намного усложняет процесс распаковки программы, а, кроме того, криптует украденный код, чтобы его нельзя было отыскать вручную.


Этот закриптованный код Asprotect как раз и раскриптовывает с помощью инструкции CALL 00DD0000. Раскриптованный код выполняется программой, после чего выполняется прыжок на адрес, который указан в инструкции PUSH, откуда и продолжается выполнение следующих инструкций программы. Следовательно, нам нужно восстановит все эти PUSH и CALL, что похоже на те действия, которые мы делали с прыжками в IAT.


Я полагаю, что более или менее понятно изложил этот материал. Если что-то осталось непонятным, я надеюсь, что это станет понятным, когда мы будем делать переадресацию всех PUSH и CALL 00DD0000 в нашу новую секцию файла dump_.exe.


Нам надо определить значение инструкции PUSH для секции IAT файла dump_.exe. Это значение можно легко вычислить по следующей формуле:


Новое значение = PUSH - BASE + NEWBASE, где PUSH - это значение, записанное в инструкции PUSH (003D04CD); BASE - это базовый адрес данной области памяти (003D0000); NEWBASE - это базовый адрес начала области размещения кода VM в секции IAT файла dump_.exe (00670000).


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


Новое значение = 003D04CD - 003D0000 + 00670000 = 006704CD


Таким образом, этот PUSH восстанавливаем как PUSH 006704CD.


Мы узнали, как восстанавливать значения инструкций PUSH, и теперь нам надо выяснить, как мы будем восстанавливать инструкции CALL 00DD0000, для чего входим в этот CALL с F7. Попадаем сюда:


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


Мы видим очень много мусорного кода, но если прокрутим код немного вниз, то увидим call типа CALL EXX, где EXX может быть любым регистром:


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


Инструкция CALL EXX указывает на функцию, которая реализует идентификацию украденной инструкции; устанавливаем BP на CALL EDI, и нажимаем F9. Удаляем BP, входим в CALL EDI по F7:

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


Прокручиваем код вниз до тех пор, пока не найдем следующий CALL; выше которого расположены следующие инструкции:



  PUSH EXX
  MOV EXX, EXX
  MOV EXX, DWORD PTR SS:[EXX+18]  
  MOV EXX, EXX
  CALL: 00ХХХХХХ → ЭТО ТОТ CALL, КОТОРЫЙ НАС ИНТЕРЕСУЕТ
  DEC EDI
  ...
  


Выше этого CALL расположен еще один CALL EXX (в нашем случае CALL EDX), который занимается выборкой и декодированием хэшей из таблицы хэшей. Сразу же, после него производится сравнение полученного хэша с хэшем текущей функции. Если они не равны, то проверяется, не является ли этот хэш последним хэшем в таблице. Если это - последний хэш, то выдается ошибка “Error: 111”. А вот CALL по адресу 00C64C4F выполнится только тогда, когда хэши совпали. Вот этот CALL и есть эмулятор украденных инструкций:



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


Устанавливаем BP на CALL, как это показано на рисунке, и нажимаем F9. Удаляем BP, и входим в CALL по F7:


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


Немного ниже мы видим очень интересное место:


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


Инструкция 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, восстановление импорта, инлайн-патч)”. Мы же прокручиваем код вниз до тех пор, пока не найдем прыжок JMP [EAX+20]:


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


Устанавливаем на него BP, и нажимаем F9, удаляем BP, и нажимаем клавишу F8, чтобы выполнить прыжок. Попадаем сюда:


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


Здесь мы видим начало другой области памяти, созданной Asprotect, и трассируем программу с F8 до тех пор, пока не увидим следующий прыжок JMP [ESP-4]:


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


Здесь находится самый главный прыжок JMP, который мы искали, поэтому устанавливаем на него Hardware BP. Нажимаем клавишу F9, останавливаемся на прыжке, и смотрим информацию в нижнем окне справки:




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


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


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


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


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


Теперь прыжок выполняется на область .CODE; т.е. мы выходим из виртуальной машины. Давайте подведем итоги проделанной работы. Из VOEP, мы пришли сюда:


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


Инструкция PUSH 3D04CD указывает адрес возврата из CALL 00DD0000, причем этот адрес возврата находится в области виртуальной машины. Инструкция CALL 00DD0000 также выполняет прыжок на адрес 003D0698, который также находится в области виртуальной машины. Следовательно, поскольку область виртуальной машины мы будем переносить в созданную нами секцию в файле dump_.exe, то эти инструкции мы можем записать так:





  003D054F    PUSH 006704CD     ; адрес возврата (в добавленную нами секцию)
  003D0554    JMP АДРЕС_В_НАШЕЙ_ОБЛАСТИ_IAT  ; переадресация CALL
  


Мы уже знаем, как можно восстанавливать значение инструкции PUSH, и теперь нам надо посмотреть, как можно восстановить инструкцию CALL.

Мы видели, что прыжок JMP [ESP-4] первый раз выполняется на область памяти 003D0000, а второй раз - на область секции .CODE (это видно на предыдущих рисунках). Следовательно, при восстановлении инструкции CALL возможны два варианта.


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


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


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


Новый адрес = (Адрес, записанный в [ESP-4] прыжка JMP [ESP-4]) - (Адрес, на котором расположена инструкция CALL 00DD0000) - 5


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


Новый адрес = 003D0698 - 003D0554 - 5 = 0000013F; такие вычисления я делаю в калькуляторе Windows, потому что вычисление в command bar, когда из меньшего числа вычитается большее число, дает неправильный результат.


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


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


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





  003D054F  	68 CD646D00  	PUSH 006704CD 	; адрес возврата (в добавленную нами секцию)  
  003D0554  	E9 3F010000   	JMP 003D0698  	; переадресация CALL
  


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


2-й вариант. Адрес, на который прыгает JMP [ESP-4], находится в секции .code:


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


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


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


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


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


В нашем случае получаем: 003D0DF8.


Записываем script, который, когда программа остановлена на VOEP, будет искать все CALL 00DD0000, затем будет искать прыжок JMP [ESP-4], и установит Hardware BP на этот прыжок; затем он восстановит CALL 00DD0000 и PUSH, и продолжит свою работу со следующей парой PUSH - CALL.

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


Теперь мы можем написать скрипт:



  //Разработчик скрипта: Ulaterck, доработка - vnekrilov.
  //Описание: Скрипт разработан для восстановления инструкций PUSH - CALL в области памяти VOEP
  //программы chmEditor, защищенной Asprotect v2.2.
  //Цель: chmEditor.exe
  //Условия применения: ODBGScript 1.48, Olly Advanced v1.26 Beta 12, PhantOm v0.60.
 
var OPCode // Переменная для хранения опкода инструкций var VOEP_BASE // Переменная для хранения базового адреса области VOEP var ESP_4 // Переменная для хранения значения инструкции JMP [ESP-4] var Address_CALL // Переменная для хранения адреса, где имеется CALL 00DD0000 var Field_Asprotect // Переменная для хранения области памяти CALL 00DD0000 var Temp_Field_Asprotect // Переменная для временного хранения памяти CALL 00DD0000 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,003D0000 // Указываем адрес начала поиска CALL 00DD0000 mov VBASE,003D0000 // Сохраняем базовый адрес области памяти VOEP mov End_scan,003D0DF8 // Указываем адрес последнего CALL 00DD0000 mov Field_Asprotect,00DD0000 // Указываем значение, которое имеет CALL 00DD0000. mov Temp_Field_Asprotect,00DD0000 // Сохраняем значение CALL 00DD0000 в переменной mov Field_JMP,00DE0000 // Указываем область памяти, где находится JMP [ESP-4] mov NEWBASE,00670000 // Сохраняем начало созданной секции в файле dump_.exe
SearchJMP: // Поиск адреса прыжка JMP [ESP-4] findop Field_JMP,#FF6424FC# // Ищем опкод прыжка JMP [ESP-4] mov Address_JMP,$RESULT // Сохраняем найденный адрес прыжка JMP [ESP-4].
SearchCall: // ** Процедура поиска CALL 00DD0000 ** findop Start_scan,#E8# // Ищем опкод инструкции Call (E8) add Start_scan,1 // Прибавляем к найденному значению 1 mov Address_Opcode_Call,$RESULT // Сохраняем адрес найденного опкода инструкции call sub Field_Asprotect,Address_Opcode_Call // Вычитаем из 00DD0000 адрес, где находится CALL sub Field_Asprotect,5 // Из полученного значения вычитаем число 5 add $RESULT,1 // К полученному результату добавляем 1 cmp [$RESULT],Field_Asprotect // Полученный результат сравниваем с 00DD0000 mov Field_Asprotect,Temp_Field_Asprotect // Восстанавливаем значение в переменной jne SearchCall // Если это не CALL 00DD0000, то продолжаем поиск mov Address_CALL,Address_Opcode_Call // Сохраняем адрес найденного опкода Call 00DD0000 mov eip,Address_CALL // Изменяем EIP на адрес найденной инструкции Call 00DD0000 mov Temp_EIP,Address_CALL // Сохраняем, на всякий случай, адрес Call 00DD0000. log Address_CALL // Регистрируем адрес, на котором находится Call 00DD0000
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 00DD0000 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 00DD0000 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 00DD0000 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 00DD0000, если ESP-4 не в области VOEP mov OPCode,Temp_EIP // Записываем адрес Call 00DD0000 в переменную 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 00DD0000 je Message // Если это последний Call 00DD0000, прыгаем на message jmp SearchCall // Если нет, ищем следующий Call 00DD0000
EditCall1: // Редактирование Call 00DD0000, если 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.


Примечание: Следует отметить, что скрипт, при восстановлении последнего CALL 00DD0000, который находится по адресу 003D0DF8, очень часто дает сбой, при выполнении команды Новый EIP здесь. При эмуляции украденного кода, в регистр EAX записывается значение 8, вместо адреса программы, и, при выполнении этого кода вызывается системная SEH, с появлением знакомой заставки. В этом случае, я поступил следующим образом: запустил второй отладчик OllyDbg, установил BP на CALL, на котором произошел сбой в работе скрипта, нажал клавишу F9, и программа остановилась на BP. Восстановил вручную этот CALL, и записал полученные данные в программе, запущенной в первом отладчике. Это позволило мне сохранить проделанную работу.


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


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


Открываем PETools, и дампируем область памяти с восстановленными инструкциями PUSH и CALL, которая находится по адресу 003D0000:


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




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


Сохраняем полученный дамп области памяти с именем Recovery_VM.bin.


В отладчике OllyDbg открываем файл dump_.exe, а в HexWorkshop - только что выполненный дамп области памяти VOEP - Recovery_VM.bin. В окне dump отладчика нажимаем клавиши Ctrl+G, и вводим адрес секции IAT, куда мы будем вставлять восстановленный код VM - 00670000.


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


Переходим в HexWorkshop, и всю сдампированную область памяти:


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


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




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


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


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


003D025E - 003D0000 + 00670000 = 0067025E


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


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


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



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


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


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


Идет ссылка на адрес 003D03B1, который был в области VOEP оригинальной программы. Давайте посмотрим, откуда он вызывается. Нажимаем кнопку OK, и, поскольку в окне стека мы ничего не видим, то нажимаем клавиши Alt+K, чтобы перейти в окно стека; там видим:


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


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


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


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


003D03B1 - 003D0000 + 00670000 = 006703B1


Однако мы не будем сейчас изменять адрес этого прыжка, поскольку таких прыжков в программе может быть очень много. Нам необходимо написать скрипт, который должен найти и восстановить все прыжки, идущие в область VM. Такой скрипт написал PE_Kill; к этому скрипту я добавил свои комментарии. Работа этого скрипта основана на инжекции кода подпрограммы, которая сканирует всю секцию кода, находит прыжки, которые прыгают на VM, и изменяет адрес этих прыжков на секцию IAT, куда мы вставили восстановленную VM:




  //Разработчик скрипта: PE_Kill, комментарии - vnekrilov.
  //Описание: Скрипт разработан для восстановления прыжков, идущих в VM, для
  //программы chmEditor, защищенной Asprotect v2.2.
  //Цель: chmEditor.exe
  //Условия применения: ODBGScript 1.48, Olly Advanced v1.26 Beta 12, PhantOm v0.60.
 
var scan_start // Переменная для хранения адреса начала сканирования кода var scan_end // Переменная для хранения адреса конца сканирования кода
var RegionVM_Start // Переменная для хранения адреса начала области VM var RegionVM_End // Переменная для хранения адреса конца области VM var RegionMain_Start // Переменная для хранения адреса начала области // восстановленной VM (в секции IAT)
var OEP // Переменная для хранения адреса OEP var Info // Переменная для записи информации LOG var Counter // Переменная для хранения чтсла обработанных JMP // на область VM var temp // Переменная для настройки работы скрипта
mov Counter,0 // Записываем в счетчик начальное значение - 0 mov RegionVM_Start,003D0000 // Адрес начала области VM mov RegionVM_End,003D3000 // Адрес конца области VM mov RegionMain_Start,00670000 // Адрес начала восстановленной области VM в dump_.exe
mov OEP,eip // Сохраняем адрес OEP
mov temp,eip // Записываем в переменную temp адрес OEP mov scan_start,[eip] // Записываем в переменную scan_start байты OEP mov [eip],#6A00# // Записываем в OEP инструкцию PUSH 0 sto // Выполняем F8 add temp,4 // Прибавляем к значению OEP число 4 mov scan_end,[temp] // Полученное значение записываем в переменную scan_end asm eip,"call GetModuleHandleA" // Записываем в EIP вызов API GetModuleHandleA, для // получения в регистре EAX базового адреса нашего файла sto // F8 (Выполняем CALL GetModuleHandleA) mov eip,OEP // Переходим на адрес OEP (Команда Новый EIP здесь) mov [eip],scan_start // Восстанавливаем оригинальные байты OEP mov [temp],scan_end // Записываем адрес конца сканирования кода add eax,1000 // К базовому адресу добавляем 1000 (получаем адрес начала // секции кода) mov scan_start,eax // В переменную scan_start записываем адрес 00401000 // (начало сканирования кода) mov scan_end,eax // В переменную scan_start записываем адрес 00401000 // (начало сканирования кода) gmi scan_start,CODESIZE // Получаем размер секции кода add scan_end,$RESULT // Прибавляем к 401000 размер секции кода, чтобы // получить адрес конца сканирования кода

// Регистрация в LOG основных данных при восстановлении прыжков на VM (для контроля) log "__________________________________________________" log "" log "Запуск переадресации прыжков на Virtual Machine..." log "_____________________________________________________" eval "Диапазон сканирования [{scan_start}..{scan_end}] ..." mov Info,$RESULT log Info

mov eip,scan_start //Выполняем команду Новый EIP здесь на адресе 401000 sub eip,200 // Указываем место для записи кода инжектора (на этом // месте записан мусорный код)


// Записываем код подпрограммы-инжектора mov [eip],#60413BCA73238039E975F6418B0103C183C0043BC672EA3BC773E62BC603C32BC183E804890145EBD861# sto // F8 (выполняем инструкцию PUSHAD) mov ecx,scan_start // Записываем в ECX адрес начала сканирования кода dec ecx // Уменьшаем значение ECX на единицу mov edx,scan_end // Записываем в EDX конец сканирования кода mov ebx,RegionMain_Start // Записываем в EBX адрес начала восстановленной VM в // секции IAT mov ebp,0 // Обнуляем регистр EBP (используем его как счетчик // восстановленных JMP) mov esi,RegionVM_Start // Записываем в ESI адрес начала VM mov edi,RegionVM_End // Записываем в EDI адрес конца VM add eip,28 // Переходим на инструкцию POPAD bp eip // Устанавливаем на эту инструкцию BP, для остановки // программы sub eip,28 // Возвращаем начальное значение EIP run // Запускаем инжектор bc eip // Удаляем BP mov Counter,ebp // записываем число восстановленных JMP в переменную // Counter sto // F8 (Выполняем инструкцию POPAD) sub eip,2A // Возвращаемся в начало кода подпрограммы-инжектора fill eip,30,00 // Заполняем код подпрограммы-инжектора нолями mov eip,OEP // Переходим на OEP (Выполняем команду Новый EIP здесь) bp eip // Устанавливаем BP на OEP ai // Выполняем Animate into в OllyDbg bc eip // Удаляем BP
// Оцениваем число восстановленных прыжков eval "Готово! Всего было восстановлено {Counter} прыжков!" msg $RESULT // Выводим сообщение pause // Делаем паузу в работе скрипта ret // Выход из скрипта

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


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




Вначале, с помощью инструкции PUSHAD мы сохраняем в стеке значения всех регистров, и начинаем поиск в секции кода инструкции JMP (опкод E9), увеличивая значение регистра ECX на 1. Как только найдена инструкция прыжка, проверяем назначение этого прыжка (прыгает ли он на область памяти VM). Если он не прыгает на область памяти VM, производим поиск другого прыжка, (поиск прыжка выполняется с адреса нахождения предыдущего прыжка). Если же прыжок выполняется на область VM, то из его адреса вычисляется адреса начала области VM, к полученному адресу прибавляется базовый адрес в секции IAT, куда мы вставили восстановленную VM, вычисляются байты прыжка на восстановленную VM, и корректируется адрес назначения этого прыжка.


Запускаем скрипт, и через секунду получаем сообщение:


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


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


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


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




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


Почему же появилось это сообщение. Перезагружаем программу dump_.exe в отладчике, запускаем ее, нажав клавишу F9. Нажимаем клавиши Alt+M, и устанавливаем BPM on access на добавленный нами код VM в секции IAT - 00649600 (выделяем всю область памяти до ее конца). Закрываем программу, и останавливаемся здесь:


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


Параллельно выполняем те же самые действия с оригинальной программой; при этом, в оригинальной программе находим все инструкции CALL 00DD0000, и на каждой инструкции устанавливаем BP:


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


Устанавливаем также BP на инструкции JMP DWORD PTR SS:[ESP-4], как это мы делали в скрипте для восстановления инструкций CALL 00DD0000.



И сравниваем значение JMP DWORD PTR SS:[ESP-4] в оригинальной программе с соответствующим значением в распакованном файле dump_.exe. Достаточно быстро находим первую ошибку. У нас скрипт определил следующий прыжок по адресу 0064A1F3:


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


При трассировании оригинальной программы, и выполнении инструкции CALL 00DD0000, расположенной по адресу 003D0BF3, мы получаем прыжок на адрес 003D0427. Поэтому корректируем адрес этого прыжка:


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


Здесь нет ошибки в работе скрипта; при установке EIP на адресе 003D0BF3 (расположение инструкции CALL 00DD0000), когда программа остановлена на VOEP, и выполнении этого CALL, мы получаем значение прыжка - 003D0D7D. Но, при остановке программы на адресе 003D0BF3 (при ее закрытии), и выполнении инструкции CALL 00DD0000, мы получаем адрес прыжка - 003D0427. Поэтому и корректируем значение адреса прыжка на 00649A27.


При проведении одновременного трассирования оригинальной и распакованной программ, мы находим еще 5 прыжков с неверными адресами:


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


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


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


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


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


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


Сохраняем изменения, запускаем программу, и она нормально закрывается. Проверяем работу распакованной нами программы, загрузив файл .chm, который поставляется вместе с программой. Процесс редактирования этого файла выполняется нормально. Хотя имеется несколько вопросов к данной программе:


  1. Появляется NAG с сообщением о том, что у нас тестовая версия программы.

  2. Не работает функция “Сохранить как…”, поскольку она требует регистрации программы.


Конец части III




Скачать статью "Взлом Asprotect 2.2 SKE, часть 3" в авторском оформление + файлы.
пароль архива на картинке



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


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