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

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

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

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


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




Массу крэкерских инструментов, видеоуроков и статей вы сможете найти на видеокурсе от нашего сайта. Подробнее здесь.



Восстановление IAT таблицы


Теперь переходим к восстановлению таблицы IAT. При поиске VOEP мы использовали метод, связанный с раскриптовкой пакером таблицы IAT, которая расположена в программе в секции 00647000, и имеет размер 5000 байтов. Сначала нам надо найти начало и конец IAT. Следует отметить, что вся секция 00647000, помимо элементов IAT, содержит массу мусорного кода, который затрудняет определить границы таблицы IAT, особенно для неопытных cracker’s. Поэтому, чтобы очистить IAT от мусорного кода, мы сделаем следующее.


Загружаем программу в отладчик, и выполняем все те действия, когда мы искали VOEP программы. Переходим в Memory Map, нажав клавиши Alt + M, и устанавливаем BPM on write на область памяти 00647000. После нескольких остановок программы на BP, которые мы устанавливали на выходе из цикла раскриптовки, и BPM on write, мы окажемся здесь:


Восстановление IAT таблицы


Удаляем BPM on write, и проходим до инструкции RET с F8. Переходим в окне dump на адрес начала секции IAT - 00647000, выделяем всю эту секцию, и выполняем команду Binary → Fill witch 00’s:


Восстановление IAT таблицы


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



Восстановление IAT таблицы


Этот фактор для нас очень важен; ASProtect очень сильно засоряет таблицу IAT мусорным кодом, поэтому мы можем убрать весь мусорный код из таблицы IAT, что намного облегчит нам ее восстановление.


Примечание: Я делал два варианта восстановления таблицы IAT; без очистки от мусорного кода, и с очисткой от мусорного кода. В первом случае скрипт не смог найти 16 API, и дописал их в конец таблицы IAT. Во втором случае, скрипт нашел все API, и ничего не дописал в секцию кода. Кроме того, поскольку секция IAT - практически пустая, я решил восстановить таблицу IAT с помощью ImpREC на ее родное место, т.е. по адресу начала таблицы IAT - 00647CB4. Когда таблица IAT была заполнена мусорным кодом, то, при загрузке программы, отладчик показал ошибку (0хс00000005), которая означает повреждение таблицы IAT. Когда же таблица IAT была очищена от мусорного кода, программа нормально загрузилась в отладчик.


Теперь найдем начало и конец IAT:


Восстановление IAT таблицы

Восстановление IAT таблицы


Итак, у нас имеются все необходимые данные для использования ImpREC:


VOEP: 003D025E

НАЧАЛО IAT: 00647CB4; для ImpREC 00647CB4 - 00400000 = 00247CB4

КОНЕЦ IAT: 00648748

РАЗМЕР: 00648748 - 00647CB4 = A94


Посмотрим, вся ли таблица IAT заполнена реальными адресами API; запускаем ImpREC, и вводим значения начала IAT и ее размер (значение OEP оставляем таким, каким его определил ImpREC):


Восстановление IAT таблицы


Нажимаем кнопку Get Imports:


Восстановление IAT таблицы


Мы видим, что ImpREC не распознал два элемента, т.е. два элемента ссылаются на область памяти, которую создал Asprotect во время загрузки программы. И второе, что бросается в глаза - таблица IAT содержит у нас только 21C распознанных элементов, и 2 нераспознанных элемента, что явно недостаточно. Для сведения, если мы запустим ImpREC на программе с секцией IAT, которая не заполнена нолями, то мы увидим следующее:




Восстановление IAT таблицы


Здесь мы видим, что таблица IAT содержит 3D1 распознанных элементов, и 1B7 нераспознанных элементов, что имеет намного более реальную картину.


Давайте рассмотрим, почему протектор записывает часть элементов таблицы IAT с правильными адресами, а остальную часть - с неправильными адресами.


Перезагружаем программу, переходим в Memory Map, нажав клавиши Alt + M, и устанавливаем BPM on write на область памяти 00647000. После нескольких остановок программы на BP и BPM on write, мы окажемся здесь:


Восстановление IAT таблицы


Здесь мы видим, что пакер будет записывать реальный адрес API SysFreeString (77114850 - на моей машине) по адресу 00647CB4, который является началом нашей таблицы IAT. Нажимаем клавишу F8, и выполняем инструкцию MOV; по адресу 00647CB4 теперь записан реальный адрес API SysFreeString. Давайте продолжим трассирование программы с F8, и попадаем сюда:


