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

ВИДЕОКУРС ВЗЛОМ
выпущен 12 ноября!


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

Распаковка библиотеки, запакованной Armadillo

Обсудить статью на форуме

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

Автор: Ra$cal <Rascalspb[dog]mail[dot]ru>

Посоветовал мне тут друг хороший Add-In для Micro$ost Visual Studio 2k3 под названием Visual Assist X. Весьма и весьма удобная вещь. Попользовался я им немного и подсел. А он, подлец, работает всего несколько дней. В общем началось все как обычно - PEiD. На этом обычность закончилась :(, ибо красовалась там надпись весьма неприятная - "Armadillo 2.51 - 3.xx DLL Stub -> Silicon Realms Toolworks". Из неприятных неожиданностей - невозможность работы библиотеки при наличии УСТАНОВЛЕННОГО Soft-Ice’a. После колупаний во внутренностях обнаружено, что он ищет NTICE, SOFTICE и BPMSuper (или как то так, точно уже не помню, смотрел давно а айсом не пользуюсь). У системного программиста наверняка должен быть Soft-Ice, поэтому столь жесткая позиция удивляет - ведь библиотека рассчитана как раз на программистов, да к тому же пишущих на C++. В общем весьма странно... Зато у нас появился лишний повод снять эту армадиллу.

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

Ну вот, с лирикой на этом пожалуй надо кончить :)

Чтонам понадобится:
OllyDebug Основной инструмент
PEiD Вроде больше и не понадобится
LordPE Будем дампить и править дамп
ImpRec Будем лечить импорт
Блокнот :) Будем там хранить все необходимое
Калькулятор Если вы Hex числа обсчитываете в уме, он вам не нужен ;)


Начинаем распаковку...


Как обычно, чтобы распаковать программу надо найти OEP, сделать дамп и восстановить таблицу импорта. Начнем ;)


<Unpacking Programm...>


<Find OEP & Dump File…>

Приступим... Чтобы попасть на OEP достаточно поставить мемори бряк на доступ на секцию .text Прервемся где-то здесь:

 1EE4D3DC > 55               PUSH EBP
 1EE4D3DD   8BEC             MOV EBP,ESP
 1EE4D3DF   53               PUSH EBX
 1EE4D3E0   8B5D 08          MOV EBX,DWORD PTR SS:[EBP+8]
 1EE4D3E3   56               PUSH ESI
 1EE4D3E4   8B75 0C          MOV ESI,DWORD PTR SS:[EBP+C]
 1EE4D3E7   57               PUSH EDI
 1EE4D3E8   8B7D 10          MOV EDI,DWORD PTR SS:[EBP+10]
 1EE4D3EB   85F6             TEST ESI,ESI
 1EE4D3ED   75 09            JNZ SHORT VA_X.1EE4D3F8


Это OEP. Дампим... Дамп есть.

</OEP Finded & File Dumped>

<Restore IAT…>


Теперь используя ImpRec пробуем восстановить импорт. Если вы не знаете, как восстанавливать импорт у библиотеки - закрываете эту страницу и идете еще чуток накачать экспу ;) А остальные уже видят, что там куча неопределившихся функций, и все они указывают куда-то на 0037####. Итак, таблица подпорчена. Это плохо, будем разбираться...
Можно поступить разными способами - например смотреть в Оле в запакованной библиотеке, куда ведут эти переходники. Но тут много неудобств - занимает много времени, можем что-нибудь пропустить... да мало ли что еще может нам не понравится :) К этому способу, если что, можно прибегнуть в любой момент.
Мы пойдем по другому пути - попробуем разобраться с алгоритмом заполнения таблицы импорта. Может найдем что-нибудь интересненькое...

Ладно, приступим:

Если вы заметили, импорт начинается с адреса 001BC000 (это секция rdata) - значит будем искать, когда и откуда туда заносятся эти адреса. Перезапускаем олю и на первом эксепшене в окне дампа ставим хард бряк на запись по адресу 1EEBC000. Окажемся здесь:

00396217   8908             MOV DWORD PTR DS:[EAX],ECX
 00396219   8B85 10C8FFFF    MOV EAX,DWORD PTR SS:[EBP-37F0] ; VA_X.1EEBC000
 0039621F   83C0 04          ADD EAX,4
 00396222   8985 10C8FFFF    MOV DWORD PTR SS:[EBP-37F0],EAX
 00396228  ^E9 CEFCFFFF      JMP 00395EFB


Это цикл заполнения таблицы импорта, то есть то, что нам и надо. Обратите внимание, что ниже в таблице уже есть и нормальные адреса и переходники. Значит заполняется она не сверху, но нам надо попасть на самое начало заполнения. Пока это не так важно. Для начала разберемся с переходниками. Начинаем трейсить...

00395F15   83A5 64C2FFFF 00 AND DWORD PTR SS:[EBP-3D9C],0
 00395F1C   8B85 7CC8FFFF    MOV EAX,DWORD PTR SS:[EBP-3784]


Здесь в EAX адрес строки, содержащей название API функции
Это нас особо не интересует. Тут довольно длинный цикл. Смотрим далее:

