Форум АНТИЧАТ

Форум АНТИЧАТ (https://forum.antichat.xyz/index.php)
-   Авторские статьи (https://forum.antichat.xyz/forumdisplay.php?f=31)
-   -   Распаковка RLPack 1.xx (https://forum.antichat.xyz/showthread.php?t=34869)

taha 07.03.2007 13:46

Распаковка RLPack 1.xx
 

[iNtr0]

Юность в сапогах отменяется, по крайней мере на семестр, поэтому решил посмотреть, что же там, в разделе CrackMe, происходит. Первый в списке крякми Zlo'го. По словам автора, он использует один малоизвестный, но очень хороший прот. Стало интересно, что за протектор такой, что распаковать не могут. Вобщем, сегодня я поведаю о результатах исследования.

[t00ls]

OllyDbg (XP / Shadow кому какая больше нравится) // отладчик
// Статья для новичков, поэтому я взял минимальный набор плагинов
// только самое необходимое. Впоследствии мы будем дополнять нашу подборку.
// Эти плаги, и последующие, можно достать на tuts4you.com
[+] OllyDump
[+] OllyScript
[+] CommandBar
// И ещё вот эти тулзы
IDA // лучший дизассемблер
ImpREC // тулза для восстановления импорта
LordPE // PE Editor

[BeGin]

Итак, начнём. Грузим запакованную прогу в Olly. Будем пропускать все исключения.

http://img240.imageshack.us/img240/363/img01gu4.gif

Стек забивается регистрами, потом выравнивается. Далее следует pusha, интересно (тут я предполагаю, что вы читали статьи по распаковке и знаете , почему эта команда нас интересует ), но пока нас это не касается. Сперва попробуем запустить программу по F9.

[anti-debugg]

Первую глупость, которую сделал этот протектор, он выдал MessageBox о том, что его отлаживают. Не теряем времени! Пока висит MessageBox, мы переключаемся на OllyDbg и жмём Ctrl-G (что означает переход либо по адресу, либо по метке). В открывшемся окне пишем MessageBoxA и жмем enter. Мы попали в тело функции, ставим бряк на "POP EBP".

Код:

77D7052A  E8 2D000000      CALL user32.MessageBoxExA
77D7052F  5D              POP EBP

Жмём в MessageBox OK и брякаемся.

http://img83.imageshack.us/img83/4049/img2xs2.gif

Снимаем бряк и выходим из функции (пару раз F8).

И вот функция проверки на наличие отладчика

http://img221.imageshack.us/img221/8083/img03qz4.gif

По ходу, функция проверяет две переменные на равенство единице. Если хотябы одна равна единице, то выдаётся MessageBox. Нужно поставить на них брикпоинты. Для этого в CmdBar пишем следующие строки: "hr EBP+1EB2", "hr EBP+1EB6". Перезапускаем прогу Ctrl-F2, F9.
Код:

004FBEC5  |. FF95 A21E0000  CALL DWORD PTR SS:[EBP+1EA2]
004FBECB  |. 0BC0          OR EAX,EAX
004FBECD  |. 74 06          JE SHORT CrackMe2.004FBED5
004FBECF  |. 8985 B21E0000  MOV DWORD PTR SS:[EBP+1EB2],EAX

Как можно заметить, после выполнения некой функции, результат проверяется на равенство нулю. И если не ноль, то результат заносится в одну из наших переменных. Поставим hardware, on execution брикпоинт на эту функцию. Перезапускаемся (Ctrl-F2), F9.

Код:

004FBEC5  |. FF95 A21E0000  CALL DWORD PTR SS:[EBP+1EA2]    ;kernel32.IsDebuggerPresent
Знакомая функция, функция проверяет, присутсвует ли отладчик. В чём проблема? - Спросит читатель - Патчить функцию! А нет, всё дело в устройстве IsDebuggerPresent. Дело в том, что она опирается на одну интересную структуру PEB (блок окружения процесса), в которой есть поле BeingDebugged. В котором хранится 1 если процесс отлаживают, 0 если процесс не отлаживают. Этим и занимается IsDebuggerPresent, тоесть проверяет это поле.

Вот она IsDebuggerPresent:
Код:

MOV EAX,DWORD PTR FS:[18]          ; NT_TIB.Self
MOV EAX,DWORD PTR DS:[EAX+30] ; TEB.Peb
MOVZX EAX,BYTE PTR DS:[EAX+2] ; Peb.BeingDebugged
RETN

Соответсвенно можно обратится к этому полю, минуя вызов IsDebuggerPresent. Думаю вы поняли, что патчить нужно не функцию, а эту переменную (BeingDebugged) в PEB структуре, чем и занимаются некоторые плагины к OllyDbg. Но не будем спешить, посмотрим что ещё нам уготовил протектор. Реверсим дальше, идём по F8.

Код:

004FBEDE  |. 83BD A61E0000 >CMP DWORD PTR SS:[EBP+1EA6],0
004FBEE5  |. 74 27          JE SHORT CrackMe2.004FBF0E
004FBEE7  |. 8D85 B61E0000  LEA EAX,DWORD PTR SS:[EBP+1EB6]
004FBEED  |. 50            PUSH EAX
004FBEEE  |. 6A FF          PUSH -1
004FBEF0  |. FF95 A61E0000  CALL DWORD PTR SS:[EBP+1EA6]            ;  kernel32.CheckRemoteDebuggerPresent

Так, так... CheckRemoteDebuggerPresent, ещё одна функция проверки на наличие отладчика. Эта функция возвращает значение в некоторый буфер, у нас это EBP+1EB6. Заглянув в эту API вы поймёте, что это обёртка вокруг ZwQueryInformationProcess. ZwQueryInformationProcess проверяет отладочный порт, и если он "открыт", то нас отлаживают. В принципе можно патчить, но пока не будем торопиться =).

И как оказывается не зря. Следующий код проверяет целостность функции, а точнее пропатченность.

Код:

004FBEF6  |. 8B85 A61E0000  MOV EAX,DWORD PTR SS:[EBP+1EA6]          ;  kernel32.CheckRemoteDebuggerPresent
004FBEFC  |. 8138 8B442408  CMP DWORD PTR DS:[EAX],824448B
004FBF02  |. 75 0A          JNZ SHORT CrackMe2.004FBF0E
004FBF04  |. C785 B61E0000 >MOV DWORD PTR SS:[EBP+1EB6],1

Если функция пропатчена, то в EBP+1EB6 1. От куда они взяли 824448B? Всё просто! Небезызвестный плагин Olly Advanced, так патчит эту функцию. Плэтому мы не будем использовать подобные плагины. Как вы поняли нам нужно скрыться от ZwQueryInformationProcess => и от CheckRemoteDebuggerPresent.

Идём дальше...

Код:

004FBF0E  |> 64:A1 30000000 MOV EAX,DWORD PTR FS:[30]
004FBF14  |. 83C0 68        ADD EAX,68
004FBF17  |. 8B00          MOV EAX,DWORD PTR DS:[EAX]
004FBF19  |. 83F8 70        CMP EAX,70
004FBF1C  |. 75 0A          JNZ SHORT CrackMe2.004FBF28
004FBF1E  |. C785 B21E0000 >MOV DWORD PTR SS:[EBP+1EB2],1

Опять Peb! Что на этот раз? А на это раз вот что:

/*68*/ DWORD NtGlobalFlag

Проверяет на равенство 70, если 70, то в EBP+1EB2 единицу.

Идём дальше...
Опять peb =((((. Надоело =\.
Дальше...

Код:

004FBF44  |> BE 09000000    MOV ESI,9
004FBF49  |. 8DBD 4E1F0000  LEA EDI,DWORD PTR SS:[EBP+1F4E]
004FBF4F  |> 6A 00          /PUSH 0
004FBF51  |. 68 80000000    |PUSH 80
004FBF56  |. 6A 03          |PUSH 3
004FBF58  |. 6A 00          |PUSH 0
004FBF5A  |. 6A 01          |PUSH 1
004FBF5C  |. 68 00000080    |PUSH 80000000
004FBF61  |. 57            |PUSH EDI
004FBF62  |. FF95 AE1E0000  |CALL DWORD PTR SS:[EBP+1EAE]
004FBF68  |. 83F8 FF        |CMP EAX,-1
004FBF6B  |. 74 0A          |JE SHORT CrackMe2.004FBF77
004FBF6D  |. C785 B21E0000 >|MOV DWORD PTR SS:[EBP+1EB2],1
004FBF77  |> 47            |INC EDI
004FBF78  |. 803F 00        |CMP BYTE PTR DS:[EDI],0
004FBF7B  |.^75 FA          |JNZ SHORT CrackMe2.004FBF77
004FBF7D  |. 47            |INC EDI
004FBF7E  |. 4E            |DEC ESI
004FBF7F  |.^75 CE          \JNZ SHORT CrackMe2.004FBF4F

Защищаемся от SoftICE. И всё =))).

Делаем выводы... Нам понадобятся:

Phant0m
[+] hide from PEB (PEB BeingDebugged, PEB NtGlobalFlag)

Olly Advanced
[+] ZwQueryInformationProcess
[+] IsDebuggerPresent

Не ставьте лишнее (помните что происходит с CheckRemoteDebuggerPresent). Удаляем поставленные бряки. И запускаем по F9. Всё гуд +))). Прога запустилась.