Восстановление IAT таблицы


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



Восстановление IAT таблицы


Здесь выполняется цикл раскриптовки элементов API в таблицу IAT. Проходим на начало цикла, и видим 4 инструкции CALL 00C63B84 с тремя условными прыжками JNZ. Продолжаем трассирование до инструкции CALL, в которую входим с F7:


Восстановление IAT таблицы


Попадаем сюда:


Восстановление IAT таблицы


Выполняем этот прыжок, и попадаем сюда:


Восстановление IAT таблицы

Восстановление IAT таблицы


Обратите внимание на регистры. В регистре EDI записано имя API, а регистре ESI - идентификатор, который связан с именем API. На адресе 00C63BB9 устанавливаем Hardware BP on execution.


Если немного далее потрассировать этот CALL, то мы можем увидеть связь идентификатора, который записан в регистре ESI, с дальнейшим поведением пакера, и найти очередные CALL, которые записывают в регистр EDI имя API. Если мы несколько раз будем нажимать клавишу F9, и, при остановке программы на Hardware BP on execution, понаблюдаем за регистром ESI, то мы увидим очень интересную картинку - регистр ESI принимает следующие значения: 4D, EF, B2, и т.д. Давайте напишем небольшой скрипт, который зарегистрирует связь значения, записываемого в регистре ESI с именем API, записываемого в регистре EDI:



  // Разработчик скрипта: vnekrilov.
  // Описание: Скрипт разработан для регистрации значений регистров ESI и EDI при записи таблицы
  // IAT для программы chmEditor, защищенной Asprotect v2.2.
  // Цель: chmEditor.exe
  // Условия применения: ODBGScript 1.48, Olly Advanced v1.26 Beta 12, PhantOm v0.60, программа  
  // остановлена на Entry Point
 

bphws 00c63bb9,"x" // Устанавливаем Hardware BP на адресе, где можно регистрировать значения // регистров ESI и EDI bphws 00c63f0c,"x" // Устанавливаем Hardware BP на выходе из цикла раскриптовки API
running: run // Запускаем программу eob login // Прыгаем на метку login:, если остановились на Hardware BP
login: cmp eip,00c63f0c // Проверяем, остановились ли мы на Hardware BP je final // Если равно, прыгаем на метку final log esi // Регистрируем значение регистра ESI log edi // Регистрируем значение регистра EDI jmp running // Продолжаем выполнение цикла
final: bphwc 00c63bb9 // Удаляем Hardware BP bphwc 00c63f0c // Удаляем Hardware BP ret // Завершаем работу скрипта


Перезагружаем программу, переходим на вкладку LOG, нажав клавиши Alt + L, и выбираем команду Log to file. Запускаем скрипт. После завершения его работы, закрываем файл регистрации, и просматриваем его в блокноте:



  00C63BB9  Аппаратная breakpoint 1 на00C63BB9
            esi: 0000004D
            edi: 0012FE02
  00C63BB9  Аппаратная breakpoint 1 на00C63BB9
            esi: 0000004D
            edi: 0012FE02 | ASCII "SysFreeString"
  00C63BB9  Аппаратная breakpoint 1 на00C63BB9
            esi: 0000004D
            edi: 0012FE02 | ASCII "SysReAllocStringLen"
  00C63BB9  Аппаратная breakpoint 1 на00C63BB9
            esi: 000000EF
            edi: 0012FE02 | ASCII "SysAllocStringLen"
  00C63BB9  Аппаратная breakpoint 1 на00C63BB9
            esi: 000000EF
            edi: 0012FE02 | ASCII "SysAllocStringLen"
  00C63BB9  Аппаратная breakpoint 1 на00C63BB9
            esi: 000000EF
            edi: 0012FE02 | ASCII "SysAllocStringLen"
  00C63BB9  Аппаратная breakpoint 1 на00C63BB9
  


Анализ этого файла показывает, что, если регистр ESI имеет значения 4D, B1 и B2, то выполняется запись адресов API в таблицу IAT. Если же регистр ESI имеет значение EF, то запись API в таблицу IAT не выполняется, и, кроме того, пакер не определяет имена этих API.