0039609E   85C0             TEST EAX,EAX
 003960A0   75 11            JNZ SHORT 003960B3
 003960A2   8B85 58C2FFFF    MOV EAX,DWORD PTR SS:[EBP-3DA8]
 003960A8   8B40 08          MOV EAX,DWORD PTR DS:[EAX+8]
 003960AB   8985 64C2FFFF    MOV DWORD PTR SS:[EBP-3D9C],EAX
 003960B1   EB 02            JMP SHORT 003960B5
 003960B3  ^EB 9D            JMP SHORT 00396052
 003960B5   8B85 A4C4FFFF    MOV EAX,DWORD PTR SS:[EBP-3B5C]


Сразу ставим бряк на 003960B5 ( у Вас адрес может быть другой, туда ведет безусловный прыг, до которого программа дойдет, если не сработает условный прыг (JNZ SHORT 003960B3), то есть это выход из цикла, который нас не интересует (цикл нас не интересует %))

Чуток потрейсим:

 003960B5   8B85 A4C4FFFF    MOV EAX,DWORD PTR SS:[EBP-3B5C]	 	
 003960BB   40               INC EAX	;Переходим к следующей API функции	
 003960BC   8985 A4C4FFFF    MOV DWORD PTR SS:[EBP-3B5C],EAX	;Сохраняем	
 003960C2   83BD 64C2FFFF 00 CMP DWORD PTR SS:[EBP-3D9C],0	;Подозрительное сравнение


