Часть 2. Исследование, взлом.
Инструменты, которые я использовал:
- дизассемблер IDA
- hex-редактор WinHex
Теперь перейдем к исследованию защиты
самой программы.
Запустим программу
и видим две неактивные кнопки: "Function 1"
и "Function 2", вероятно когда программа
будет зарегистрирована кнопки станут
активными и будут выполнять свои функции.
При этом никаких полей ввода пароля для
регистрации нам не предоставляются,
очевидно данные регистрации проверяются из
файла или реестра, значит наша задача самим
отследить, что и как проверяет программа.
Теперь запустим IDA или любой другой
дизассемблер и дизассемблируем нашу
программу, когда IDA закончит, перейдем на
точку входа нашей программы, она находится
по адресу 403F00.
00403F00 push ebp
00403F01 8B EC mov ebp, esp
00403F03 83 C4 F0 add esp, 0FFFFFFF0h
00403F06 B8 C8 3E 40 00 mov eax, offset dword_403EC8
00403F0B E8 5C F9 FF FF call sub_40386C
00403F10 A1 00 57 40 00 mov eax, ds:dword_405700
00403F15 A3 18 57 40 00 mov ds:hInstance, eax
00403F1A 6A 00 push 0 ; пеpедается
в паpаметpе lParam сообщения wm_InitDialog
00403F1C 68 C8 3D 40 00 push offset sub_403DC8 ; адpес
экземпляpа пpоцедуpы функции диалога
00403F21 6A 00 push 0 ; окно
владельца
00403F23 6A 64 push 64h ; имя
шаблона блока диалога (заканчивающееся
пустым символом)
00403F25 FF 35 18 57 40 00 push ds:hInstance ; экземпляр
модуля, исполнимый файл которого содержит
шаблон блока диалога
00403F2B E8 F0 F9 FF FF call DialogBoxParamA ; вызов
диалога (главной формы)
00403F30 E8 43 F3 FF FF call sub_403278
00403F35 8D 40 00 lea eax, [eax+0]
Теперь перейдем на адрес 00403DC8, здесь
происходит обработка событий диалога.
00403DC8 55 push ebp
00403DC9 8B EC mov ebp, esp
00403DCB 53 push ebx
00403DCC 33 DB xor ebx, ebx
00403DCE 8B 45 0C mov eax, [ebp+arg_4]
00403DD1 83 F8 1C cmp eax, 1Ch
00403DD4 7F 19 jg short loc_403DEF ; сообщения
windows: WM_INITDIALOG, WM_COMMAND
00403DD6 74 26 jz short loc_403DFE ; сообщения
windows: WM_ACTIVATE
00403DD8 83 E8 02 sub eax, 2
00403DDB 0F 84 A4 00 00 00 jz loc_403E85 ; сообщения
windows: WM_DESTROY
00403DE1 83 E8 0E sub eax, 0Eh
00403DE4 0F 84 9B 00 00 00 jz loc_403E85 ; сообщения
windows: WM_CLOSE
00403DEA E9 9B 00 00 00 jmp loc_403E8A
Сообщения WM_ACTIVATE - возможно именно там
находится проверка регистрационных данных,
смотрим по адресу 403DFE:
00403DFE E8 51 FD FF FF call sub_403B54 ; интересующий
нас вызов процедуры
00403E03 E9 82 00 00 00 jmp loc_403E8A ; возвращаемся
там идет вызов какой-то процедуры,
посмотрим что это за процедура, перейдем на
адрес 403B54:
В eax помещается имя файла "reg.k" (название
файла говорит само за себя). Теперь смело
можем предположить, что это и есть
процедура проверки регистрации.
Значит в eax помещается имя файла "reg.k" и
вызывается функция по адресу 00403B8A, зайдем в
нее:
Кратко опишу, что функция делает: она
пытается открыть переданный ей файл и
обрабатывает код ошибки, вследствие чего
устанавливает существует ли файл, если
существует - возвращает в al=1, в противном
случае al=0. Т.о. функция служит для проверки
существования файла. В нашем случае
проверяется наличие файла: "reg.k".
Возвращаемся к нашему основному коду и идем
дальше:
Рассмотрим, что здесь происходит.
Открывается файл "reg.k", считывается 50
байт в массив и проверяется первые три
байта. Теперь мы знаем первые три байта из
файла "reg.k": "reg". Идем дальше.
00403C14 BF 08 00 00 00 mov edi, 8 ; помещаем
в регистр edi, 8
00403C19 B8 28 57 40 00 mov eax, offset unk_405728 ;
в eax смещение массива, начиная с 5-го байта
00403C1E
00403C1E loc_403C1E: ; CODE XREF: sub_403B54+D3j
00403C1E 33 D2 xor edx, edx ; очищаем
edx
00403C20 8A 10 mov dl, [eax] ; в dl n-ый
байт из массива
00403C22 01 55 F8 add [ebp+var_8], edx ; [ebp+var_8]
накапливает сумму байт
00403C25 40 inc eax ; следующий
байт
00403C26 4F dec edi ; уменьшаем
edi, при edi=0, выход из цикла, иначе
00403C27 75 F5 jnz short loc_403C1E ; прыжок
обратно в цикл
00403C29 BF 08 00 00 00 mov edi, 8 ; помещаем
в регистр edi, 8
00403C2E B8 30 57 40 00 mov eax, offset unk_405730 ; в
eax смещение массива, начиная с 13-го байта
00403C33
00403C33 loc_403C33: ; CODE XREF: sub_403B54+E8j
00403C33 33 D2 xor edx, edx ; очищаем
edx
00403C35 8A 10 mov dl, [eax] ; в dl n-ый
байт из массива
00403C37 01 55 F4 add [ebp+var_C], edx ; [ebp+var_С]
накапливает сумму байт
00403C3A 40 inc eax ; следующий
байт
00403C3B 4F dec edi ; уменьшаем
edi, при edi=0, выход из цикла, иначе
00403C3C 75 F5 jnz short loc_403C33 ;
прыжок обратно в цикл
Здесь присутствуют 2 цикла. Первый цикл
складывает 8 байт из нашего массива, начиная
с 5-го по 12-ый байты и кладет в [ebp+var_8]. Второй
цикл складывает следующие 8 байт, т.е. с 13-го
по 20-ый байты и кладет в [ebp+var_C].
Здесь также присутствуют 2 цикла. Первый
цикл складывает 9 байт, начиная с 22-го по 30-ый
байты и кладет в [ebp+var_10], но также в цикле
происходит следующее: к каждому байту
прибавляется 11hex=17 dec и переводится в строку,
которая кладется по *[ebp+var_170], далее к этой
строке таким же образом прибавляется
следующий байт. Т.о. по адресу *[ebp+var_170]
формируется строка. Предположим это имя, на
которое будет зарегистрирована программа.
Второй цикл складывает следующие 9 байт, т.е.
с 31-го по 39-ый байты и кладет в [ebp+var_14] и
происходит то же самое что и в предыдущем
цикле: но теперь к каждому байту
прибавляется 15hex=21 dec и переводится в строку,
которая кладется по *[ebp+var_174], далее к этой
строке таким же образом прибавляется
следующий байт. Предположим, что это будет
компания.
Подведем итог:
В [ebp+var_8] - сумма с 5-го по 12-ый байт
В [ebp+var_C] - сумма с 13-го по 20-ый байт
В [ebp+var_10] - сумма с 22-го по 30-ый байт
В [ebp+var_14] - сумма с 31-го по 39-ый байт
00403CA8 8B 45 F8 mov eax, [ebp+var_8] ;
в eax кладется [ebp+var_8]
00403CAB 3B 45 F0 cmp eax, [ebp+var_10] ; и
сравнивается с [ebp+var_10]
00403CAE 75 3F jnz short loc_403CEF ; если
не равны выходит из процедуры и регистрации
нам не видать
00403CB0 8B 45 F4 mov eax, [ebp+var_C] ; в
eax кладется [ebp+var_C]
00403CB3 3B 45 EC cmp eax, [ebp+var_14] ; и
сравнивается с [ebp+var_14]
00403CB6 75 37 jnz short loc_403CEF ; если
не равны выходит из процедуры и регистрации
нам не видать
Первые три байта мы знаем: "reg". Теперь
можем вычислить еще 35 байт, с 5-го по 39-ый
байт.
Сумма с 5-го по 12-ый байт должна равняться
сумме с 22-го по 30-ый байт, а
сумма с 13-го по 20-ый байт должна равняться
сумма с 31-го по 39-ый байт.
Т.к. мы предположили, что с 22-го по 30-ый байт
имя, а с 31-го по 39-ый байт компания, начнем
вычислять исходя из этого.
Зарегистрируем на имя: ^cracker^ и компанию
^company^. Переведем строку "^cracker^" в hex-коды:
5E 63 72 61 63 6B 65 72 5E и отнимем от каждого байта 11
hex, получим: 4D 52 61 50 52 5A 54 61 4D. Тоже самое
сделаем для названия компании. Переведем в
hex-коды "^company^": 5E 63 6F 6D 70 61 6E 79 5E и отнимем
от каждого байта 15 hex, получим: 49 4E 5A 58 5B 4C 59 64
49
Т.о. получим с 22-го по 39-байт такими:
4D 52 61 50 52 54 61 4D 49 4E 5A 58 5B 4C 59 64 49
Из этого легко вычислим с 5-го по 20-ый байты,
зная что сумма с 22-го по 30-й: 4D 52 61 50 52 54 61 4D
должна равняться сумме с 5-го по 12-байт и
сумма с 31-го по 39-ый: 49 4E 5A 58 5B 4C 59 64 49 должна
равняться сумме с 13-но по 20-ый.
Складываем байты: 4D 52 61 50 52 5A 54 61 4D = 2FE hex = 766
dec
делим 766 на 8 байт = 95,7.
100 * 7 байт = 700
766 - 700 = 66
Простыми вычислениями нашли 8 байт с 5-го по
12-ый байты:
7 байт 100 dec=64 hex и байт 66 dec=42hex
64 64 64 64 64 64 64 42
Складываем байты компании: 49 4E 5A 58 5B 4C 59 64 49 =
2F6 hex = 758 dec
делим 758 на 8 байт = 94,7.
100 * 7 байт = 700
758 - 700 = 58
Нашли 8 байт с 13-го по 20-ый байты:
7 байт 100 dec=64 hex и байт 58 dec=3A hex
64 64 64 64 64 64 64 3A
Посмотрим, что происходит дальше в коде:
Больше проверок нет, а значит нам известны
все байты файла "reg.k", причем 4 и 21 байты
в проверке не участвуют, а следовательно
могут быть любыми. Создадим файл "reg.k" и
с помощью hex-редактора запишем байты:
72 65 67 00 49 4E 5A 58 5B 4C 59 64 49 64 64 64 64 64 64 64 3A 00 4D 52 61 50
52 5A 54 61 4D 49 4E 5A 58 5B 4C 59 64 49
Пробуем запустить программу. Появилась
надпись: "Registered to ^cracker^ / ^company^" и кнопки
стали активными.
На первый взгляд работа выполнена,
программа зарегистрирована, но не тут то
было. Пробуем нажать на кнопки "Function 1"
и "Function 2", никаких действий не
происходит. Поглядим, что происходит в коде
при нажатии на кнопки. Обработка событий
диалога:
00403DC8 55 push ebp
00403DC9 8B EC mov ebp, esp
00403DCB 53 push ebx
00403DCC 33 DB xor ebx, ebx
00403DCE 8B 45 0C mov eax, [ebp+arg_4]
00403DD1 83 F8 1C cmp eax, 1Ch
00403DD4 7F 19 jg short loc_403DEF ; сообщения
windows: WM_INITDIALOG, WM_COMMAND
00403DD6 74 26 jz short loc_403DFE ; сообщения
windows: WM_ACTIVATE
00403DD8 83 E8 02 sub eax, 2
00403DDB 0F 84 A4 00 00 00 jz loc_403E85 ; сообщения
windows: WM_DESTROY
00403DE1 83 E8 0E sub eax, 0Eh
00403DE4 0F 84 9B 00 00 00 jz loc_403E85 ; сообщения
windows: WM_CLOSE
00403DEA E9 9B 00 00 00 jmp loc_403E8A
Перейдем на адрес 403DEF:
00403DEF sub eax, 110h ;
00403DF4 jz short loc_403E26 ; WM_INITDIALOG
00403DF6 dec eax ;
00403DF7 jz short loc_403E08 ; WM_COMMAND
00403DF9 jmp loc_403E8A
Перейдем на адрес 403E08:
00403E08 loc_403E08: ; CODE XREF:
sub_403DC8+2Fj
00403E08 cmp [ebp+arg_8], 3E9h ; нажата ли
кнопка "Function 1"
00403E0F jnz short loc_403E16 ; если
нажата,
00403E11 call sub_403AC0 ; заходим в
этот call
00403E16
00403E16 loc_403E16: ; CODE XREF: sub_403DC8+47j
00403E16 cmp [ebp+arg_8], 3EAh ; нажата ли
кнопка "Function 2"
00403E1D jnz short loc_403E8A ; если
нажата,
00403E1F call sub_403B00 ; заходим в
этот call
00403E24 jmp short loc_403E8A ;
Перейдем на адрес 403AC0:
00403AC0 call sub_403A68 ; вызов
какой-то функции
00403AC5 test al, al ; проверка al,
что возвратила функция
00403AC7 jz short locret_403AE0 ; если
al=0 - происходит прыжок на выход из функции
00403AC9 push 0 ;
00403ACB push offset dword_403AE4 ;
00403AD0 push offset dword_403AEC ;
00403AD5 mov eax, ds:hDlg ;
00403ADA push eax ;
00403ADB call MessageBoxA_0 ;
Значит программа только дала понять, что
нормально зарегистрирована, а на самом деле
в каждой функции программы происходит еще
дополнительная проверка и только в случае
успешного ее прохождения запускает функции.
Смотрим, что это за проверка, переходим на
адрес 403A68:
00403A68 push ebp ;
00403A69 mov ebp, esp ;
00403A6B push ebx ;
00403A6C mov edx, offset byte_405724 ; помещается
в edx, адрес нашего массива начиная с 41-го
байта
00403A71 xor eax, eax ; очищается
eax
00403A73 push ebp ;
00403A74 push offset loc_403AB3 ;
00403A79 push dword ptr fs:[eax] ;
00403A7C mov fs:[eax], esp ;
00403A7F mov al, [edx+2Bh] ; в
регистр al помещается 44 байт
00403A82 mov ecx, eax ;
00403A84 xor cl, 13h ; 44 байт xor'ится
с 13hex и
00403A87 cmp cl, [edx+29h] ; сравнивается
с 42 байтом
00403A8A jnz short loc_403AA3 ;
00403A8C mov cl, [edx+2Ah] ; в
регистр cl помещается 43 байт
00403A8F xor cl, 1Fh ; xor'ится с 1F
hex и
00403A92 cmp cl, [edx+28h] ; сравнивается
с 41 байтом
00403A95 jnz short loc_403AA3 ;
00403A97 xor al, [edx+2Ah] ; 44 байт
xor'ится с 43 байтом и
00403A9A cmp al, [edx+28h] ; сравнивается
с 41 байтом
00403A9D jnz short loc_403AA3 ;
00403A9F mov bl, 1 ; если все
условия удовлетворены, в bl помещается 1
00403AA1 jmp short loc_403AA5 ;
А происходит здесь вот что. Берется 4 байта:
41, 42, 43, 44 и проверяется следующими условиями:
44 байт xor 13 = 42 байт
43 байт xor 1F = 41 байт
44 байт xor 43 байт = 41 байт
Начнем вычислять с 44-го байта:
44 байт = 1F
1F xor 13 = 0C (42 байт)
41 байт = 0C
0C xor 1F = 13 (43 байт)
Получили:
0C 0C 13 1F
Дописываем эти байты в наш регистрационный
файл.
Теперь файл будет выглядеть так:
72 65 67 00 49 4E 5A 58 5B 4C 59 64 49 64 64 64 64 64 64 64 3A 00 4D 52 61 50 52
5A 54 61 4D 49 4E 5A 58 5B 4C 59 64 49 00 0C 0C 13 1F
Сохраняем и запускаем нашу программу.
Проверим "Function 1" и "Function 2", нажмем
на них, появиться сообщение: "The function work!".
Теперь программу можно считать полностью
зарегистрированной.