Примечание: Здесь следует отметить, что получаемое имя API всегда записывается по адресу стека 0012FE02. Поэтому, при выполнении цикла восстановления IAT, сначала в регистр EDI помещается адрес стека 0012FE02, с записанным в нем именем последней восстановленной API. А затем в регистр ESI записывается идентификатор, который указывает, восстанавливать ли значение API по этому адресу, или нет. По этой причине, в полученном файле log, мы видим, что регистр ESI содержит значение EF, а в регистре EDI три раза записано имя последней восстановленной API - SysAllocStringLen, хотя эта API была восстановлена на предыдущем проходе цикла, когда регистр ESI имел значение - 4D.


Таким образом, мы нашли плохое значение, которое не позволяет записать в таблицу IAT реальные адреса API:


Восстановление IAT таблицы


Восстановление IAT таблицы


Если мы в регистре ESI запишем значение 4D вместо значения EF, то, очевидно, мы можем восстановить все адреса API в таблице IAT (я в скрипт подставлял все три значения 4D, B1 и B2, но восстановление адресов API в таблице IAT обеспечивает только значение 4D).


Примечание: Для разработки script, мы видим, что, когда программа остановлена на Hardware BP on execution, мы можем посмотреть содержимое регистра ESI, а если мы посмотрим на содержимое регистра EBP в стеке, то увидим, что EBP + 20 содержит адрес, куда будет записано значение следующей API.


Восстановление IAT таблицы


Теперь мы можем написать скрипт для восстановления адресов всех API в таблицу IAT:



  // Разработчик скрипта: vnekrilov.
  // Описание: Скрипт разработан для восстановления API в таблице IAT для программы chmEditor,
  // защищенной Asprotect v2.2.
  // Цель: chmEditor.exe
  // Условия применения: ODBGScript 1.48, Olly Advanced v1.26 Beta 12, PhantOm v0.60
 

bphws 00c63bb9,"x" // Устанавливаем Hardware BP на адресе, где можно изменить значение // регистра ESI bphws 00c63f0c,"x" // Устанавливаем Hardware BP на выходе из цикла раскриптовки API bphws 00c63cf8,"x" // Устанавливаем Hardware BP на адресе записи API в IAT (идентификатор 4D) bphws 00c63c19,"x" // Устанавливаем Hardware BP на адресе записи API в IAT (идентификаторы B1 // и B2)
running: run // Запускаем программу
recovery: cmp eip,00c63f0c // Проверяем, остановились ли мы на Hardware BP je final // Если не равно, прыгаем на метку final log esi cmp esi,4d // Проверяем значение esi je continue // Если равно, прыгаем на метку continue cmp esi,b1 // Проверяем значение esi je continue // Если равно, прыгаем на метку continue cmp esi,b2 // Проверяем значение esi je continue // Если равно, прыгаем на метку continue mov esi,4d // Записываем в регистр значение b2
continue: run
loggin: log edx // Регистрируем адрес таблицы IAT log eax // Регистрируем адрес восстановленной API jmp running // Продолжаем цикл раскриптовки IAT
final: bphwc 00c63bb9 // Удаляем Hardware BP bphwc 00c63f0c // Удаляем Hardware BP
bphwc 00c63cf8 // Удаляем Hardware BP bphwc 00c63c19 // Удаляем Hardware BP ret // Завершаем работу скрипта


Перезагружаем программу, очищаем секцию таблицы IAT (00647000) от мусорного кода, и запускаем наш скрипт, который я назвал Recovery_API_IAT.osc. Через несколько минут скрипт заканчивает свою работу, и давайте посмотрим на наш журнал регистрации - log:



            esi: 0000004D
 
edx: 00647CB4 eax: 77114850 | oleaut32.SysFreeString
esi: 0000004D
edx: 00647CB8 eax: 7713C99D | oleaut32.SysReAllocStringLen
esi: 0000004D
edx: 00647CBC eax: 77114B59 | oleaut32.SysAllocStringLen
esi: 000000EF
edx: 00647CC4 eax: 77DC7883 | ADVAPI32.RegQueryValueExA
esi: 000000EF
edx: 00647CC8 eax: 77DC761B | ADVAPI32.RegOpenKeyExA
esi: 000000EF
edx: 00647CCC eax: 77DC6BF0 | ADVAPI32.RegCloseKey


Как видно из журнала регистрации, там, где регистр ESI имел значение EF, теперь восстановлены адреса API в таблице IAT. Теперь нам нужно восстановленную секцию таблицы IAT скопировать в файл. Для этого выделяем всю секцию Таблицы IAT, и выбираем команду binary copy:




Восстановление IAT таблицы



Запускаем HexWorkshop, и повторяем все те действия, которые мы делали при создании файла таблицы INIT. Сохраняем полученный файл с именем Recovery_IAT.


4. Восстановление прыжков на IAT



Перезагружаем программу в Olly, проходим на VOEP с помощью скрипта, переходим в секцию .code на адрес 00401000, и выполняем команду Search for → all intermodular calls. В окне ссылок мы видим много calls типа CALL 003F0000. Наша задача состоит в том, чтобы восстановить все эти прыжки в их оригинальный формат:


Восстановление IAT таблицы


Сначала мы сделаем это вручную, чтобы понять идею восстановления этих прыжков. Выполняем поиск всех CALL 003F0000, выбрав команду Serch for → All commands:



Восстановление IAT таблицы


В окне ссылок видим:


Восстановление IAT таблицы


Запоминаем первое появление этого CALL по адресу 00401338. Теперь посмотрим, где находится последний CALL 003F0000, для чего прокручиваем листинг вниз, и находим следующий адрес:


Восстановление IAT таблицы


Итак, последний CALL 003F0000 находится по адресу 0049AC0C.


Переходим на любой из этих CALL, например, по адресу 00401358:


Восстановление IAT таблицы



Этот CALL мы восстановим вручную; поэтому нажимаем клавиши CRTL + * (команда “New origin here”). Для получения адреса API, Asprotect последних версий использует API VirtualAlloc, а не API GetProcAddress, о чем писал в своей статье PE_Kill. Поэтому переходим на начало API VirtualAlloc, для чего нажимаем клавиши CTRL+G, и вводим имя этой API - VirtualAlloc:



Восстановление IAT таблицы


Восстановление IAT таблицы


Начало API находится по адресу 7C809A81; устанавливаем BP (F2) на адресе возврата API, и вычисляем разницу между этими адресами (7C809A9A - 7C809A81 = 19), которую будем использовать в скрипте.


Теперь устанавливаем BP on access на секцию .code:




Восстановление IAT таблицы


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


Восстановление IAT таблицы




Смотрим в стеке, дважды щелкнув по первой строке, чтобы получить более наглядный вид. В ESP+40 мы видим адрес API WriteFile, которую вызывает этот CALL:


Восстановление IAT таблицы


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


Восстановление IAT таблицы


Теперь адрес этой API находится на ESP+2C.


Следовательно, если адрес данной API записан по двум адресам - ESP+40 == ESP+2C, то все сделано правильно.


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


Восстановление IAT таблицы


Таким образом, мы выяснили, что этот CALL должен быть прыжком на IAT - типа JMP [address IAT]. Причем, это будет именно JMP [IAT] потому, что перед этим прыжком расположен опкод 8BC0, который является опкодом инструкции MOV EAX,EAX. Поскольку мы знаем, что этот CALL выполняет прыжок на API WriteFile, то нам надо найти эту API в таблице IAT. Но, перед поиском этой API, нам надо восстановить корректную таблицу IAT из файла, который мы сделали чуть ранее, и назвали Recovery_IAT.


Запускаем HexWorkshop, открываем в нем файл Recovery_IAT, выделяем и копируем весь код:


Восстановление IAT таблицы


Теперь переходим в окно dump отладчика, выделяем всю секцию таблицы IAT, и выбираем команду binary paste:


Восстановление IAT таблицы



Ищем в Таблице API WriteFile:


Восстановление IAT таблицы




Мы видим, что эта API находится в IAT по адресу 00648354; поэтому мы уже сможем восстановить наш прыжок:


Восстановление IAT таблицы


Удаляем флажок на опции “Fill with NOP’s”, нажимаем клавишу Assemble, и получаем восстановленный прыжок:


Восстановление IAT таблицы


Здесь мы видим, что до и после прыжка находится инструкция MOV EAX,EAX, которая представлена опкодом 8BC0; именно по этой причине мы знаем, что здесь должен быть прыжок, а не CALL.


Итак, приступаем к написанию скрипта, который восстановит все эти CALL:



  //Разработчик скрипта: Ulaterck, доработка - vnekrilov.
  //Описание: Скрипт разработан для восстановления прыжков в таблицу IAT
  //программы chmEditor, защищенной Asprotect v2.2.
  //Цель: chmEditor.exe
  //Условия применения: ODBGScript 1.48, Olly Advanced v1.26 Beta 12, PhantOm v0.60.
 