003960C2 - Довольно подозрительное сравнение, т.к. если там не ноль он идет дальше и дальше и в итоге сохраняет это значение (DWORD PTR SS:[EBP-3D9C) в таблице импорта, но пока там ноль. Можно заметить, что в EAX хранится порядковый номер функции из той библиотеки, которую мы сейчас обрабатываем (сейчас это ADVAPI32). Заглянем в ImpRec и увидим, что вместо адреса 4 функции - переходник. Значит теоретически скоро в DWORD PTR SS:[EBP-3D9C] будет записано 37E06D. Это легко проверить. Трейсим... О да, когда EAX=4 по тому адресу записан именно тот переходник.

А что же происходит, если переходника нет и прыг не срабатывает? Адреса нормальных функций вычисляются вот здесь:

 003960E5   8B85 60C2FFFF    MOV EAX,DWORD PTR SS:[EBP-3DA0]
 003960EB   8985 7CADFFFF    MOV DWORD PTR SS:[EBP+FFFFAD7C],EAX
 003960F1   6A 01            PUSH 1
 003960F3   FFB5 7CADFFFF    PUSH DWORD PTR SS:[EBP+FFFFAD7C];Хэндл библиотеки
 003960F9   FFB5 9CC4FFFF    PUSH DWORD PTR SS:[EBP-3B64]    ;Разыскиваемая функция
 003960FF   E8 F53CFEFF      CALL 00379DF9	


Думаю, вам понятно, как избавиться от переходников... Если нет, поясняю - арма проверяет, есть ли переходник для функции, рассматриваемой в данный момент (CMP DWORD PTR SS:[EBP-3D9C],0), если есть, она сразу записывет его в таблицу импорта программы, если нет, то находит нормальный адрес функции и записывает его. Если занопить прыг после проверки наличия переходника получим нормальный импорт.

Алгоритм заполнения таблицы импорта понятен.

Осталась малость - попасть на формирование таблицы импорта до внесения первой записи. Ставим хард бряк на исполнение на адрес 003960C2 (на проверку наличия переходника), перезапускаемся и останавливаемся. Теперь нопим прыжок (003960C9 75 42 JNZ SHORT 0039610D) и отпускаем библу на волю... А эта сволочь хлоп и загнулась, да и текст теперь красного цвета :( Но нам это уже до лампочки, т.к. правильный импорт арма нам составила. Запускаем ImpRec и натравливаем на скончавшуюся библиотеку, но все еще висящую в памяти на исключении (надеюсь Вы не нажали Shift+F9). OEP указываем 14D3DC и смотрим на импорт - опять левые адреса :( , но их меньше, да и выглядит импорт лучше, без неопределившихся адресов в середине функций из одной библиотеки %). Любопытные могли заметить, что арма заменяет нули-разделители своими адресами, а происходит это во время исключений (тогда же она переходит к другим библиотекам), поэтому смело выкидываем неопределившиеся и фиксим дамп... ImpRec не может добавить секцию :( У Нарваи написано, что делать. Если забыли - надо поменять свойства всех секций на E00000020 и сделать Rebuild. С импортом вроде все. Как видите, ничего особо сложного в этой программе с ним не делалось.

<IAT Restored>

</Programm Unpacked>


Ну вот... Теоретически распаковка закончена. Итак, теперь мы запускаем дамп с исправленным импортом и... видим обращения к каким-то не тем адресам :((( Первый раз (и пока последний ;) это происходит здесь:


1ED1BAFF   90               NOP
 1ED1BB00  -E9 98CE18E3      JMP 01EA899D
 1ED1BB05   E8 E8CF1200      CALL VA_X.1EE48AF2


Это есть антидамп (кто не в курсе - это тот джамп ;). В итоге дамп все равно не рабочий. Но Вы его не удаляйте, мы из него импорт будем брать ;)
Как выяснилось, не все так просто, как хотелось. Типовой распаковки не получилось. Ладно, будем исправлять и эту неприятность.

Fix Antidump…


Теперь берем запакованный файл и продолжаем извращаться с ним.
Способ поиска антидампов абсолютно такой же, как у Нарваи (интересно, а есть другой). Как обычно ставим хард бряк (далее просто бряк) на запись по адресу 1ED1BB01 (т.к. нас интересует сам адрес джампа). Остановимся здесь:

77C32F41   72 29            JB SHORT MSVCRT.77C32F6C
 77C32F43   F3:A5            REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
 77C32F45   FF2495 5830C377  JMP NEAR DWORD PTR DS:[EDX*4+77C33058]


Тут в EDI копируются байты из ESI. Смотрим, что там:

00D1AB1C  90 90 90 90 | E9 98 CE 9D  ђђђђй&#65533;Оќ
 00D1AB24  E2 E8 E8 CF | 12 00 8D 46  вииП_.ЌF


Тут все на лицо - это тот кусок кода, который расположен вокруг антидампа и сам антидамп, E8 CF | 12 00 - а это колл, который после антидампа. Теперь ставим бряк на запись сюда (98 CE 9D E2) - это байты прыжка. Следите за содержимым дампа, оно будет довольно часто меняться. Тормознемся тут:

00394EE1   8B85 CCC5FFFF    MOV EAX,DWORD PTR SS:[EBP-3A34]
 00394EE7   0385 A4C5FFFF    ADD EAX,DWORD PTR SS:[EBP-3A5C]
 00394EED   8B8D ACC5FFFF    MOV ECX,DWORD PTR SS:[EBP-3A54]
 00394EF3   8908             MOV DWORD PTR DS:[EAX],ECX
 00394EF5   EB 0E            JMP SHORT 00394F05
 00394EF7   8B85 A8C5FFFF    MOV EAX,DWORD PTR SS:[EBP-3A58]
 00394EFD   8B8D ACC5FFFF    MOV ECX,DWORD PTR SS:[EBP-3A54]
 00394F03   8908             MOV DWORD PTR DS:[EAX],ECX
 00394F05  ^E9 0AFFFFFF      JMP 00394E14


Опять цикл. Видим, что байты записаны из ECX, а в ECX они попадают из EBP-3A54. Посчитаем теперь, сколько это. У меня это 6B394. Ставим бряк на запись туда...

00394E23   8B85 B0C5FFFF    MOV EAX,DWORD PTR SS:[EBP-3A50]
 00394E29   8B00             MOV EAX,DWORD PTR DS:[EAX]
 00394E2B   8985 A8C5FFFF    MOV DWORD PTR SS:[EBP-3A58],EAX
 00394E31   8B85 B0C5FFFF    MOV EAX,DWORD PTR SS:[EBP-3A50]
 00394E37   83C0 04          ADD EAX,4
 00394E3A   8985 B0C5FFFF    MOV DWORD PTR SS:[EBP-3A50],EAX
 00394E40   8B85 B0C5FFFF    MOV EAX,DWORD PTR SS:[EBP-3A50]
 00394E46   8B00             MOV EAX,DWORD PTR DS:[EAX]
 00394E48   8985 ACC5FFFF    MOV DWORD PTR SS:[EBP-3A54],EAX
 00394E4E   8B85 B0C5FFFF    MOV EAX,DWORD PTR SS:[EBP-3A50]
 00394E54   83C0 04          ADD EAX,4


А в это время, неподалеку...

00394E48   8985 ACC5FFFF    MOV DWORD PTR SS:[EBP-3A54],EAX


Вот здесь запись. Смотрим, как эти байты попадают в EAX.

00394E40   8B85 B0C5FFFF    MOV EAX,DWORD PTR SS:[EBP-3A50]
 00394E46   8B00             MOV EAX,DWORD PTR DS:[EAX]


Значит из DWORD PTR SS:[EBP-3A50] получают адрес, где хранятся байты. Смотрим туда...

0094A8F8  0E 10 D0 1E | EE EF 59 E4      _Р_опYд
 0094A900  21 00 2A 03 | ED 0F A6 1B      !.*_н_¦_
 0094A908  25 10 D0 1E | FC EF 59 E4      %_Р_ьпYд
 0094A910  3B 00 2A 03 | EA 0F A6 1B      ;.*_к_¦_


Это таблица, содержащая адреса и байты: *** - куда писать (адрес в программе), *** - какие байты (прыжок в антидамп), в следующей строчке - то же самое, только разница в том, что это адрес в антидампе, а байты - байты возврата в программу. Далее все повторяется... Обратите внимание - первый адрес совсем не тот, где мы запоролись при запуске исправленного антидампа. Когда остановитесь на OEP прокрутите код на самый верх - 1ED0100D E9 EEEF92E2 JMP 01630000 - вот самый первый антидамп. Теперь посмотрите на первую строчку в таблице - адрес 1ED0100E. На конце E а не D потому, что не учитывается байт прыжка (E9). А сами байты весьма похожи - EEEF****. Значит здесь (94A8F8) начало таблицы адресов и байтов. Его можно найти, просто посмотрев выше - там совсем другие байты, не похожие на искомые.

Ставим бряк на запись на 0094A8F8. Следите за содержимым. Прервемся здесь:

0039416B   0385 28C6FFFF    ADD EAX,DWORD PTR SS:[EBP-39D8]
 00394171   8B8D 50C6FFFF    MOV ECX,DWORD PTR SS:[EBP-39B0]
 00394177   8901             MOV DWORD PTR DS:[ECX],EAX
 00394179   8B85 50C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B0]
 0039417F   83C0 04          ADD EAX,4
 00394182   8985 50C6FFFF    MOV DWORD PTR SS:[EBP-39B0],EAX


Опять цикл, причем весьма большой. Давайте его рассматривать подробно.

В этом месте рассчитываются байты для прыжка в антидамп и для возврата из него. Если кто не в курсе - байты прыжка показывают не адрес, куда надо прыгать, а на сколько относительно текущего положения надо передвинуться. Итак, алгоритм достаточно прост - из адреса "куда" вычитаем адрес "откуда".

003940EB   8B85 48C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B8]
 003940F1   8B00             MOV EAX,DWORD PTR DS:[EAX]         ; где поправить адреса для прыга в антидамп
 003940F3   8985 28C6FFFF    MOV DWORD PTR SS:[EBP-39D8],EAX
 003940F9   8B85 48C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B8]
 003940FF   83C0 04          ADD EAX,4
 00394102   8985 48C6FFFF    MOV DWORD PTR SS:[EBP-39B8],EAX
 00394108   8B85 48C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B8]
 0039410E   8B00             MOV EAX,DWORD PTR DS:[EAX]         ; смещение начала антидампа
 00394110   8985 24C6FFFF    MOV DWORD PTR SS:[EBP-39DC],EAX
 00394116   8B85 48C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B8]
 0039411C   83C0 04          ADD EAX,4
 0039411F   8985 48C6FFFF    MOV DWORD PTR SS:[EBP-39B8],EAX
 00394125   8B85 48C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B8]
 0039412B   8B00             MOV EAX,DWORD PTR DS:[EAX]         ; смещение конца антидампа (тут править байты для возврата)
 0039412D   8985 2CC6FFFF    MOV DWORD PTR SS:[EBP-39D4],EAX
 00394133   8B85 48C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B8]
 00394139   83C0 04          ADD EAX,4
 0039413C   8985 48C6FFFF    MOV DWORD PTR SS:[EBP-39B8],EAX
 00394142   A1 80E83A00      MOV EAX,DWORD PTR DS:[3AE880]      ; начало выделенной памяти под антидампы
 00394147   0385 24C6FFFF    ADD EAX,DWORD PTR SS:[EBP-39DC]    ; Начало антидампа с учетом адреса загрузки
 0039414D   8B8D 28C6FFFF    MOV ECX,DWORD PTR SS:[EBP-39D8]
 00394153   8B95 0CC7FFFF    MOV EDX,DWORD PTR SS:[EBP-38F4]
 00394159   8D4C0A 04        LEA ECX,DWORD PTR DS:[EDX+ECX+4]   ; Считаем адрес возврата
 0039415D   2BC1             SUB EAX,ECX                        ; Считаем байты прыга в антидамп
 0039415F   8985 30C6FFFF    MOV DWORD PTR SS:[EBP-39D0],EAX
 00394165   8B85 0CC7FFFF    MOV EAX,DWORD PTR SS:[EBP-38F4]
 0039416B   0385 28C6FFFF    ADD EAX,DWORD PTR SS:[EBP-39D8]    ; Куда писать байты прыга в антидамп
 00394171   8B8D 50C6FFFF    MOV ECX,DWORD PTR SS:[EBP-39B0]
 00394177   8901             MOV DWORD PTR DS:[ECX],EAX         ; Пишем в таблицу
 00394179   8B85 50C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B0]
 0039417F   83C0 04          ADD EAX,4
 00394182   8985 50C6FFFF    MOV DWORD PTR SS:[EBP-39B0],EAX
 00394188   8B85 50C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B0]
 0039418E   8B8D 30C6FFFF    MOV ECX,DWORD PTR SS:[EBP-39D0]    ; Байты для прыга в антидамп
 00394194   8908             MOV DWORD PTR DS:[EAX],ECX         ; Пишем в таблицу
 00394196   8B85 50C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B0]
 0039419C   83C0 04          ADD EAX,4
 0039419F   8985 50C6FFFF    MOV DWORD PTR SS:[EBP-39B0],EAX
 003941A5   8B85 28C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39D8]
 003941AB   8B8D 0CC7FFFF    MOV ECX,DWORD PTR SS:[EBP-38F4]
 003941B1   8D4401 04        LEA EAX,DWORD PTR DS:[ECX+EAX+4]   ; Куда вернуться
 003941B5   8B8D 2CC6FFFF    MOV ECX,DWORD PTR SS:[EBP-39D4]    ; Куда писать байты возврата
 003941BB   8B15 80E83A00    MOV EDX,DWORD PTR DS:[3AE880]
 003941C1   8D4C0A 04        LEA ECX,DWORD PTR DS:[EDX+ECX+4]
 003941C5   2BC1             SUB EAX,ECX                        ; Считаем байты возврата
 003941C7   8985 30C6FFFF    MOV DWORD PTR SS:[EBP-39D0],EAX
 003941CD   A1 80E83A00      MOV EAX,DWORD PTR DS:[3AE880]
 003941D2   0385 2CC6FFFF    ADD EAX,DWORD PTR SS:[EBP-39D4]    ; Куда их пропишем
 003941D8   8B8D 50C6FFFF    MOV ECX,DWORD PTR SS:[EBP-39B0]
 003941DE   8901             MOV DWORD PTR DS:[ECX],EAX         ; Сохраним адрес для записи
 003941E0   8B85 50C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B0]
 003941E6   83C0 04          ADD EAX,4
 003941E9   8985 50C6FFFF    MOV DWORD PTR SS:[EBP-39B0],EAX
 003941EF   8B85 50C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B0]
 003941F5   8B8D 30C6FFFF    MOV ECX,DWORD PTR SS:[EBP-39D0]
 003941FB   8908             MOV DWORD PTR DS:[EAX],ECX         ; И сами байты возврата
 003941FD   8B85 50C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39B0]
 00394203   83C0 04          ADD EAX,4
 00394206   8985 50C6FFFF    MOV DWORD PTR SS:[EBP-39B0],EAX
 0039420C  ^E9 C8FEFFFF      JMP 003940D9


