Автор: [RU].Ban0K! <ru.ban0k@mail.ru>
Программа основана на (цитата автора): 1. Защита реализована по принципу имя-код (от имени юзера зависит код). 2. Когда вы найдёте правильный код, вы увидите, что красный квадратик с надписью "! Unregistered !" станет другого цвета, с надписью что-то вроде "зарегистрировано". 3. Вот некоторые фичи данной защиты: Защита от String Reference взлома, использование самомодифицирующегося кода (а-ля АссПротект), немножко самодельной криптографии (хэш), использование регистрационного кода в качестве адреса ... всё это я в данной статье опишу и исследую...
00406676 8BC0 MOV EAX,EAX 00406678 -FF25 C0E44400 JMP DWORD PTR DS:[<&USER32.LoadKeyboardLayoutA>] 0040667E 8BC0 MOV EAX,EAX 00406680 -FF25 BCE44400 JMP DWORD PTR DS:[<&USER32.LoadStringA>] 00406686 8BC0 MOV EAX,EAX 00406688 -FF25 B8E44400 JMP DWORD PTR DS:[<&USER32.MapVirtualKeyA>] 0040668E 8BC0 MOV EAX,EAX 00406690 -FF25 B4E44400 JMP DWORD PTR DS:[<&USER32.MapWindowPoints>] 00406696 8BC0 MOV EAX,EAX 00406698 -FF25 B0E44400 JMP DWORD PTR DS:[<&USER32.MessageBoxA>] 0040669E 8BC0 MOV EAX,EAX 004066A0 -FF25 ACE44400 JMP DWORD PTR DS:[<&USER32.OemToCharA>] 004066A6 8BC0 MOV EAX,EAX 004066A8 -FF25 A8E44400 JMP DWORD PTR DS:[<&USER32.OffsetRect>] 004066AE 8BC0 MOV EAX,EAX 004066B0 -FF25 A4E44400 JMP DWORD PTR DS:[<&USER32.PeekMessageA>]Ну конечно таких строк будет море и вы их не пропустите.... Так вот значит что, ставим на 405598 бряку, жмём F9, ... набираем пароль логин и ... и всё функция не была вызвана.... Далее... скорее всего присутствует функция GetWindowTextA... ставим бряку на неё ... F9! ... прописываем липовые пароль и логин.... F9! ... а вот и мы... Далее нам придется трассить до тех пор пока мы не выйдем из уровня 77XXXXXX на наш уровень 0044XXXX, а точнее на такую процедуру:
00443BF0 PUSH EBX 00443BF1 PUSH ESI 00443BF2 ADD ESP,-100 00443BF8 MOV ESI,EDX 00443BFA MOV EBX,EAX 00443BFC CMP BYTE PTR DS:[EBX+94],0 00443C03 JE SHORT NEW.00443C25 00443C05 PUSH 100 00443C0A LEA EAX,DWORD PTR SS:[ESP+4] 00443C0E PUSH EAX 00443C0F MOV EAX,DWORD PTR DS:[EBX+24] 00443C12 PUSH EAX 00443C13 CALLСамо сабой что мы попали на адрес 00443C18... обидно только то что данная процедура не читает ни наш пароль ни логин... она скорее всего относится к вызову окошка .... которое идёт вместо MessageBox.. т.к. в той процедуре нам ничего не надо то проходим её по F8, нас интересует что было раньше неё... процедура выше... скорее всего занимается формированием этого же окошка, т.к. прямо в её теле находится вызов API LoadIcon а так же ссылка на строку "Message"... проходим её также по F8.... Проходим выше... сморим на неё... так как две процедуры были подготовительными, и OllyDbg не остановился для того чтобы подождать нажатия кнопки OK... следовательно это тоже либо подготовительная либо процедура вызова сообщения на экран... проходим её по F8... и отладчик остановился на после входа в процедуру:00443C18 MOV ECX,EAX 00443C1A MOV EDX,ESP 00443C1C MOV EAX,ESI 00443C1E CALL NEW.004039E0 00443C23 JMP SHORT NEW.00443C2F 00443C25 MOV EAX,ESI 00443C27 MOV EDX,DWORD PTR DS:[EBX+7C] 00443C2A CALL NEW.0040394C 00443C2F ADD ESP,100 00443C35 POP ESI 00443C36 POP EBX 00443C37 RETN
004471A1 CALL DWORD PTR DS:[EDX+D8]... переводим окно КрэкМе на первый план и видим сообщение гласящее что-то типа " Ты ещё ничего не сделал "... жмём OK... переходим выше. Тут видим маленькую процедуру с вызовом одного CALL-а... в который мы уже входили... а также перенос параметров из стэка... две процедуры ещё выше такие же, там ничего интересного только CALL-ы... проходим их по F8.... Далее видим ОГРОМНУЮ (хотя это только из-за самодельного UN-(String Reference) кода... ) процедуру .... поднимаемся вверх и ставим на самое начало бряк....
0044A454 PUSH EBP 0044A455 MOV EBP,ESP 0044A457 MOV ECX,45 0044A45C PUSH 0 0044A45E PUSH 0 0044A460 DEC ECX 0044A461 JNZ SHORT NEW.0044A45CЭто начало процедуры..... в начале ... ммм... начало... потом идёт заполнение стэка нулями.... всего 45 + 45 = 8A нулей... далее процедура выглядит так:
0044A463 PUSH EBX 0044A464 PUSH ESI 0044A465 PUSH EDI 0044A466 MOV EBX,EAX 0044A468 XOR EAX,EAX 0044A46A PUSH EBP 0044A46B PUSH NEW.0044B012 0044A470 PUSH DWORD PTR FS:[EAX] 0044A473 MOV DWORD PTR FS:[EAX],ESP 0044A476 LEA EDX,DWORD PTR SS:[EBP-8] 0044A479 MOV EAX,DWORD PTR DS:[EBX+2E0] 0044A47F CALL NEW.00426020 0044A484 MOV EAX,DWORD PTR SS:[EBP-8] 0044A487 CALL NEW.00403B78 0044A48C CMP EAX,14 0044A48F JNZ NEW.0044AD69Ну что сказать.. до 44A484 мало чего интересного, кроме того что в ячёйке по адресу EBP-8, появляется адрес ячейки вашего пароля.... далее смотрим... по адресу 44A487 происходит вызов стандартной процедуры (потому что из дельфей) высчитывания длины строки адрес которой находится в EAX, ... длина так же возвращается в EAX... далее в 44A48C длина сравнивается с 14, и выполняет или не выполняет прыжок на то место из которого мы поднялись (ну почти на то... там ещё идёт код заполнения стэка)... следовательно нам туда не надо... значит пароль должен состоять из 20 символов (14 в hex это 20 в dec )... жмём F9, и переставляем наш пароль 1234567890 на 12345678901234567890.... и нажимаем регистринг.... после того как мы не совершили прыжок... идёт перекодировка логина...
0044A4A6 CALL NEW.00449A08но с этой процедурой мы разберёмся немного позже... Далее идёт процедура ( самое интересное что проверка идёт прямо в этой процедуре не входя в другие... если вы работаете в OllyDbg то она у вас будет выделена как процедура в процедуре... ) проверка кода на разрешённые символы.... ну и как вы думаете на какие? Конечно на "1".."0","A".."F"... эта процедура начинается в 0044A4DF и успешно заканчивается в 0044A689...
0044A68F LEA EAX,DWORD PTR SS:[EBP-88] 0044A695 PUSH EAX 0044A696 MOV ECX,8 0044A69B MOV EDX,1 0044A6A0 MOV EAX,DWORD PTR SS:[EBP-4] 0044A6A3 CALL NEW.00403D80Это так же стандартная процедура Дельфи... которая обрезает строку по адресу EAX с EDX по ECX символ... и помещает этот обрезок в память по адресу указанному в параметре стэка... в нашем случае это:
0044A6A8 MOV EAX,DWORD PTR SS:[EBP-88] 0044A6AE PUSH EAX 0044A6AF LEA EAX,DWORD PTR SS:[EBP-8C] 0044A6B5 PUSH EAX 0044A6B6 LEA EDX,DWORD PTR SS:[EBP-90] 0044A6BC MOV EAX,DWORD PTR DS:[EBX+2E0] 0044A6C2 CALL NEW.00426020 0044A6C7 MOV EAX,DWORD PTR SS:[EBP-90] 0044A6CD MOV ECX,8 0044A6D2 MOV EDX,1 0044A6D7 CALL NEW.00403D80Обрезок загоняется в стэк... и такие же действия (обрезка) производятся с введённым нами паролем... интересно для чего :) ???
0044A6DC MOV EDX,DWORD PTR SS:[EBP-8C] 0044A6E2 POP EAX 0044A6E3 CALL NEW.00403C88 0044A6E8 JE NEW.0044A89DА вот для чего.... в EDX обрезок пароля, а в ЕАХ посылается адрес закодированной и обрезанной части логина ( точнее он выталкивается из стэка ), а далее выполняется стандартная процедура Delphi для сравнения строк которые находятся по адресам EAX и EDX... если не равны.... то обидно короче... надеюсь вы запомнили/записали эту часть закодированного логина??? короче это надо было сделать... у меня это FC619C0C... это будут первые 8 символов пароля....
0044A89D LEA EAX,DWORD PTR SS:[EBP-EC] 0044A8A3 PUSH EAX 0044A8A4 LEA EAX,DWORD PTR SS:[EBP-F4] 0044A8AA PUSH EAXУстанавливает в стэк две ячейки позднее это будут адрес строки и её длина
0044A8AB LEA EDX,DWORD PTR SS:[EBP-F8] 0044A8B1 MOV EAX,DWORD PTR DS:[EBX+2E0] 0044A8B7 CALL NEW.00426020Находит длину пароля...
0044A8BC MOV EAX,DWORD PTR SS:[EBP-F8] 0044A8C2 MOV ECX,10 0044A8C7 MOV EDX,1 0044A8CC CALL NEW.00403D80Обрезает наш пароль от 1 до 16 символа
0044A8D1 MOV EAX,DWORD PTR SS:[EBP-F4] 0044A8D7 CALL NEW.00449A08Кодирует строку ( доставая из стэка её адрес )
0044A8DC PUSH EDX 0044A8DD PUSH EAX 0044A8DE LEA EDX,DWORD PTR SS:[EBP-F0] 0044A8E4 MOV EAX,10 0044A8E9 CALL NEW.00407C38Данная процедура преобразует кодированный пароль в строку
0044A8EE MOV EAX,DWORD PTR SS:[EBP-F0] 0044A8F4 MOV ECX,4 0044A8F9 MOV EDX,4 0044A8FE CALL NEW.00403D80 0044A903 MOV EAX,DWORD PTR SS:[EBP-EC] 0044A909 PUSH EAXОбрезается эта строка с 4 символа и до 8 .... короче 4 символа всего... и загоняется в стэк...
0044A90A LEA EAX,DWORD PTR SS:[EBP-FC] 0044A910 PUSH EAX 0044A911 LEA EDX,DWORD PTR SS:[EBP-100] 0044A917 MOV EAX,DWORD PTR DS:[EBX+2E0] 0044A91D CALL NEW.00426020Нахождение длинны пароля.... господи опять....
0044A922 MOV EAX,DWORD PTR SS:[EBP-100] 0044A928 MOV ECX,4 0044A92D MOV EDX,11 0044A932 CALL NEW.00403D80 0044A937 MOV EDX,DWORD PTR SS:[EBP-FC] 0044A93D POP EAXОбрезание оного... с начиная с 17 и ... тоже 4 символа.... загоняется в стэк...
0044A93E CALL NEW.00403C88 0044A943 JE NEW.0044AA73Достаёт из стэка адреса двух 4-x байтых обрезков и выполняет процедуру сравнения строк... если верно то прыгает если... нет то нет... :) Отсюда мы узнаём последние 4 байта нашего пароля... у меня это 0A4E но вы должны так же понять что эти 4 байта являются как бы контрольной суммой пароля, и при любых (ну как сказать... ну не любых... даже при только некоторых) изменениях в первых двух частях пароля (первая уже будет константой всегда... т.к. она генерируется исключительно из логина...) она тоже будет меняться... теперь мы нашли, можно так сказать "поддельный" пароль.... осталось найти настоящий... вся эта часть должна быть вам ПОЛНОСТЬЮ понятна... если же это не так... то либо ещё раз её прочитайте либо не продолжайте читать статью... ибо это бесполезно... далее дела намного труднее и интереснее...
0044AA73 LEA EAX,DWORD PTR SS:[EBP-140] 0044AA79 PUSH EAX 0044AA7A LEA EDX,DWORD PTR SS:[EBP-144] 0044AA80 MOV EAX,DWORD PTR DS:[EBX+2E0] 0044AA86 CALL NEW.00426020.... блин опять проверка длинны пароля....
0044AA8B MOV EAX,DWORD PTR SS:[EBP-144] 0044AA91 MOV ECX,8 0044AA96 MOV EDX,1 0044AA9B CALL NEW.00403D80Обрезать первые 8 символов...
0044AAA0 MOV EAX,DWORD PTR SS:[EBP-140] 0044AAA6 CALL NEW.0044A0ECПроцедура переделывает 8 символов в число (точнее почти в адрес...)
0044AAAB MOV ESI,EAXЧисло из EAX в ESI....
0044AAAD LEA EAX,DWORD PTR SS:[EBP-148] 0044AAB3 PUSH EAX 0044AAB4 LEA EDX,DWORD PTR SS:[EBP-14C] 0044AABA MOV EAX,DWORD PTR DS:[EBX+2E0] 0044AAC0 CALL NEW.00426020Хм... меня уже бесит эта процедура....
0044AAC5 MOV EAX,DWORD PTR SS:[EBP-14C] 0044AACB MOV ECX,8 0044AAD0 MOV EDX,9 0044AAD5 CALL NEW.00403D80Вырезание из нашего пароля вторых 8 символов... наконец-то вот и они задействованы....
0044AADA MOV EAX,DWORD PTR SS:[EBP-148] 0044AAE0 CALL NEW.0044A0ECВторая часть пароля в из строки в число...
0044AAE5 XOR ESI,EAXКсоринг первой части со второй частью.... само собой отксоренное число в ESI
0044AAE7 MOV DWORD PTR DS:[44D880],ESIЭто отксоренное число в 44D880... запомните это число, если вы были внимательны то это число и есть адрес по которому при правильном пароле находится процедура, вот если честно сказать, что еслиб процедура проверки на этом заканчивалась... то сломать эту защиту не зная кода этой процедуры... найти пароль было бы трудновато.... но автор на этом не закончил и облегчил нам задачу... а зря...
0044AAED LEA EAX,DWORD PTR SS:[EBP-150] 0044AAF3 PUSH EAX 0044AAF4 LEA ECX,DWORD PTR SS:[EBP-158] 0044AAFA MOV EDX,8 0044AAFF MOV EAX,DWORD PTR DS:[44D880] 0044AB04 CALL NEW.00407BFCПеренос числа (а точнее адреса процедуры) ... в строку
0044AB09 MOV EAX,DWORD PTR SS:[EBP-158] 0044AB0F CALL NEW.00449A08Кодирование этой строки
0044AB14 PUSH EDX 0044AB15 PUSH EAX 0044AB16 LEA EDX,DWORD PTR SS:[EBP-154] 0044AB1C MOV EAX,10 0044AB21 CALL NEW.00407C38Также .... перенос из числа в строку
0044AB26 MOV EAX,DWORD PTR SS:[EBP-154] 0044AB2C MOV ECX,8 0044AB31 MOV EDX,5 0044AB36 CALL NEW.00403D80Вырезание 8 символов начиная с 5-й позиции
0044AB3B MOV EAX,DWORD PTR SS:[EBP-150] 0044AB41 PUSH EAXОбрезанные 8 байт в стэк
0044AB42 PUSH 0 0044AB44 PUSH E8484694 0044AB49 LEA EDX,DWORD PTR SS:[EBP-15C] 0044AB4F MOV EAX,8 0044AB54 CALL NEW.00407C38Перенос числа E8484694 в строку...
0044AB59 MOV EDX,DWORD PTR SS:[EBP-15C]... и в EDX
0044AB5F POP EAXОбрезок нашего закодированного адреса из стэка в EAX
0044AB60 CALL NEW.00403C88Сравнить строки...
0044AB65 JE NEW.0044AD5A... и прыгнуть если что....
00449B52 MOV EAX,DWORD PTR SS:[EBP-20] 00449B55 CALL NEW.00403B78 00449B5A PUSH EAX 00449B5B MOV EAX,EBX 00449B5D POP EDX 00449B5E MOV ECX,EDX 00449B60 CDQ 00449B61 IDIV ECX 00449B63 MOV EAX,DWORD PTR SS:[EBP-20] 00449B66 MOVZX EAX,BYTE PTR DS:[EAX+EDX] 00449B6A MOV EDI,EBX 00449B6C AND EDI,80000007 00449B72 JNS SHORT NEW.00449B79 00449B74 DEC EDI 00449B75 OR EDI,FFFFFFF8 00449B78 INC EDI 00449B79 XOR EDX,EDX 00449B7B MOV DL,BYTE PTR SS:[EBP+EDI-8] 00449B7F IMUL EDX 00449B81 AND EAX,800000FF 00449B86 JNS SHORT NEW.00449B8F 00449B88 DEC EAX 00449B89 OR EAX,FFFFFF00 00449B8E INC EAX 00449B8F MOV BYTE PTR SS:[EBP+EDI-8],AL 00449B93 INC EBX 00449B94 DEC ESI 00449B95 JNZ SHORT NEW.00449B52В ней не трудно разобраться... я расскажу только ключевые моменты.... Кодирующее слово находится в EBP+EDI-8 (оно туда было занесено до этого кода), причём EDI в самом начале равно 1, а не 0.Заметьте что данная штука перекодирует наш адрес не один... а два раза, смещая кодирующий код при первом разе на один символ... в общем всю эту ерунду можно заменить такими математическими выражением (здесь я показал $N как N-ый символ адреса...., а _N как N-ый символ полученного "хэша".... их мы не узнаем :(, хотя 4 из них нам известны... это 94 46 48 E8 (именно так... перевёрнуто...) :D ) :
49 * $2 * $1 = _1 BC * $3 * $2 = 94 3A * $4 * $3 = 46 DE * $5 * $4 = 48 2F * $6 * $5 = E8 FD * $7 * $6 = _6 1A * $8 * $7 = _7 75 * 2A * $8 = _8Ну для тех кто не понял что это за такое ... то вам надо рассмотреть эту процедуру по лучше... или подучить ASM....
function GetNum ($F,$L) { for ($I=0x30;$I<0x47;$I++) { for ($J=0x30;$J<0x47;$J++) { $S = $J*$I*$F; $D = $S & 0xff; if ($D==$L) echo ($I." ".$J."<br>"); } } } echo "<table>"; echo "<td>||"; echo "<td>"; GetNum(0xbc,0x94); echo "<td>||"; echo "<td>"; GetNum(0x3a,0x46); echo "<td>||"; echo "<td>"; GetNum(0xde,0x48); echo "<td>||"; echo "<td>"; GetNum(0x2f,0xe8);Она просто возвращает мне те числа которые подходят под уравнение... вот результат её работы (Имеется ввиду что она выдала такие параметры...):
----- 57 67 67 57 ----- 51 59 55 57 57 55 69 51 ----- 50 62 52 67 55 68 62 50 67 52 68 55 ----- 52 62 53 56 56 53 58 60 60 58 68 70 70 68 -----Ищем в этой таблице сходства... и находим цепочку.. 67 57 55 68 70 ... в HEX как 43 39 37 44 46.... а строка C97DF
0044AD5A XOR DWORD PTR DS:[44D880],5CD3407DЯ вам говорил запомните эту ячейку!.... вот и она... здесь наш адрес ксорится ещё раз... до хорошего адреса... поксорив наш недоделанный адрес ( 0С97DF00 ) получаем.... 50449F7D... Задумались.... с какого фига там 5 в начале... так как всё остальное более или мене подходит под шаблон адреса (у нас есть такие адреса в проге начинающиеся с 00449FXX ( последние два мы ещё не нашли...) ) значит нам надо чтобы поксори эту ерунду у нас вышло что-то типа 00449FXX.... ну те кто знают как работает XOR поймут что пароль в этом случае будет не FCBE0B00, а FCBE0B50... далее осталось найти последние два байта... просто имея это всё их найти не возможно, надо просто искать процедуру... в памяти где-то.... на промежутке 449F00-449FFF... но искать её лучше в IDA... т.к. OllyDbg не показал её... :(
00449F34 push ebp 00449F35 mov ebp, esp 00449F37 mov ecx, 6 00449F3C push 0 00449F3E push 0 00449F40 dec ecx 00449F41 jnz short loc_449F3C 00449F43 xor eax, eax 00449F45 push ebp 00449F46 push offset unk_44A04E 00449F4B push dword ptr fs:[eax] 00449F4E mov fs:[eax], esp 00449F51 push eax 00449F52 push edx 00449F53 push ecx 00449F54 mov edx, 34h 00449F59 mov eax, ds:dword_44D880 00449F5F mov [ebp-4], eax 00449F62 add dword ptr [ebp-4], 48h 00449F66 mov ecx, [ebp-4] 00449F69 mov eax, [ecx] 00449F6B xor eax, 96969696h 00449F70 mov [ecx], eax 00449F72 add dword ptr [ebp-4], 4 00449F76 dec edx 00449F77 jnz short loc_449F66 00449F79 pop ecx 00449F7A pop edx 00449F7B pop eax 00449F7C aaa 00449F7D jmp far ptr 7A16h:1D96D24EhЧто она делает нас уже мало волнует... т.к. мы нашли свои последние 2 байта.... это 34... перед вторым ксорингом... это 49... перед первым B5.... потом восстанавливаем контрольную часть.... 0DAA и получаем наконец пароль!!! -- FC619C0CB5BE0B500DAA на логин [RU].Ban0K! ...
Комментарий Bad_guy'я от 1 сентября 2003 года: Меня очень удивила эта настолько грамотная статья от Рубанка, кое-что даже я не понял с первого раза, учитывая что писал рассматриваемый крэкми. Но сам способ взлома - не самый рациональный, в смысле - слишком трудоемкий, хотя, по-видимому, самый последовательный из всех, которые были осуществлены (всего 3 способа).
Материалы находятся на сайте http://cracklab.narod.ru/doc/