[oep]

Фууууууухх, с антиотладкой разобрались. Итак, как мы заметили вначале, регистры сохраняются с помощью pusha. И будут храниться в стеке, пока прот не захочет их восстановить. А когда он захочет их восстановить? Когда будет переходить на OEP!

http://img112.imageshack.us/img112/3770/img4is8.gif

Ещё разок F8, и регистры запушены. Ставим бряк "hr esp". Пробуем - F9. Мы тут

Код:

004FA85B  . 61            POPAD
004FA85C  .-E9 5B49F7FF    JMP CrackMe2.0046F1BC

Снимаем бряк и прыгаем. Delphi!
Код:

0046F1BC  55              PUSH EBP
0046F1BD  8BEC            MOV EBP,ESP
0046F1BF  83C4 F0          ADD ESP,-10
0046F1C2  B8 54EF4600      MOV EAX,CrackMe2.0046EF54
0046F1C7  E8 EC6AF9FF      CALL CrackMe2.00405CB8
0046F1CC  A1 B0584700      MOV EAX,DWORD PTR DS:[4758B0]
0046F1D1  8B00            MOV EAX,DWORD PTR DS:[EAX]
0046F1D3  E8 F033FEFF      CALL CrackMe2.004525C8
0046F1D8  8B0D D4594700    MOV ECX,DWORD PTR DS:[4759D4]            ; CrackMe2.00476F98
0046F1DE  A1 B0584700      MOV EAX,DWORD PTR DS:[4758B0]
0046F1E3  8B00            MOV EAX,DWORD PTR DS:[EAX]
0046F1E5  8B15 9CB44600    MOV EDX,DWORD PTR DS:[46B49C]            ; CrackMe2.0046B4E8
0046F1EB  E8 F033FEFF      CALL CrackMe2.004525E0
0046F1F0  A1 B0584700      MOV EAX,DWORD PTR DS:[4758B0]
0046F1F5  8B00            MOV EAX,DWORD PTR DS:[EAX]
0046F1F7  E8 6434FEFF      CALL CrackMe2.00452660
0046F1FC  E8 B34BF9FF      CALL CrackMe2.00403DB4
0046F201  8D40 00          LEA EAX,DWORD PTR DS:[EAX]
0046F204  0000            ADD BYTE PTR DS:[EAX],AL

