Мишень: HP-crackme (272кб) Что еще нужно: 1. OllyDbg + плагины 2. ImpRec 1.6 Final 3. IDA 4. Ну и, естессно, некривые руки Для кого это написано? Сею статья я написал, прежде всего, для новичков в крекинге, которым (как я слышал) зачастую не все понятно в несколько однобоком современном подходе к вопросу крекинга. Ведь со временем прямо на глазах исчезают туторы, где бы не описывалось только один из подходов реверсинга (только распаковка, восстановление импорта, правка в памяти кода программы, нахождение верного серийник, или пропатчивание проги), а все они использовались комплексно. Предчуствую, что "асы" крекинга c ухмылкой прочли прошлую строку и начнут мне твердить, что, например, распаковка - это уже больше половины выполненой работы. Но, несмотря на это, я решился написать тутор "от А до Я" - от ручной распаковки "жертвы" до ее взлома патчингом файла и нахождением верных регистрационных данных. Думаю, многим начинающим будет полезно "прощупать" различные варианты взлома крекми. Ручная распаковка Так выглядит наш крекмиЗапустим OllyDbg и загрузим в него наш крекми. Мы остановимся на EP и увидим такую картину: Находим оригинальную точку входа видим, что вызывается какая-то функция, которой передаются 3 аргумента. Далее выполняется прыжок на тоже пока не известный код. Замечаем, что по адресу 00482019 находится массив интересных строк. Судя по ним, наша программа запакована паковщиком PKLite и чтобы ее исследовать, модифицировать ее придется распаковывать. (пойдя путем наименьшего сопративления было решено воспользоваться плагином автораспаковки в PEID 0.92, но ему, почему-то, пакер пришелся "не по зубам"). Немного протрассировав код можно узнать, что по адресу 0040200F находится процедура распаковки упакованного "тела" программы. После завершения ее работы крекми в памяти будет находиться в полностью функционильном состоянии. Чтобы его отдампить нужно найти OEP. Самое интересное то, что сразу после процедуры распаковки и выполняется прыжок на эту самую OEP (JMP 00468A48). Жмем F8 и очутимся на ней. Теперь можно и дампить. Для этого можно использовать плагин к Оле OllyDump (Plugins-OllyDump-DumpDebuggedProcess-Dump). Появляется такое окно: дампим...
Восстановление импорта Просто дампнутый крекми(я назвал файл dumped.exe) запускаться не будет(выдается ошибка нахождения точки входа одной из апи) - ему нужно восстановить таблицу импортированных функций. Для этого запустим крекми(hp.exe) и после него ImpRec. В нем вводим адрес нашей OEP и кликаем по "IAT AutoSearch"-"Get Imports"-"Fix Dump" Imprec восстанавливает импорт... итак, мы получили ПОЛНОСТЬЮ работоспособный распакованый крекми (dumped_.exe)! Приступим к его анализу... Дизассемблирование и взлом нахождением верного ключа Дизассемблируем дамп Идой и загружаем сигнатуру Delphi(поскольку прога написана именно в этой среде программирования). Даже при беглом просмотре кода в глаза сразу кидается огромное количество операций со строками (начиная с адреса 00466BC7). Смотрим немного выше: Code: loc_466B0C: ; CODE XREF: CODE:00466B11j push 0 push 0 dec ecx jnz short loc_466B0C push ecx push ebx push esi push edi mov [ebp-4], eax mov ebx, offset unk_46BC58 mov esi, offset unk_46BC78 xor eax, eax push ebp push offset loc_468100 push dword ptr fs:[eax] mov fs:[eax], esp lea edx, [ebp-8] mov eax, [ebp-4] mov eax, [eax+308h] call @TControl@GetText$qqrv ; считываем имя юзера с Edit1 cmp dword ptr [ebp-8], 0 jnz short loc_466B58 ; прыгаем если ввели хоть что-то mov eax, offset _str________________.Text call @Dialogs@ShowMessage$qqrx17System@AnsiString ; Dialogs::ShowMessage(System::AnsiString) jmp loc_4680C0 ; jmp на выход Далее (00466C43) идет код считывания имени, ключа, серийника и проверка ввели лы мы их: Code: call @TControl@GetText$qqrv ; eax = length(name) mov edx, [ebp-0Ch] mov eax, offset dword_46BC64 call @System@@LStrAsg$qqrv ; System::__linkproc__ LStrAsg(void) lea edx, [ebp-10h] mov eax, [ebp-4] mov eax, [eax+2F4h] call @TControl@GetText$qqrv ; eax = length(key) mov edx, [ebp-10h] ; edx = *key mov eax, offset unk_46BC5C call @System@@LStrAsg$qqrv ; System::__linkproc__ LStrAsg(void) lea edx, [ebp-14h] mov eax, [ebp-4] mov eax, [eax+2F0h] call @TControl@GetText$qqrv ; eax=length(serial) mov edx, [ebp-14h] ; edx= *serial mov eax, offset unk_46BC60 call @System@@LStrAsg$qqrv ; System::__linkproc__ LStrAsg(void) mov eax, ds:dword_46BC64 call @System@_16823 ; eax=length(name) mov edi, eax test edi, edi jle loc_46740E ; если не ввели имя, то выходим mov ds:dword_46BC7C, 1 Пропустив около 100 операций копирования и преобразования строк и столько же арифметических (автор, видимо, думал что такое их количество должно отпугнуть начинающих крекеров) видим уже более интересную картину: Code: push eax lea edx, [ebp-6Ch] mov eax, [ebp-4] mov eax, [eax+2F4h] call @TControl@GetText$qqrv ; считываем ключ mov edx, [ebp-6Ch] pop eax ; eax = valid key! call @System@@LStrCmp$qqrv ; System::__linkproc__ LStrCmp(void) jnz loc_4680C0 ; прыгаем если не равны push ds:dword_46BD00 push ds:dword_46BD04 push ds:dword_46BD08 push ds:dword_46BD0C push offset _str___22.Text push ds:dword_46BC98 push ds:dword_46BC9C push ds:dword_46BCA0 push ds:dword_46BCA4 push ds:dword_46BCA8 push offset _str___22.Text push ds:dword_46BCF4 push ds:dword_46BCF8 push ds:dword_46BCFC lea eax, [ebp-70h] mov edx, 0Eh ; --------------- S U B R O U T I N E --------------------------------------- sub_468062 proc near call @System@@LStrCatN$qqrv ; подс4ет валидного регномера mov eax, [ebp-70h] ; сохраняем его в eax push eax lea edx, [ebp-74h] mov eax, [ebp-4] mov eax, [eax+2F0h] call @TControl@GetText$qqrv ; TControl::GetText(void) mov edx, [ebp-74h] pop eax call @System@@LStrCmp$qqrv ; финальное сравнение ! jnz short loc_4680C0 ; прыгаем, если BadGuy mov edx, 190h mov eax, ds:dword_46BC50 call @Forms@TCustomForm@SetClientWidth$qqri ; Forms::TCustomForm::SetClientWidth(int) mov eax, [ebp-4] mov eax, [eax+310h] mov dl, 1 call @Controls@TControl@SetVisible$qqro ; Controls::TControl::SetVisible(bool) mov eax, [ebp-4] mov eax, [eax+314h] mov dl, 1 call @Controls@TControl@SetVisible$qqro ; Controls::TControl::SetVisible(bool) mov ds:dword_46BCC4, 1 loc_4680C0: ; CODE XREF: CODE:00466B53j ; CODE:00468002j ... xor eax, eax ; выходим... pop edx pop ecx pop ecx mov fs:[eax], edx push offset loc_468107
Итак, грузим Олю и ставим бряки на 00467FFD и 00468080, где в eax'ах храняться наши верные регистрационные данные. Подсматриваем их и вводим в крякми. Мои валидные данные таковы: Взлом jump-коррекцией А для нежелающих долго ковыряться в отладчике могу предложить просто создать несложный *.crk файл и пропатчить кряк. Теперь он не будет проверять длинну вводимых имен и будет думать, что валидные данные вводятся всегда =) 00066B47: 75 EB 00068002: 0F 90 00068003: 85 90 00068004: B8 90 00068005: 00 90 00068006: 00 90 00068007: 00 90 00068085: 75 90 00068086: 39 90 gl hf 2 all