Взлом CrackMe от Cruehead-а
CrackMe - это небольшая программа, разработанная для проверки навыков реверс-инженеринга программистов. © Википедия
В этой статье я опишу процесс взлома CrackMe #1 от Cruehead’а.
Описание программы
Скачать файл можно по ссылке. В архиве, помимо рассматриваемого в этой статье CrackMe, содержится еще несколько подобных примеров, о них я постараюсь написать позже.
Итак, при запуске мы видим основное окно программы:
Меню содержит 3 основных пункта:
File - Exit
- выйти из программыHelp - Register
- показать окно регистрации программыHelp - About
- показать окно информации о программе
При выборе пункта Help - Register
появляется окно ввода имени и серийного номера. Любому допустимому имени соответствует серийный номер. В случае ввода имени и серийника, соответствующих друг другу появляется окно:
В противном случае окно будет таким:
Итак, наша задача заключается в следующем: необходимо определить, каким образом проверяется соответствие введенных имени и пароля.
Логика работы программы
Открываем отладчик. Первое, что мы должны найти - адрес процедуры, рисующей окно “Good work!”. Судя по коду, это 0040134D
. Из этого делаем вывод, что вызов JMP
или CALL
на этот адрес приводит нас к успеху.
Теперь рассмотрим код, выполняющийся при нажатии кнопки OK
в меню Help - Register
:
00401228 | 68 8E214000 PUSH OFFSET Crackme1.0040218E
0040122D | E8 4C010000 CALL 0040137E
00401232 | 50 PUSH EAX
00401233 | 68 7E214000 PUSH OFFSET Crackme1.0040217E
00401238 | E8 9B010000 CALL 004013D8
0040123D | 83C4 04 ADD ESP,4
00401240 | 58 POP EAX
00401241 | 3BC3 CMP EAX,EBX
00401243 | 74 07 JE SHORT 0040124C
00401245 | E8 18010000 CALL 00401362
0040124A | EB 9A JMP SHORT 004011E6
0040124C | E8 FC000000 CALL 0040134D
00401251 | EB 93 JMP SHORT 004011E6
В этом коде нужно обратить внимание на инструкции 0040122D
и 00401238
. Первая получает на вход введенное имя и считает по нему некоторую характеристическую величину. Вторая считает характеристическую величину по серийному номеру. Алгоритмы подсчета разные, но результат должен получиться один. В зависимости эквивалентности этих величин осуществляется переход на вызов соответствующей функции, рисующей окно “Good luck” или “No luck”.
Рассмотрим процедуры 0040137E
(вычисление характеристической величины от имени) и 004013D8
(от серийника).
0040137E:
0040137E | 8B7424 04 MOV ESI,DWORD PTR SS:[ARG.1]
00401382 | 56 PUSH ESI
00401383 | 8A06 MOV AL,BYTE PTR DS:[ESI]
00401385 | 84C0 TEST AL,AL
00401387 | 74 13 JE SHORT 0040139C
00401389 | 3C 41 CMP AL,41
0040138B | 72 1F JB SHORT 004013AC
0040138D | 3C 5A CMP AL,5A
0040138F | 73 03 JNB SHORT 00401394
00401391 | 46 INC ESI
00401392 | EB EF JMP SHORT 00401383
00401394 | E8 39000000 CALL 004013D2
00401399 | 46 INC ESI
0040139A | EB E7 JMP SHORT 00401383
0040139C | 5E POP ESI
0040139D | E8 20000000 CALL 004013C2
004013A2 | 81F7 78560000 XOR EDI,00005678
004013A8 | 8BC7 MOV EAX,EDI
004013AA | EB 15 JMP SHORT 004013C1
004013AC | 5E POP ESI
004013AD | 6A 30 PUSH 30
004013AF | 68 60214000 PUSH OFFSET Crackme1.00402160
004013B4 | 68 69214000 PUSH OFFSET Crackme1.00402169
004013B9 | FF75 08 PUSH DWORD PTR SS:[EBP+8]
004013BC | E8 79000000 CALL
004013C1 | C3 RETN
Эта процедура делает следующее:
Если код какого-либо символа меньше, чем код символа ‘A’, то программа выводит окно “No luck” (даже не проверяя при этом серийный номер).
Если код символа больше или равен коду символа ‘Z’, то из кода символа вычитается 20h.
Находится сумма кодов всех символов, к которой применяется операция XOR с числом 5678h. Результат помещается в EAX.
004013D8:
004013D8 | 33C0 XOR EAX,EAX
004013DA | 33FF XOR EDI,EDI
004013DC | 33DB XOR EBX,EBX
004013DE | 8B7424 04 MOV ESI,DWORD PTR SS:[ARG.1]
004013E2 | B0 0A MOV AL,0A
004013E4 | 8A1E MOV BL,BYTE PTR DS:[ESI]
004013E6 | 84DB TEST BL,BL
004013E8 | 74 0B JE SHORT 004013F5
004013EA | 80EB 30 SUB BL,30
004013ED | 0FAFF8 IMUL EDI,EAX
004013F0 | 03FB ADD EDI,EBX
004013F2 | 46 INC ESI
004013F3 | EB ED JMP SHORT 004013E2
004013F5 | 81F7 34120000 XOR EDI,00001234
004013FB | 8BDF MOV EBX,EDI
004013FD | C3 RETN
Эта процедура реализует схему горнера для перевода десятичного числа, сохраненного в виде строки в шестнадцатеричное, выполняет операцию XOR с числом 1234h
и помещает ответ в регистр EBX
.
Пишем KeyGen
Итак, зная как работает программа попробуем разработать программу, которая по введенному имени выдаст серийный номер, который подходит для “разблокировки” нашего CrackMe.
Пока упустим некоторые формальности вроде ограничений на введенное имя и сосредоточимся на процессе генерации ключа.
Пусть у нас есть 2 функции: f(s)
и g(s)
, которые считают характеристические величины для строки s соответственно, по алгоритмам приведенным выше, но без применения операции XOR. Следовательно для успешного взлома необходимо, чтобы выполнялось условие
f(name) XOR 5678h = g(key) XOR 1234h
выражаем g(key)
:
g(key) = f(name) XOR 5678h XOR 1234h
т.к. функция g(s)
- функция перевода десятичного числа s
, записанного в виде строки в шестнадцатеричное, то можно ввести обратную функцию g'(x)
, результатом которой будет являться строка, содержащая десятичную запись шестнадцатеричного числа x
. Тогда
key = g'(f(name) XOR 5678h XOR 1234h)
это и будет результатом работы программы.
Проверим.
Пусть
name = "AA".
Тогда
f(name) = 82h
f(name) XOR 5678h XOR 1234h = 44CEh
g'(f(name) XOR 5678h XOR 1234h) = “17614”
Попробовав ввести в окно регистрации имя AA
и пароль 17614
увидим окно “Good luck”, поздравления и предложение попробовать взломать следующий CrackMe.
Код моего KeyGen-а: http://pastebin.com/nMjTa6Ek
Если есть пожелания или вопросы - милости прошу в комментарии :)