var RETN_VirtualAlloc // Переменная для хранения адреса RET API VirtualAlloc var Address_Call // Переменная для хранения найденного адреса CALL var Start_IAT // Переменная для хранения адреса начала таблицы IAT var End_IAT // Переменная для хранения адреса конца таблицы IAT var New_IAT // Переменная для записи адресов API, которых нет в IAT var Field_Asprotect // Переменная для хранения области памяти CALL var Start_scan // Переменная для хранения адреса начала сканирования var End_scan // Переменная для хранения адреса конца сканирования var Address_API // Переменная для хранения найденного адреса API var Stack // Переменная для хранения адреса стека var Temp_EIP // Переменная для хранения текущего адреса EIP var OP_Code // Переменная для хранения значения опкода var New_EIP // Переменная для хранения нового адреса EIP var Address_IAT // Переменная для хранения адреса IAT с найденной API var My_API // Переменная для хранения адреса IAT с восстановленной // API var Flag // Переменная для хранения значения флажка

preparation: gpa "VirtualAlloc","kernel32.dll" // Находим адрес API VirtualAlloc. add $RESULT,19 // Прибавляем к адресу 19, чтобы получить адрес RETN. mov RETN_VirtualAlloc,$RESULT // Сохраняем в переменной RETN_VirtualAlloc адрес RETN. mov Start_IAT,00647CB4 // Сохраняем начало таблицы IAT. mov End_IAT,00648744 // Сохраняем конец таблицы IAT. mov New_IAT,0064874C // Указываем адрес, куда будем записывать недостающие API mov Field_Asprotect,003F0000 // Сохраняем адрес, который мы будем искать. mov Start_scan,00401000 // Указываем адрес начала поиска CALL 003F0000. mov End_scan,0049AC0C // После этого адреса CALL, прекращаем поиск mov eip,Start_scan // Устанавливаем EIP на начало секции .code. bp Start_scan // Устанавливаем breakpoint на начало секции .code. run // Запускаем программу. bc Start_scan // Удаляем breakpoint с адреса 401000.
SearchCallAsprotect: // Поиск calls типа CALL 003F0000 (область asprotect) findop Start_scan,#E8# // Ищем инструкцию CALL, которая имеет опкод #E8# mov Address_Call,$RESULT // Сохраняем найденный адрес в переменной Address_Call mov Start_scan,$RESULT // Сохраняем найденный адрес в переменной Start_scan mov New_EIP,$RESULT // Сохраняем найденный адрес в переменной New_EIP. mov Field_Asprotect,003F0000 // В этой области памяти asprotect мы будем работать (CALL // 003F0000) sub Field_Asprotect,Address_Call // Проверяем, является ли найденный CALL требуемым sub Field_Asprotect,5 // CALL 003F0000 add $RESULT,1 cmp [$RESULT],Field_Asprotect // Сравниваем полученные результаты, и если найден jne SearchCallAsprotect // не CALL 003F0000, то прыгаем на начало поиска cmp Start_scan,End_scan // Проверяем, не является ли последним этот Call. je activeflag // Если пришли на последний CALL, прыгаем на activeflag jmp SearchApi // Если это не последний CALL, то прыгаем на поиск API
activeflag: mov Flag,1 // активизируем переменную Flag
SearchApi: // Процедура поиска API, которая соответствует этому прыжку mov eip,New_EIP // Устанавливаем EIP на найденный адрес CALL 003F0000. bp RETN_VirtualAlloc // Устанавливаем breakpoint на RETN API VirtualAlloc run // Запускаем программу. mov Stack,esp // Останавливаемся на RETN, записываем адрес вершины стека. add Stack,40 // Прибавляем к вершине стека число 40 mov Address_API,[Stack] // Записываем содержимое адреса ESP+40 (адрес API) run // Запускаем программу mov Stack,esp // Останавливаемся на RETN, записываем адрес вершины стека add Stack,2C // Прибавляем к вершине стека число 2C cmp Address_API,[Stack] // Проверяем, что адрес в ESP+2C равен адресу в Addr_API je AnalysCall // Если не прыгаем, значит имеются проблемы. bc RETN_VirtualAlloc jmp Error
AnalysCall: // Процедура анализа, что использовать - JMP или CALL bc RETN_VirtualAlloc // Удаляем breakpoint с инструкции RETN mov eip,New_EIP // Устанавливаем EIP на адресе нашего CALL 003F0000 bp New_EIP // Устанавливаем breakpoint на адрес CALL 003F0000 run // Запускаем программу bc New_EIP // Удаляем breakpoint с адреса CALL 003F0000 mov Temp_EIP,New_EIP // Сохраняем в переменной Temp_EIP адрес CALL 003F0000 sub Temp_EIP,2 // Вычитаем из адреса 2 байта mov OP_Code,[Temp_EIP] // Сохраняем результат в переменной OP_Code and OP_Code,0000FFFF // Выполняем логический AND результата переменной OP_Code log OP_Code // Регистрируем полученный результат cmp OP_Code,C08B // Проверяем, являются ли байты перед Call байтами 8BC0 je EditAsJMP // Если байты имеются, то пишем call как JMP [addr IAT] add Temp_EIP,8 // Если нет байтов 8BC0 перед call, проверяем, есть ли они // после CALL. mov OP_Code,[Temp_EIP] // Сохраняем в переменной OP_Code полученный результат and OP_Code,0000FFFF // Выполняем логический AND результата переменной OP_Code log OP_Code // Регистрируем полученный результат cmp OP_Code,C08B // Проверяем, являются ли байты после Call байтами 8BC0 je EditAsJMP // Если равно, то редактируем call как JMP [addr IAT]
EditAsCall: // Если мы редактируем CALL как CALL [addr IAT] mov Address_IAT,Start_IAT // Копируем в переменную Address_IAT адрес начала IAT
continue1: cmp Address_API,[Address_IAT] // Сравниваем полученное значение API со значением в IAT jne next1 // Если не равны, прыгаем на метку next: mov My_API,Address_IAT // Если равны, записываем значение адреса IAT в My_API, jmp build1 // и прыгаем на метку build1:
next1: add Address_IAT,4 // Переходим на следующий адрес IAT cmp Address_IAT,End_IAT // Проверяем этот адрес с концом IAT je newwrite1 // Если прыгаем, значит в таблице IAT нет данной API. jmp continue1 // Если не конец IAT, повторяем цикл
build: // Процедура для формирования прыжка как Call eval "Call dword[{My_API}]" // Оцениваем адрес IAT, где записана найденная API asm New_EIP, $RESULT // Ассемблируем CALL [My_API] в двоичный код cmp Flag,1 // Проверяем, работаем ли мы с последним CALL 003F0000 je Final // Если Да, прыгаем на завершение работы скрипта jmp SearchCallAsprotect // Если Нет, ищем следующий CALL 003F0000
EditAsJMP: // Процедура для формирования прыжка как JMP
mov Address_IAT,Start_IAT // Копируем в переменную Address_IAT адрес начала IAT
continue2: cmp Address_API,[Address_IAT] // Сравниваем полученное значение API со значением в IAT jne next2 // Если не равны, прыгаем на метку next: mov My_API,Address_IAT // Если равны, записываем значение адреса IAT в My_API, jmp build2 // и прыгаем на метку build2:
next2: add Address_IAT,4 // Переходим на следующий адрес IAT cmp Address_IAT,End_IAT // Проверяем этот адрес с концом IAT je newwrite2 // Если прыгаем, значит в таблице IAT нет данной API. jmp continue2 // Если не конец IAT, повторяем цикл
building2: // Процедура для формирования прыжка как JMP eval "Jmp dword[{My_API}]" // Оцениваем адрес IAT, где записана найденная API asm New_EIP, $RESULT // Ассемблируем JMP [My_API] в двоичный код cmp Flag,1 // Проверяем, работаем ли мы с последним CALL 003F0000 je Final // Если Да, прыгаем на завершение работы скрипта jmp SearchCallAsprotect // Если Нет, ищем следующий CALL 003F0000

