• XSS.stack #1 – первый литературный журнал от юзеров форума

CrackMe Ручная распаковка и взлом HP-crackme (writeup)

ProTeuS

RAID-массив
Пользователь
Регистрация
22.07.2006
Сообщения
54
Реакции
1
Мишень: HP-crackme (272кб)
Что еще нужно:
1. OllyDbg + плагины
2. ImpRec 1.6 Final
3. IDA
4. Ну и, естессно, некривые руки ;)

Для кого это написано?
Сею статья я написал, прежде всего, для новичков в крекинге, которым (как я слышал) зачастую не все понятно в несколько однобоком современном подходе к вопросу крекинга. Ведь со временем прямо на глазах исчезают туторы, где бы не описывалось только один из подходов реверсинга (только распаковка, восстановление импорта, правка в памяти кода программы, нахождение верного серийник, или пропатчивание проги), а все они использовались комплексно. Предчуствую, что "асы" крекинга c ухмылкой прочли прошлую строку и начнут мне твердить, что, например, распаковка - это уже больше половины выполненой работы. Но, несмотря на это, я решился написать тутор "от А до Я" - от ручной распаковки "жертвы" до ее взлома патчингом файла и нахождением верных регистрационных данных. Думаю, многим начинающим будет полезно "прощупать" различные варианты взлома крекми.

Ручная распаковка
crackme.jpg

Так выглядит наш крекми​
Запустим OllyDbg и загрузим в него наш крекми. Мы остановимся на EP и увидим такую картину:
olly_load.jpg

Находим оригинальную точку входа​

видим, что вызывается какая-то функция, которой передаются 3 аргумента. Далее выполняется прыжок на тоже пока не известный код. Замечаем, что по адресу 00482019 находится массив интересных строк. Судя по ним, наша программа запакована паковщиком PKLite и чтобы ее исследовать, модифицировать ее придется распаковывать. (пойдя путем наименьшего сопративления было решено воспользоваться плагином автораспаковки в PEID 0.92, но ему, почему-то, пакер пришелся "не по зубам"). Немного протрассировав код можно узнать, что по адресу 0040200F находится процедура распаковки упакованного "тела" программы. После завершения ее работы крекми в памяти будет находиться в полностью функционильном состоянии. Чтобы его отдампить нужно найти OEP. Самое интересное то, что сразу после процедуры распаковки и выполняется прыжок на эту самую OEP (JMP 00468A48). Жмем F8 и очутимся на ней. Теперь можно и дампить. Для этого можно использовать плагин к Оле OllyDump (Plugins-OllyDump-DumpDebuggedProcess-Dump). Появляется такое окно:
oep.jpg

дампим...​
Добавлено в [time]1162594191[/time]
Восстановление импорта

Просто дампнутый крекми(я назвал файл dumped.exe) запускаться не будет(выдается ошибка нахождения точки входа одной из апи) - ему нужно восстановить таблицу импортированных функций. Для этого запустим крекми(hp.exe) и после него ImpRec. В нем вводим адрес нашей OEP и кликаем по "IAT AutoSearch"-"Get Imports"-"Fix Dump"
imprec.jpg

Imprec восстанавливает импорт...​

итак, мы получили ПОЛНОСТЬЮ работоспособный распакованый крекми (dumped_.exe)! Приступим к его анализу...

Дизассемблирование и взлом нахождением верного ключа

Дизассемблируем дамп Идой и загружаем сигнатуру Delphi(поскольку прога написана именно в этой среде программирования). Даже при беглом просмотре кода в глаза сразу кидается огромное количество операций со строками (начиная с адреса 00466BC7). Смотрим немного выше:

Код:
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) идет код считывания имени, ключа, серийника и проверка ввели лы мы их:

Код:
                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 операций копирования и преобразования строк и столько же арифметических (автор, видимо, думал что такое их количество должно отпугнуть начинающих крекеров) видим уже более интересную картину:

Код:
                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
Добавлено в [time]1162594271[/time]
Итак, грузим Олю и ставим бряки на 00467FFD и 00468080, где в eax'ах храняться наши верные регистрационные данные. Подсматриваем их и вводим в крякми. Мои валидные данные таковы:

key.jpg

serial.jpg

http://securityprobe.net/other/ProTeuS/cracked[.]jpg

Взлом 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
 
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
ProTeuS, а можно заставить олю распознавать функции дельфи, типа @TControl@GetText, @System@@LStrAsg, итд.?
 
>>ProTeuS, а можно заставить олю распознавать функции дельфи, типа @TControl@GetText, @System@@LStrAsg, итд.?

напрямую нет, т.к. встроеный в OllyDbg дизассемблер с IDA не сравним. Но для выхода из этой ситуации можно проделать такие шаги:

1. выбрать и дизассмить файл в IDA
2. вызавать File -> Load File -> FLIRT Signature File -> соответствующая сигнатура (delphi5,6,7,VCL), которую можно ска4ать, например, с cracklab.ru
3. Produce File -> Create Map File
4. Открываем OllyDbg с плагином GoDUP(ка4аем с tuts4you.com) и выбираем в его настройках сгенеренный Идой МАП-файл
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх