Взлом CrackMe от Cruehead-а

CrackMe - это небольшая программа, разработанная для проверки навыков реверс-инженеринга программистов. © Википедия

В этой статье я опишу процесс взлома CrackMe #1 от Cruehead’а.

Описание программы

Скачать файл можно по ссылке. В архиве, помимо рассматриваемого в этой статье CrackMe, содержится еще несколько подобных примеров, о них я постараюсь написать позже.

Итак, при запуске мы видим основное окно программы:

image1

Меню содержит 3 основных пункта:

  • File - Exit - выйти из программы
  • Help - Register - показать окно регистрации программы
  • Help - About - показать окно информации о программе

При выборе пункта Help - Register появляется окно ввода имени и серийного номера. Любому допустимому имени соответствует серийный номер. В случае ввода имени и серийника, соответствующих друг другу появляется окно:

image2

В противном случае окно будет таким:

image3

Итак, наша задача заключается в следующем: необходимо определить, каким образом проверяется соответствие введенных имени и пароля.

Логика работы программы

Открываем отладчик. Первое, что мы должны найти - адрес процедуры, рисующей окно “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

Если есть пожелания или вопросы - милости прошу в комментарии :)