Error: // Не найдена API в таблице IAT msg "Ошибка, API в данном CALL не получена" jmp Final
newwrite1: add End_IAT,4 // Добавляем к концу IAT 4 байта mov [End_IAT],0 // Делаем разделительные нули между концом IAT и New_IAT mov [New_IAT],Address_API //Записываем недостающее значение API add New_IAT,4 // Увеличиваем размер IAT на 4 байта mov End_IAT,New_IAT // Записываем новое значение конца IAT jmp EditAsCall // Прыгаем новый поиск API в IAT, и восстанавливаем CALL // 003F0000
newwrite2: add End_IAT,4 // Добавляем к концу IAT 4 байта mov [End_IAT],0 // Делаем разделительные нули между концом IAT и New_IAT mov [New_IAT],Address_API //Записываем недостающее значение API add New_IAT,4 // Увеличиваем размер IAT на 4 байта mov End_IAT,New_IAT // Записываем новое значение конца IAT jmp EditAsJMP // Прыгаем новый поиск API в IAT, и восстановление CALL // 003F0000
Final: Ret // Завершаем работу скрипта


Примечание: Этот скрипт предоставлен Ulaterck в своем туториале “Asprotect 2.2 Parte II by Ulaterck”, я его доработал в части записи в конце таблицы IAT тех API, которые отсутствуют в IAT. Дело в том, что хотя при восстановлении таблицы IAT мы восстановили много API за счет применения константы 4D в регистре ESI, однако, как показала работа этого скрипта на таблице IAT, заполненной мусорным кодом, в таблице IAT были восстановлены не все API. Поэтому, я, на всякий случай, добавил в скрипт команды, которые записывают недостающие API в конец таблицы IAT. Я полагаю, что комментарии достаточно полно объясняют работу скрипта. Значения, помеченные желтым цветом, вписываются для каждой конкретной программы.