Вот и OEP!

[IAT]

Проскролируем вверх. Что то API невидать =\. Ctrl-G > GetModuleHandleA. Ставим бряк на выход из функции.

http://img105.imageshack.us/img105/5713/img5ua8.gif

F8 и мы тут

Код:

00405CC2  6A 00            PUSH 0
00405CC4  E8 2BFFFFFF      CALL CrackMe2.00405BF4
00405CC9  A3 64664700      MOV DWORD PTR DS:[476664],EAX            ; CrackMe2.00400000

00405BF4, чтож пройдёмся по этому адресу! А вот и переходники

http://img228.imageshack.us/img228/1872/img6jn2.gif

Пройдёмся по этому переходу

Код:

003C0386  93              XCHG EAX,EBX
003C0387  68 E9BD3C47      PUSH 473CBDE9
003C038C  812C24 C1D62949  SUB DWORD PTR SS:[ESP],4929D6C1
003C0393  93              XCHG EAX,EBX
003C0394  813424 89519282  XOR DWORD PTR SS:[ESP],82925189
003C039B  C3              RETN

Здесь формируется адрес API. Получается, что протектор разлагает адрес API на некоторые части, которые вследствии собираются, и выполняется переход. Адрес полученной функции забивается в таблицу. Нам известен элемент 0048A20C.

Перезапускаемся и ставим бряк на 0048A20C (hr 0048A20C).

http://img257.imageshack.us/img257/746/img7az8.gif

Мдяя... Думаю пора пересесть на IDA Pro. Реверсим, кментим и в итоге
Код:

_cUB)9o]:004FA7BE                call    dword ptr [ebp+0AF5h] ; call kernel32.GetProcAddress
_cUB)9o]:004FA7C4                test    eax, eax        ; проверяем полученный адрес на валидность
_cUB)9o]:004FA7C6                jz      loc_4FB1BE
_cUB)9o]:004FA7CC                call    calc_to_new_proc_addr ;
_cUB)9o]:004FA7CC                                        ; функция строит кусок кода, который
_cUB)9o]:004FA7CC                                        ; будет считать смещение до API и
_cUB)9o]:004FA7CC                                        ; передовать управление на эту API
_cUB)9o]:004FA7D1                mov    dword ptr [ebp+1B09h], 0
_cUB)9o]:004FA7DB                mov    [edi], eax      ; заносим в таблицу импорта

