Авторские статьи Распаковка RLPack 1.xx

Discussion in 'Статьи' started by taha, 7 Mar 2007.

  1. taha

    taha Elder - Старейшина

    Joined:
    20 Aug 2006
    Messages:
    399
    Likes Received:
    330
    Reputations:
    251

    [iNtr0]

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

    [t00ls]

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

    [BeGin]

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

    [​IMG]

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

    [anti-debugg]

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

    Code:
    77D7052A   E8 2D000000      CALL user32.MessageBoxExA
    77D7052F   5D               POP EBP
    Жмём в MessageBox OK и брякаемся.

    [​IMG]

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

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

    [​IMG]

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

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

    Вот она IsDebuggerPresent:
    Code:
    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.

    Code:
    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 проверяет отладочный порт, и если он "открыт", то нас отлаживают. В принципе можно патчить, но пока не будем торопиться =).

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

    Code:
    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.

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

    Code:
    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 =((((. Надоело =\.
    Дальше...

    Code:
    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!

    [​IMG]

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

    Code:
    004FA85B   . 61             POPAD
    004FA85C   .-E9 5B49F7FF    JMP CrackMe2.0046F1BC
    Снимаем бряк и прыгаем. Delphi!
    Code:
    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. Ставим бряк на выход из функции.

    [​IMG]

    F8 и мы тут

    Code:
    00405CC2   6A 00            PUSH 0
    00405CC4   E8 2BFFFFFF      CALL CrackMe2.00405BF4
    00405CC9   A3 64664700      MOV DWORD PTR DS:[476664],EAX            ; CrackMe2.00400000
    00405BF4, чтож пройдёмся по этому адресу! А вот и переходники

    [​IMG]

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

    Code:
    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).

    [​IMG]

    Мдяя... Думаю пора пересесть на IDA Pro. Реверсим, кментим и в итоге
    Code:
    _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, тоесть настоящий адрес.

    Запомним адрес этой функции
    Code:
    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 ). Патчим начало, теперь оно

    Code:
    004FBD9E     C3             RETN
    004FBD9F   . 83BD 5A200000 >CMP DWORD PTR SS:[EBP+205A],0
    004FBDA6   . 75 48          JNZ SHORT CrackMe2.004FBDF0
    Запускаем, F9! Переходим на oep, скроллируем вверх.

    Code:
    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.

    [​IMG]

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

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

    [​IMG]

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

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

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

    zl0y Banned

    Joined:
    13 Sep 2006
    Messages:
    371
    Likes Received:
    270
    Reputations:
    109
    Хорошая стотья вот Ap0x и обасрался :D зы + как всегда ))
     
    1 person likes this.