Удаляем все установленные breakpoints, включая Hardware BP. Перезагружаем программу, и запускаем скрипт OEPFinder, для прохода на VOEP. Когда программа остановилась на VOEP, нам нужно вставить Таблицу IAT и Таблицу INIT на их соответствующие места.


Проходим на начало секции таблицы IAT, которое находится по адресу 00647000, и вставляем скопированные байты восстановленной секции таблицы IAT из HexWorkshop (как это мы делали чуть выше).


Затем вставляем Таблицу INIT, для чего из HexWorkshop копируем байты файла Recovery_INIT:


Восстановление IAT таблицы


И вставляем на ее место в Olly - по адресу 0061A5FC:


Восстановление IAT таблицы



Теперь можем запускать наш скрипт, который я назвал Recovery_CALL_AS_JMP_OR_CALL .osc. Ждем около 5 минут, пока скрипт не закончит свою работу, и видим, что скрипт ничего не добавил в таблицу IAT (т.е. он нашел все нужные API в таблице IAT):


Восстановление IAT таблицы


Давайте, выполним поиск и посмотрим, все ли мы восстановили CALL, для чего выбираем команду Search for → All commands:


Восстановление IAT таблицы


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


Восстановление IAT таблицы


А где же наш CALL? Он спрятан среди мусорного кода, поэтому его и не нашел наш скрипт. Очистим его с помощью инструкций NOP:


Восстановление IAT таблицы


Выбираем команду New origin here, устанавливаем BP (F2) на инструкцию RETN 10 API VirtualAlloc, и нажимаем клавишу F9. Программа остановилась на BP, и смотрим в стеке по адресу [ESP+40]:


Восстановление IAT таблицы


Проверяем правильность этой API, для чего еще раз нажимаем на клавишу F9, и, после остановки программы, смотрим в стеке по адресу [ESP+2C]:



Восстановление IAT таблицы


Теперь переходим на наш CALL. Видим, что он изменил свое значение:


Восстановление IAT таблицы


API GetStdHandle находим в таблице IAT, применив бинарный поиск:


Восстановление IAT таблицы


И заменяем CALL на JMP. Выше этой инструкции должна быть записана инструкция MOV EAX,EAX:




Восстановление IAT таблицы


Точно также восстанавливаем и второй CALL. Сначала находим API GetUserName, нажав клавиши Ctrl+B, и заменяем CALL инструкцией JMP


Восстановление IAT таблицы


Мы закончили восстановление CALL, и теперь переходим на новую OEP, которая находится после конца Таблицы INIT. Когда мы запускали второй скрипт для восстановления таблицы INIT, то видели адрес, где находится наша OEP - 0061AA94. Переходим на этот адрес, и выполняем команду - New origin here:


Восстановление IAT таблицы


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




Восстановление IAT таблицы


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


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

  OEP : 0061AA94 - 00400000 = 0021AA94
  RVA:  00647CB4 - 00400000 = 00247CB4
  SIZE: 00648748 - 00647CB4 = A94
  

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



Восстановление IAT таблицы


