[iNtr0] Юность в сапогах отменяется, по крайней мере на семестр, поэтому решил посмотреть, что же там, в разделе CrackMe, происходит. Первый в списке крякми Zlo'го. По словам автора, он использует один малоизвестный, но очень хороший прот. Стало интересно, что за протектор такой, что распаковать не могут. Вобщем, сегодня я поведаю о результатах исследования. [t00ls] OllyDbg (XP / Shadow кому какая больше нравится) // отладчик // Статья для новичков, поэтому я взял минимальный набор плагинов // только самое необходимое. Впоследствии мы будем дополнять нашу подборку. // Эти плаги, и последующие, можно достать на tuts4you.com [+] OllyDump [+] OllyScript [+] CommandBar // И ещё вот эти тулзы IDA // лучший дизассемблер ImpREC // тулза для восстановления импорта LordPE // PE Editor [BeGin] Итак, начнём. Грузим запакованную прогу в Olly. Будем пропускать все исключения. Стек забивается регистрами, потом выравнивается. Далее следует 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 и брякаемся. Снимаем бряк и выходим из функции (пару раз F8). И вот функция проверки на наличие отладчика По ходу, функция проверяет две переменные на равенство единице. Если хотябы одна равна единице, то выдаётся 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! Ещё разок 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. Ставим бряк на выход из функции. 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, чтож пройдёмся по этому адресу! А вот и переходники Пройдёмся по этому переходу 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). Мдяя... Думаю пора пересесть на 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. Никаких Rebuild'ов Import! Жмём Dump. Отлично! Всё сдампилось =)). Теперь собственно восстановление. Запускаем ImpREC, открываем наш процесс, вводим oep, IAT AutoSearch, GetIports. Функции найденны, это радует. Теперь фиксируем наш дамп. ImpREC говорит всё прошло отлично. Момент истины! Запускаем! Оба ошибка инициализации данных 0xc000007b! Тут что то не так с PE-заголовком. Нужно восстановить. Для этого нам понадобится LordPE. В нём есть одна очень полезная фитча - Rebuild PE. Как можно понять из названия она восстановит наш PE. Готово! Попробуем запустить теперь! Уряяяя!! Заработало. Вот с этим у меня возникли главные проблемы, пока не вспомнил про эту фитчу. [library] www.cracklab.ru www.wasm.ru www.tuts4you.com http://guru-exe.ripgames.org/ [heppy end] Вроде всё! Что ещё сказать? До новых встреч! =)))