Вот и весь алгоритм (прочитав понять не легко, но если потрейсите в оле, все поймете и разберетесь довольно быстро - тут сплошная математика). Отсюда становится понятным, как нам перенаправить антидамп туда, куда надо нам. Если не понятно - просто изменив значение по адресу DWORD PTR DS:[3AE880] (от него зависит, куда направится прыжок из программы в антидамп и прыжок возврата в антидампа в программу, а больше нам ничего и не требуется) Но для начала посмотрим, сколько места нам надо... И вот тут нас ждет полная засада, т.к. FFBF свободных байтов у нас нет :( Самое главное - нам надо писать в такую область памяти, которая не будет использоваться программой после распаковки. Единственная подобная область - секции армадилы. В .text1 писать нельзя - там код армадилы, да и доступа на запись в эту секцию нет (хотя это и поправимо). .adata в принципе подходит, но я решил туда не писать. .data1 - импорт арме нужен долго, полетим 100%. .reloc1 - может потом пригодится, да и места там в притирочку. Будем писать в .pdata. По размеру похоже, что здесь находится запакованный код программы. Ну да попробуем, т.к. 64 килобайта кода наверно уже будут распакованы. В общем ставим бряк на запись DWORD PTR DS:[3AE880]. Прервемся здесь:

00393DD7   A3 80E83A00      MOV DWORD PTR DS:[3AE880],EAX
 00393DDC   EB 10            JMP SHORT 00393DEE
 00393DDE   83BD 68C6FFFF 00 CMP DWORD PTR SS:[EBP-3998],0
 00393DE5   75 07            JNZ SHORT 00393DEE
 00393DE7   8325 80E83A00 00 AND DWORD PTR DS:[3AE880],0
 00393DEE   6A 40            PUSH 40
 00393DF0   68 00200000      PUSH 2000
 00393DF5   FFB5 64C6FFFF    PUSH DWORD PTR SS:[EBP-399C]
 00393DFB   FF35 80E83A00    PUSH DWORD PTR DS:[3AE880]
 00393E01   FF15 A4F13900    CALL NEAR DWORD PTR DS:[39F1A4]   ; kernel32.VirtualAlloc
 00393E07   8985 6CC6FFFF    MOV DWORD PTR SS:[EBP-3994],EAX
 00393E0D   83BD 6CC6FFFF 00 CMP DWORD PTR SS:[EBP-3994],0
 00393E14   74 33            JE SHORT 00393E49
 00393E16   6A 40            PUSH 40
 00393E18   68 00100000      PUSH 1000
 00393E1D   FFB5 64C6FFFF    PUSH DWORD PTR SS:[EBP-399C]
 00393E23   FF35 80E83A00    PUSH DWORD PTR DS:[3AE880]
 00393E29   FF15 A4F13900    CALL NEAR DWORD PTR DS:[39F1A4]   ; kernel32.VirtualAlloc
 00393E2F   8985 6CC6FFFF    MOV DWORD PTR SS:[EBP-3994],EAX
 00393E35   83BD 6CC6FFFF 00 CMP DWORD PTR SS:[EBP-3994],0
 00393E3C   74 0B            JE SHORT 00393E49
 00393E3E   8B85 6CC6FFFF    MOV EAX,DWORD PTR SS:[EBP-3994]
 00393E44   A3 80E83A00      MOV DWORD PTR DS:[3AE880],EAX


Здесь выделяется память под антидамп. Главная строчка последняя. Останавливайтесь на ней и в ЕАХ пишите 1EFE7000 (чтобы не париться и не переписывать эти байты задом наперед в окне дампа данных). Теперь ставим бряк на доступ на секцию .text и запускаем программу, пропуская все бряки... До OEP вроде дошли, крутим окно кода в самый верх и смотрим на первый антидамп:

1ED01007   64:A1 00000000   MOV EAX,DWORD PTR FS:[0]
 1ED0100D  -E9 EE5F2E00      JMP VA_X.1EFE7000
 1ED01012   93               XCHG EAX,EBX


Теперь он ведет внутрь библиотеки. Уже хорошо. Теперь идем и смотрим, что нас ждет в антидампе:

1EFE7000   50               PUSH EAX
 1EFE7001   44               INC ESP
 1EFE7002   41               INC ECX
 1EFE7003   54               PUSH ESP
 1EFE7004   41               INC ECX
 1EFE7005   3030             XOR BYTE PTR DS:[EAX],DH
 1EFE7007   3000             XOR BYTE PTR DS:[EAX],AL
 1EFE7009   0000             ADD BYTE PTR DS:[EAX],AL
 1EFE700B   F0:0300          LOCK ADD EAX,DWORD PTR DS:[EAX]    ; LOCK prefix is not allowed
 1EFE700E   6217             BOUND EDX,QWORD PTR DS:[EDI]
 1EFE7010   0200             ADD AL,BYTE PTR DS:[EAX]
 1EFE7012  -78 DA            JS SHORT VA_X.1EFE6FEE
 1EFE7014   EC               IN AL,DX                           ; I/O command
 1EFE7015   BD 7F7C93D5      MOV EBP,D5937C7F
 1EFE701A   F5               CMC
 1EFE701B   38FE             CMP DH,BH
 1EFE701D   E4 47            IN AL,47                           ; I/O command
 1EFE701F   DB40 ED          FILD DWORD PTR DS:[EAX-13]
 1EFE7022   9F               LAHF
 1EFE7023   D1FF             SAR EDI,1
 1EFE7025   8A45 AB          MOV AL,BYTE PTR SS:[EBP-55]
 1EFE7028   54               PUSH ESP
 1EFE7029   45               INC EBP
 1EFE702A   AB               STOS DWORD PTR ES:[EDI]
 1EFE702B   AE               SCAS BYTE PTR ES:[EDI]


Полный отстой. Это совсем не то, что мы ожидали здесь увидеть. Антидампа здесь нет :( Думать о причинах не будем. Надо что-то делать. Думаем, что делать... Для начала сдампим то, что сделали... Теперь думаем... Надо где-то достать антидамп. В принципе это не сложно, только прыги у нас будут ну совсем не те... Короче задумка такая - прописать антидамп где-нибудь, где он не затрется, а байты возврата получить из расчета, что антидамп будет начинаться с адреса 1EFE7000. Сам антидамп не зависит от того, где он находится. Прыги возврата наоборот напрямую зависят от этого. Теперь подробнее:
Надо сделать, чтобы антидамп писался по такому адресу, по которому он запишется и не испортится - значит будем писать его туда, куда нам предложит арма.
Следующий вопрос: "байты возврата получить из расчета, что антидамп будет начинаться с адреса 1EFE7000" - Сейчас нам важны только байты возврата, т.к. байты джампа в антидамп мы вроде как получили. Итак, нас интересует лишь этот кусок генератора (бинарно его копируем):

0039419F   8985 50C6FFFF    MOV DWORD PTR SS:[EBP-39B0],EAX
 003941A5   8B85 28C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39D8]
 003941AB   8B8D 0CC7FFFF    MOV ECX,DWORD PTR SS:[EBP-38F4]
 003941B1   8D4401 04        LEA EAX,DWORD PTR DS:[ECX+EAX+4]   ; Куда вернуться
 003941B5   8B8D 2CC6FFFF    MOV ECX,DWORD PTR SS:[EBP-39D4]    ; Куда писать байты возврата
 003941BB   8B15 80E83A00    MOV EDX,DWORD PTR DS:[3AE880]
 003941C1   8D4C0A 04        LEA ECX,DWORD PTR DS:[EDX+ECX+4]
 003941C5   2BC1             SUB EAX,ECX                        ; Считаем байты возврата


Получаются они вычитанием из адреса, куда вернемся, адреса, откуда вернемся. "Куда вернемся" у нас постоянен (в том смысле, что ImageBase один, поэтому сдвиги относительно него всегда ведут туда же, куда и в прошлый раз), а вот "откуда" - уже по разному, т.к. винда выделяет разные участки памяти, а нам нужно, чтобы это "откуда" (DWORD PTR DS:[3AE880]) было 1EFE7000, причем не постоянно, т.к. куда эти байты возврата запишутся тоже рассчитывается из этого числа, и следовательно, они опять попадут в ту секцию и затрутся. Внимание! Вывод - надо подправить процедуру генерации так, чтобы перед генерацией байтов возврата в DWORD PTR DS:[3AE880] был 1EFE7000, а как только байты получены, DWORD PTR DS:[3AE880] = тому, что и предложила арма. А потом мы скопируем полученный антидамп и вставим начиная с адреса 1EFE7000. В общем все просто. Меняем

0039419F    MOV DWORD PTR SS:[EBP-39B0],EAX


на

0039419F    JMP ********


где ******** - подходящий адрес...
А там делаем так (бинарно вставляем генератор и дописываем необходимые инструкции):

003B0000   C705 80E83A00 00>MOV DWORD PTR DS:[3AE880],1EFE7000  ; ASCII "PDATA000"
 003B000A   90               NOP
 003B000B   90               NOP
 003B000C   90               NOP
 003B000D   90               NOP
 003B000E   90               NOP
 003B000F   90               NOP
 003B0010   90               NOP
 003B0011   90               NOP
 003B0012   90               NOP
 003B0013   90               NOP
 003B0014   8985 50C6FFFF    MOV DWORD PTR SS:[EBP-39B0],EAX
 003B001A   8B85 28C6FFFF    MOV EAX,DWORD PTR SS:[EBP-39D8]
 003B0020   8B8D 0CC7FFFF    MOV ECX,DWORD PTR SS:[EBP-38F4]
 003B0026   8D4401 04        LEA EAX,DWORD PTR DS:[ECX+EAX+4]
 003B002A   8B8D 2CC6FFFF    MOV ECX,DWORD PTR SS:[EBP-39D4]
 003B0030   8B15 80E83A00    MOV EDX,DWORD PTR DS:[3AE880]
 003B0036   8D4C0A 04        LEA ECX,DWORD PTR DS:[EDX+ECX+4]
 003B003A   2BC1             SUB EAX,ECX
 003B003C   90               NOP
 003B003D   90               NOP
 003B003E   90               NOP
 003B003F   90               NOP
 003B0040   90               NOP
 003B0041   90               NOP
 003B0042   C705 80E83A00 00>MOV DWORD PTR DS:[3AE880],1C20000
 003B004C   90               NOP
 003B004D   90               NOP
 003B004E  ^E9 1FECFEFF      JMP 0039EC72
 003B0053   90               NOP


(Не забудьте скопировать бинарно этот патч и сохранить, он нам еще пригодится)

Джамп меняете на свой и 1C20000 меняете на то значение, которое было у вас. Если вы его не записали - перезапускайте и повторяйте шаги снова...

Жмем F9 и смотрим на результат... Нда... Опять заглохла... Попробуем стереть патч и вернуть все как было, остановившись сразу после завершения цикла. Опять ошибка:( Можно провести эксперимент - заменить в конце участка памяти пару байтов с 00 00 на 90 90, а потом вернуть их обратно, и итог будет тот же. Значит тут нас пасут, и значит тут нас ждет, верно, облом:). Но не будем отчаиваться....

Еще немного пошевеля мозгами мы вспоминаем, что это еще не запись байтов в антидамп, а лишь составление таблицы. Потом арма пишет по адресу из таблицы байты все из той же таблицы... Уловили мысль? Если нет - программа слетает после любых изменений в ее коде. А нам позарез нужна не стандартная таблица, а так сказать "тюнингованная" под наши нужды. Если мы ее делаем арма падает. Но перед слетом арма любезно составляет нашу таблицу. Значит таблица-то у нас есть. Ну а раз есть, что нам мешает заменить ей таблицу в другой запущенной копии этой библиотеки, чтобы та сделала из нее нормальный антидамп. Тут никакие проверки целостности кода не помогут, т.к. пропишем мы ее туда, где она и должна быть. Внимание! Значит теперь делаем так - запускаем две копии, доходим во второй до выделения памяти под антидамп и записываем адрес начала участка памяти. В первой копии доходим до самого-самого начала цикла составления таблицы и делаем патч. Самое главное - не забудьте заменить значение по адресу DWORD PTR DS:[3AE880] тем, которое используется во второй копии (Для тех, кто в танке - этот (первый) экземпляр библиотеки после всех издевательств работать не будет, он нужен нам только для составления таблицы. Таблица рассчитана на другой (второй) экземпляр библиотеки. Байты возврата из антидампа пишутся по адресу, указанному в этой таблице, сам адрес получается путем несложных вычислений из адреса, с которого расположен в памяти антидамп. Причем при каждом запуске этот адрес меняется. Если мы его не изменим перед расчетом, в таблицу внесутся адреса, рассчитанные на первый экземпляр библиотеки, потом байты будут записаны по этим адресам, а они наверняка не совпадают с адресами участка памяти под антидамп во втором экземпляре, поэтому последует неожиданное исключение из-за записи в несуществующую память и арма опять нас кинет, ну а если память существует, все равно антидамп получится не рабочий, то есть без возвратов в библиотеку (т.к. куда надо байты не записались) Если что-то непонятно дочитайте до конца этот тутор и перечитайте его сначала, может и дойдет :) ). И в патче тоже не забудьте этот адрес, заодно и джампы проверьте, все ли туда, куда надо прыгают, а то мало ли куда вернемся... Теперь ставим бряк за концом цикла и F9. Новая таблица готова. Выделяем ее и бинарно копируем (если не знаете, что выделять: смотрим здесь (самое начало цикла)

003940C7   8985 00C7FFFF    MOV DWORD PTR SS:[EBP-3900],EAX


В EAX начало таблицы. Конец можно увидеть по количеству байтов 00 и F0 и отсутствию привычных 1E и ED). Отлично! Половину дела сделали.