У нас еще остались 2 нераспознанных элемента таблицы IAT, которые имеют ссылку на адрес 00C57574. Давайте посмотрим, что находится по этому адресу:


Восстановление IAT таблицы


Но, ведь это - подпрограмма эмуляции API GetProcAddress! Значит, можно смело заменить значение 00C57574 значением API GetProcAddress - 7C80AC28:


Восстановление IAT таблицы



Восстановление IAT таблицы


Снова нажимаем в ImpREC кнопку “Get Imports”, и теперь видим:


Восстановление IAT таблицы


Добавлено 2 распознанных элемента, а не распознанных элементов - 0, т.е. у нас таблица IAT содержит все распознанные API.


Теперь нам нужно вставить таблицу IAT в наш дамп по ее родному адресу 00647CB4. Удаляем флажок из опции “Add new section”, и в окно вводим адрес начала таблицы IAT - 00247CB4:


Восстановление IAT таблицы




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


Восстановление IAT таблицы


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


Восстановление IAT таблицы


Теперь мы можем вырезать лишние секции в нашем файле dump_.exe, которые были добавлены ASProtect. Но сначала давайте проверим, не украл ли пакер какие-либо ресурсы. Для этого запускаем утилиту ResFixer v1.0 beta 1:




Восстановление IAT таблицы


Как видим, пакер действительно украл несколько ресурсов из секции ресурсов .rsrc, и перенес их в созданную им секцию .data. Нам нужно вернуть эти ресурсы в их родную секцию .rsrc, для чего мы будем использовать утилиту GUI for Resource Rebuilder v1.0 by Dr.Golova. Конечно, можно использовать и утилиту ResFixer v1.0 beta 1, однако она не всегда корректно восстанавливает ресурсы. Наиболее корректно работает утилита Resource Rebuilder v1.0 by Dr.Golova:


Восстановление IAT таблицы



Загружаем файл в утилиту, оставляем настройки по умолчанию, нажимаем кнопку “Rebuild”, и утилита восстанавливает секцию ресурсов:


Восстановление IAT таблицы


Теперь мы можем удалить из нашего файла dump_.exe две последние секции, созданные ASProtect, и секцию ресурсов .rsrc, которая сейчас - не полная. Для этого используем утилиту PE Tools v1.5 RC7:


Восстановление IAT таблицы


Нам надо поправить данные Optional Header, для чего нажимаем на три кнопки с вопросительным знаком, которые я на рисунке указал стрелками:



Восстановление IAT таблицы


И вставляем секцию .rsrc из файла rsrc.bin:


Восстановление IAT таблицы


Корректируем имя этой секции на .rsrc:


Восстановление IAT таблицы


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


Восстановление IAT таблицы


В чем же дело. Открываем файл dump_.exe в PE Tools v1.5 RC7, и проходим на вкладку Directory Editor:


Восстановление IAT таблицы


И нам сразу же бросаются в глаза адреса директорий Base Relocation Table, и TLS Directory, которые ранее находились в удаленной нами секции .data. Нам надо перенести эти адреса в другую секцию. Я решил перенести адрес TLS Directory - в секцию, которая находится по адресу 0024E000, а Base Relocation Table - в секцию, которая находится по адресу 0024F000. Почему я принял такое решение? Если мы посмотрим на адрес 0024E000 в HexWorkshop, то увидим следующее:


Восстановление IAT таблицы


А если мы посмотрим на адрес 006AC9E4 в упакованной программе, то увидим:



Восстановление IAT таблицы


Те же самые значения директории TLS, которые пакер перенес в созданную им секцию.


Секция файла, которая начинается с адреса 0024F000, пустая (заполнена нолями), поэтому мы можем сюда перенести Base Relocation Table:


Восстановление IAT таблицы


В упакованной программе по адресу 006AC9DC мы видим:


Восстановление IAT таблицы


Значит, нам нужно вставить эти байты в наш файл dump_.exe:


Восстановление IAT таблицы


Восстановление IAT таблицы


Сохраняем изменения, и корректируем адреса в этих директориях:


Восстановление IAT таблицы


Одновременно корректируем имена секций (за аналог я взял неупакованную программу GridinSoft Notepad v3.2 тех же самых авторов), чтобы придать файлу dump_.exe законченный вид:


Восстановление IAT таблицы


На этом мы закончили работу с доведением дампа dump_.exe до нормального вида.



Конец части II




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



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


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