Здравствуйте Хабралюди. В этом топике я расскажу как я исследовал простой crackme. Этот крякмис предназначен, в первую очередь, для новичков, желающих попрактиковаться в реверсинге. Я планирую продолжать цикл статей этой тематики, двигаясь постепенно от простого к сложному.
Итак, приступим.
Необходимые инструменты:
Для начала запустим крякми. Видим простую форму с двумя полями для ввода имени и регистрационного кода. Вводим фейковые данные и видим ругательное сообщение о том, что серийник неправилный.
Ситуация проясняется когда видим мы сообщение, вызванное функцией MessageBoxA, а сам текст из полей заносится в буффер функцией GetDlgItemTextA.
Итак дальнейший алгоритм действий:
1. Загружаем крякми в отладчик.
2. Ставим бряки на все вызовы функции MessageBoxA и GetDlgItemTextA (в Olly это делается нажатием сочитания клавиш Alt + F1 и вводом, в появившуюся форму, команды bpx %имя_функции% ).
3. Выполняем программу до бряка и: а) ищем место перехода на ветку с ругательным сообщением и меняем условный переход так, что бы мы переходили на ветку с «правильным» сообщением;
б) ищем место модификации имени в серийник, выясняем алгоритм шифровки и пишем кейген.
Мы пойдём по плану б, так как основная моя цель-это исследовать.
Расставив бряки и запустив программу, мы видим то, что бряк на MessageBoxA сработал. Но какой ещё МесаджБокс, ведь запустив программу в первый раз без отладчика, мы не видели никакого мессаджа? Протрассировав программу на шаг вперёд(в Олли нажав F8), лицезреем следующее:
Выходит в нашем крякмисе есть антиотладочный код. Придётся его обойти. Прокручиваем код немного вверх и выясняем откуда был прыжок на наш «антиотладочный мессадж бокс». Вот кусок кода с этого места:
Придётся патчить. Изменяем команду JNZ (опкод 75) на JE (опкод 74). Применяем патч, перезапускаем прогу под отладчиком и не видим ругательного мессадж бокса.
Вводим в поля фейковые данные (я вводил someuser:somepassword), и нажимаем на Check. И тут же срабатывает бряк на вызове функции GetDlgItemTextA. Вот этот участок кода:
Таким образом, мы видим алгоритм шифровки Username.
Трассируя дальше, мы попадаем на следующий участок:
На мой взгляд, это ещё один антиотладочный приём, поэтому заменяем JG на JE и спокойно трассируем дальше. И опять мы натыкаемся на бряк функции GetDlgItemTextA. В этот раз она считывает рег. код:
Таким образом, получается, что весь алгоритм шифрования — это замена каждого второго символа имени на символ, следующий в таблице ASCII.
Плюс к тому, программа проверяет только первые 8 символов серийника, что, соостветственно, является недостатком.
А вот и кейген:
Спасибо за внимание.
P.S. Извините за Паскаль, другого компилятора не нашёл.
P.P.S. И извините за простоту.
Начало
Итак, приступим.
Необходимые инструменты:
- 1. Сам crackme (с crackmes.de)
- 2. Отладчик (лично я использую Olly)
Для начала запустим крякми. Видим простую форму с двумя полями для ввода имени и регистрационного кода. Вводим фейковые данные и видим ругательное сообщение о том, что серийник неправилный.
Ситуация проясняется когда видим мы сообщение, вызванное функцией MessageBoxA, а сам текст из полей заносится в буффер функцией GetDlgItemTextA.
План действий
Итак дальнейший алгоритм действий:
1. Загружаем крякми в отладчик.
2. Ставим бряки на все вызовы функции MessageBoxA и GetDlgItemTextA (в Olly это делается нажатием сочитания клавиш Alt + F1 и вводом, в появившуюся форму, команды bpx %имя_функции% ).
3. Выполняем программу до бряка и: а) ищем место перехода на ветку с ругательным сообщением и меняем условный переход так, что бы мы переходили на ветку с «правильным» сообщением;
б) ищем место модификации имени в серийник, выясняем алгоритм шифровки и пишем кейген.
Мы пойдём по плану б, так как основная моя цель-это исследовать.
Антиотладочный код
Расставив бряки и запустив программу, мы видим то, что бряк на MessageBoxA сработал. Но какой ещё МесаджБокс, ведь запустив программу в первый раз без отладчика, мы не видели никакого мессаджа? Протрассировав программу на шаг вперёд(в Олли нажав F8), лицезреем следующее:
Выходит в нашем крякмисе есть антиотладочный код. Придётся его обойти. Прокручиваем код немного вверх и выясняем откуда был прыжок на наш «антиотладочный мессадж бокс». Вот кусок кода с этого места:
004027DA . 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
004027E0 . 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
004027E3 . 8B40 18 MOV EAX,DWORD PTR DS:[EAX+18]
004027E6 . 8378 10 00 CMP DWORD PTR DS:[EAX+10],0
004027EA 75 01 JNZ SHORT crackme_.004027ED //вот отсюда и совершается прыжок
Придётся патчить. Изменяем команду JNZ (опкод 75) на JE (опкод 74). Применяем патч, перезапускаем прогу под отладчиком и не видим ругательного мессадж бокса.
Алгоритм
Вводим в поля фейковые данные (я вводил someuser:somepassword), и нажимаем на Check. И тут же срабатывает бряк на вызове функции GetDlgItemTextA. Вот этот участок кода:
00401D3E . E8 CD0D0000 CALL <JMP.&user32.GetDlgItemTextA> //вызов самой функции GetDlgItemTextA
00401D43 . BE 64404000 MOV ESI,crackme_.00404064 //помещаем в ESI указатель на полученное слово
00401D48 > 8006 01 ADD BYTE PTR DS:[ESI],1 //добавляем 1 байт к первому байту слова находящемуся по адресу расположенному в ESI
00401D4B . 83C6 02 ADD ESI,2 // добавляем в ESI 2, тем самым указатель в ESI теперь указывает на то же слово, но без первых двух символов
00401D4E . 803E 00 CMP BYTE PTR DS:[ESI],0 //указывает ли ESI на пустую строку
00401D51 .^75 F5 JNZ SHORT crackme_.00401D48 //если нет то "прыгаем" на 00401D48
00401D53 . E9 BB010000 JMP crackme_.00401F13 //если да то "прыгаем" на 00401F13
Таким образом, мы видим алгоритм шифровки Username.
Трассируя дальше, мы попадаем на следующий участок:
00401F1D . 81F9 00500000 CMP ECX,5000
00401F23 -7F FE JG SHORT crackme_.00401F23
На мой взгляд, это ещё один антиотладочный приём, поэтому заменяем JG на JE и спокойно трассируем дальше. И опять мы натыкаемся на бряк функции GetDlgItemTextA. В этот раз она считывает рег. код:
00401FE6 > E8 250B0000 CALL <JMP.&user32.GetDlgItemTextA> //снова вызов функции GetDlgItemTextA
00401FEB . BE 64404000 MOV ESI,crackme_.00404064 //помещаем в ESI указатель на шифрованное имя пользователя (читай на валидный рег. ключ)
00401FF0 . BF 64484000 MOV EDI,crackme_.00404864 //помещаем в EDI указатель на рег. ключ, который мы ввели
00401FF5 . 8B07 MOV EAX,DWORD PTR DS:[EDI] //помещаем в EAX первые четыре байта рег. ключа, который мы ввели
00401FF7 . 8B1E MOV EBX,DWORD PTR DS:[ESI] //помещаем в EBX первые четыре байта валидного рег. ключа
00401FF9 . 3BC3 CMP EAX,EBX //сравниваем их
00401FFB 0F85 C4020000 JNZ crackme_.004022C5 //если они не равны, то прыгаем на 004022C5
00402001 . 83C6 04 ADD ESI,4
00402004 . 83C7 04 ADD EDI,4
00402007 . 8B1E MOV EBX,DWORD PTR DS:[ESI] //помещаем в EBX первые четыре байта валидного рег. ключа
00402009 . 8B07 MOV EAX,DWORD PTR DS:[EDI] //помещаем в EAX следующие четыре байта рег. ключа, который мы ввели
0040200B . 3BC3 CMP EAX,EBX //сравниваем их
0040200D 0F85 B2020000 JNZ crackme_.004022C5 //если они не равны то прыгаем на 004022C5
00402013 . E9 5C000000 JMP crackme_.00402074 //если они равны то прыгаем на "правильную" ветку программы
Вывод
Таким образом, получается, что весь алгоритм шифрования — это замена каждого второго символа имени на символ, следующий в таблице ASCII.
Плюс к тому, программа проверяет только первые 8 символов серийника, что, соостветственно, является недостатком.
Кейген
А вот и кейген:
var
s:string;
i:integer;
c:char;
begin
readln(s);
while i<= length(s) do begin
Inc(s[i]);
i:=i+2;
end;
writeln(s);
end.
Спасибо за внимание.
P.S. Извините за Паскаль, другого компилятора не нашёл.
P.P.S. И извините за простоту.