Переходим ко второй копии - встаем после завершения цикла заполнения таблицы(перед GetTickCount)(сейчас таблица во второй копии сформированна, поэтому ее можно без боязни заменять), и бинарно вставляем ту таблицу, которую мы получили в первой копии, теперь исправляем флаг нуля у GetTickCount( этот GetTickCount не с проста - так они пытаются проследить, трейсим мы программу или нет. Там идет сравнение результата с BB8h (что в переводе на более привычные числа означает 3000, или 3 секунды)), и если больше, то нам будет плохо, запускаемся и... Опять падаем :((( Но тут вряд ли проверки на изменение кода виноваты, ведь мы изменили то, что изменять можно. Значит наверняка это связано с GetTickCount. Все искать глупо. Тогда поступим следующим образом - опять запускаем 2 копии, у второй на первом исключении ищем вызов GetTickCount и смотрим в дизассемблере, что там делается...

77E7A29B > BA 0000FE7F      MOV EDX,7FFE0000
 77E7A2A0   8B02             MOV EAX,DWORD PTR DS:[EDX]
 77E7A2A2   F762 04          MUL DWORD PTR DS:[EDX+4]
 77E7A2A5   0FACD0 18        SHRD EAX,EDX,18
 77E7A2A9   C3               RETN


Было так... А сделаем вот так...

77E7A29B > BA 0000FE7F      MOV EDX,7FFE0000
 77E7A2A0   8B02             MOV EAX,DWORD PTR DS:[EDX]
 77E7A2A2   B8 FFFFFFFF      MOV EAX,-1
 77E7A2A7   90               NOP
 77E7A2A8   90               NOP
 77E7A2A9   C3               RETN


Теперь нам не будут докучать проверки на трассирование кода :)

Теперь повторим описанное выше...

После вставления таблицы во вторую копию ставим бряк на доступ на секцию .text и пропускаем все исключения и с замеранием сердца ждем... Прерываемся на OEP, что оЧень радует. Крутим в самый верх и видим, что антидамп направлен куда-то, но не в наш файл - это понятно. Заходим туда, антидамп в норме, но вот адреса возврата... они идут в никуда - что тоже понятно и уже обнадеживает (кто не понял - перечитываем ту часть, где рассказывается о получениях байтов возврата и о получении "тюнингованной" таблицы). Теперь бинарно скопируем весь код антидампа, откроем тот дамп, в котором прыги антидампа перенаправлены внутрь библиотеки в секцию .pdata(надеюсь, Вы его еще не удалили;), выделим начиная с адреса 1EFE7000 FFFF байт и бинарно вставим. И... о чудо, мы видим следующее:

01E1000E   71 00                 JNO SHORT 01E10010
 01E10010   75 02                 JNZ SHORT 01E10014
 01E10012   75 41                 JNZ SHORT 01E10055
 01E10014   7A 00                 JPE SHORT 01E10016
 01E10016   66:87F3               XCHG BX,SI
 01E10019   8BF1                  MOV ESI,ECX
 01E1001B   57                    PUSH EDI
 01E1001C   897424 0C             MOV DWORD PTR SS:[ESP+C],ESI
 01E10020  -E9 ED0FEF1C           JMP VA_X.1ED01012
 01E10025   8D7E 48               LEA EDI,DWORD PTR DS:[ESI+48]
 01E10028   C74424 18 00000000    MOV DWORD PTR SS:[ESP+18],0
 01E10030   51                    PUSH ECX
 01E10031   53                    PUSH EBX
 01E10032   73 00                 JNB SHORT 01E10034
 01E10034   5B                    POP EBX
 01E10035   73 00                 JNB SHORT 01E10037
 01E10037   59                    POP ECX
 01E10038   8BCF                  MOV ECX,EDI
 01E1003A  -E9 EA0FEF1C           JMP VA_X.1ED01029
 01E1003F   8B5C24 20             MOV EBX,DWORD PTR SS:[ESP+20]
 01E10043   C64424 18 01          MOV BYTE PTR SS:[ESP+18],1
 01E10048  -E9 EE0FEF1C           JMP VA_X.1ED0103B
 01E1004D   66:87CB               XCHG BX,CX
 01E10050   57                    PUSH EDI
 01E10051   66:87F3               XCHG BX,SI
 01E10054   0FCF                  BSWAP EDI
 01E10056   0FCB                  BSWAP EBX
 01E10058   79 02                 JNS SHORT 01E1005C
 01E1005A   79 03                 JNS SHORT 01E1005F
 01E1005C   0FCB                  BSWAP EBX
 01E1005E   0FCF                  BSWAP EDI
 01E10060   66:87F3               XCHG BX,SI
 01E10063   5F                    POP EDI
 01E10064   66:87CB               XCHG BX,CX
 01E10067   C64424 18 02          MOV BYTE PTR SS:[ESP+18],2
 01E1006C  -E9 0110EF1C           JMP VA_X.1ED01072


Антидамп цел, а прыги возврата идут именно туда, куда надо. Теперь “Copy to executable file”, сохраняем этот файл как VA_X.DLL. Восстанавливаем импорт, запустив дамп с исправленным IAT и натравив на него ИмпРек. Не забудьте поправить свойства секций. Заменяем старый VA_X.DLL распакованным, запускаем студию и... Начинаем ломать... ;) Надеюсь это вы сделаете сами. Если нет - пишите, может помогу :)