Делаем вывод, что протектор сначала получает настоящий адрес API с помощью GetProcAddress. Затем с помощью функции, которую я обозвал calc_to_new_proc_addr, создаёт функцию переходник и заносит адрес этой функции в таблицу ипорта.

Ответ напрашивается сам собой. Если пропатчить функцию calc_to_new_proc_addr, тоесть вначале поставить ret, то в [edi] будет заноситься результат GetProcAddress, тоесть настоящий адрес.

Запомним адрес этой функции
Код:

004FBD9E    60            PUSHAD
004FBD9F  . 83BD 5A200000 >CMP DWORD PTR SS:[EBP+205A],0
004FBDA6  . 75 48          JNZ SHORT CrackMe2.004FBDF0
004FBDA8  . 60            PUSHAD
004FBDA9  . 8D9D 451B0000  LEA EBX,DWORD PTR SS:[EBP+1B45]
004FBDAF  . 53            PUSH EBX
004FBDB0  . FF95 050B0000  CALL DWORD PTR SS:[EBP+B05]

Удаляем бряк с 0048A20C. Савим бряк (hardware) на 004FA85B. Теперь путешествуем к 004FBD9E ( спомощью Ctrl-G ). Патчим начало, теперь оно

Код:

004FBD9E    C3            RETN
004FBD9F  . 83BD 5A200000 >CMP DWORD PTR SS:[EBP+205A],0
004FBDA6  . 75 48          JNZ SHORT CrackMe2.004FBDF0

Запускаем, F9! Переходим на oep, скроллируем вверх.

Код:

0040124A  8BC0            MOV EAX,EAX
0040124C  -FF25 A0A14800    JMP DWORD PTR DS:[48A1A0]                ; kernel32.GetCommandLineA
00401252  8BC0            MOV EAX,EAX
00401254  -FF25 9CA14800    JMP DWORD PTR DS:[48A19C]                ; kernel32.GetLocaleInfoA
0040125A  8BC0            MOV EAX,EAX
0040125C  -FF25 98A14800    JMP DWORD PTR DS:[48A198]                ; kernel32.GetModuleFileNameA
00401262  8BC0            MOV EAX,EAX
00401264  -FF25 94A14800    JMP DWORD PTR DS:[48A194]                ; kernel32.GetModuleHandleA
0040126A  8BC0            MOV EAX,EAX
0040126C  -FF25 90A14800    JMP DWORD PTR DS:[48A190]                ; kernel32.GetProcAddress
00401272  8BC0            MOV EAX,EAX
00401274  -FF25 8CA14800    JMP DWORD PTR DS:[48A18C]                ; kernel32.GetStartupInfoA
0040127A  8BC0            MOV EAX,EAX
0040127C  -FF25 88A14800    JMP DWORD PTR DS:[48A188]                ; kernel32.GetThreadLocale
00401282  8BC0            MOV EAX,EAX
00401284  -FF25 84A14800    JMP DWORD PTR DS:[48A184]                ; kernel32.LoadLibraryExA
0040128A  8BC0            MOV EAX,EAX
0040128C  -FF25 D0A14800    JMP DWORD PTR DS:[48A1D0]                ; user32.LoadStringA

Красота... Дампим, пока мы на oep.

http://img228.imageshack.us/img228/4125/img8nu8.gif

Никаких Rebuild'ов Import! Жмём Dump. Отлично! Всё сдампилось =)).

Теперь собственно восстановление. Запускаем ImpREC, открываем наш процесс, вводим oep, IAT AutoSearch, GetIports. Функции найденны, это радует.
Теперь фиксируем наш дамп. ImpREC говорит всё прошло отлично.
Момент истины! Запускаем! Оба ошибка инициализации данных 0xc000007b!
Тут что то не так с PE-заголовком. Нужно восстановить. Для этого нам понадобится LordPE. В нём есть одна очень полезная фитча - Rebuild PE. Как можно понять из названия она восстановит наш PE.

http://img214.imageshack.us/img214/7664/img9sy9.gif

Готово! Попробуем запустить теперь! Уряяяя!! Заработало.
Вот с этим у меня возникли главные проблемы, пока не вспомнил про эту фитчу.

[library]
www.cracklab.ru
www.wasm.ru
www.tuts4you.com
http://guru-exe.ripgames.org/

[heppy end]
Вроде всё! Что ещё сказать? До новых встреч! =)))

zl0y 18.03.2007 00:28

Хорошая стотья вот Ap0x и обасрался :D зы + как всегда ))


Время: 05:22