</Antidump Fixed>


Программа распакована...

Юппииииии, вот теперь действительно все. Всем спасибо за внимание.

P.S.:
Если Вы дошли до этого места и библиотека и у Вас распакована - значит я не зря карпел над этой статьей. Если у Вас что-то не получилось - не спешите слать мне гневные письма - перечитайте и переделайте все с нуля. Читая эту статью не стремитесь слепо следовать всем шагам, лучше перечитайте ее несколько раз, параллельно рассматривая работу АРМы в OllyDebug, главное - поймите, какие основные методы были применены для распаковки и как мы к ним пришли, старайтесь понять ход мыслей автора. Лишь после этого распаковывайте сами, изредка глядя в этот тутор за разъяснением непонятных мест, хотя объем его не столь велик, чтобы осветить хотя бы сотую часть работу АРМы. Тогда Вы действительно научитесь распаковывать армадиллу (правда только с этими настройками) на любой программе.

P.P.S: Большое спасибо Sne за подсунутую программу, моральную поддержку :) и красивое офрмление статьи, увы, не попавшее сюда :(

Автор: Ra$cal
mailto: Rascalspb[dog]mail[dot]ru
Дата: 09.02.2005




Обсуждение статьи: Распаковка библиотеки, запакованной Armadillo >>>


Комментарии к статье: Распаковка библиотеки, запакованной Armadillo

Alex_ZZ 04.08.2007 18:18:24
В статье ошибка с самого начала: "Чтобы попасть на OEP достаточно поставить мемори бряк на доступ на секцию .text". Прерываемся на ОЕП самой АРМЫ, соответственно ImpRec тутже определяет все функции которые эта арма и юзает!
---

Материалы находятся на сайте https://exelab.ru



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


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