Исследование защиты программы Fine Metronome


Скриншот программы Fine Metronome

Программа Fine Metronome представляет собой компьютерный вариант метронома. Обычным пользователям она вряд ли пригодится, а вот музыкантам может оказаться полезной. Программа триальная, имеет пробный период в 7 дней без ограничений функционала. А чтобы не отвлекать творческих людей от создания музыки, мы сейчас поможем программе стать бесплатной.

Скачиваем дистрибутив, устанавливаем, запускам. Внешне никаких признаков и ограничений действительно не видно, только в окне "О программе" тикает счетчик оставшихся бесплатных дней ("Осталось семь днее-ее-ее-й!"). Исполняемый файл ничем не упакован, отправим пока его в дизассемблер, а сами поищем где и как оно вообще регистрируется. Окно регистрации обнаружилось за кнопкой "Menu" под пунктом "Register..."


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

А вот на ввод всяких левых данных программа никак не реагирует, только молча закрывает окно ввода серийника. Грамотный ход со стороны разработчиков, ничего не скажешь. Придется искать что-нибудь, похожее на строку "registered", "unregistered", "licensed", "trial" и т.п. Долго искать не пришлось, со второй попытки в файле обнаружилась юникодная строка сообщения с благодарностью за регистрацию:


Строка сообщения об успешной регистрации

Смотрим в дизассемблере откуда чего берется. Код чистенький, без сюрпризов, все понятно с первого взгляда:
...
.text:005AF17B                 call    sub_407530
.text:005AF180                 mov     eax, [ebp+var_C]
.text:005AF183                 pop     edx
; Вызвать функцию проверки
.text:005AF184                 call    sub_567F0C
.text:005AF189                 test    al, al
; Если в регистре AL вернулся 0, то серийный номер неправильный
.text:005AF18B                 jz      loc_5AF2A5
.text:005AF191                 xor     edx, edx
.text:005AF193                 mov     eax, [ebx+3A4h]
.text:005AF199                 mov     ecx, [eax]
...
; Часть кода пропущена. В нем регистрационные данные сохраняются в реестр и т.п.
...
.text:005AF282                 call    sub_488E20
.text:005AF287                 mov     eax, off_5CB6B0
.text:005AF28C                 mov     eax, [eax]
.text:005AF28E                 mov     eax, [eax+43Ch]
.text:005AF294                 xor     edx, edx
.text:005AF296                 call    sub_4CE480
; Вывести сообщение об успешной регистрации
.text:005AF29B                 mov     eax, offset aSuccessfullyRe
; "Successfully registered! Thank you."
.text:005AF2A0                 call    sub_4C6D80
.text:005AF2A5 loc_5AF2A5:
; Закрыть окно регистрации
.text:005AF2A5                 xor     eax, eax
.text:005AF2A7                 pop     edx
...

Простой путь - пропатчить начало функции проверки серийного номера по адресу 00567F0C, чтобы она всегда возвращала AL=1. Более сложный путь - разобрать алгоритм проверки серийного номера. На этот раз загружаем программу в отладчик, введем какой-нибудь левый серийник и перед вводом последнего символа поставим точку останова на начало функции проверки регистрационных данных. Почему нельзя ставить точку останова сразу? Программа вызывает эту функцию проверки после ввода каждого символа в поле имени и серийного номера, соответственно, каждый раз будет срабатывать наш breakpoint. Оно вам надо? :)
.text:00567F0C                 push    ebp
.text:00567F0D                 mov     ebp, esp
.text:00567F0F                 mov     ecx, 4
.text:00567F14 loc_567F14:
.text:00567F14                 push    0
.text:00567F16                 push    0
.text:00567F18                 dec     ecx
.text:00567F19                 jnz     short loc_567F14
.text:00567F1B                 push    ecx
.text:00567F1C                 push    esi
.text:00567F1D                 mov     [ebp+var_8], edx
.text:00567F20                 mov     [ebp+var_4], eax
.text:00567F23                 lea     eax, [ebp+var_4]
.text:00567F26                 call    sub_40727C
.text:00567F2B                 lea     eax, [ebp+var_8]
.text:00567F2E                 call    sub_40727C
.text:00567F33                 xor     eax, eax
.text:00567F35                 push    ebp
.text:00567F36                 push    offset loc_56801A
.text:00567F3B                 push    dword ptr fs:[eax]
.text:00567F3E                 mov     fs:[eax], esp
.text:00567F41                 mov     [ebp+var_9], 0
.text:00567F45                 lea     eax, [ebp+var_18]
.text:00567F48                 mov     edx, [ebp+var_4]
.text:00567F4B                 call    sub_40751C
.text:00567F50                 mov     eax, [ebp+var_18]
.text:00567F53                 lea     edx, [ebp+var_14]
.text:00567F56                 call    sub_413A68
.text:00567F5B                 mov     edx, [ebp+var_14]
.text:00567F5E                 lea     eax, [ebp+var_4]
.text:00567F61                 call    sub_407530
.text:00567F66                 lea     eax, [ebp+var_20]
.text:00567F69                 mov     edx, [ebp+var_8]
.text:00567F6C                 call    sub_40751C
.text:00567F71                 mov     eax, [ebp+var_20]
.text:00567F74                 lea     edx, [ebp+var_1C]
.text:00567F77                 call    sub_413A68
.text:00567F7C                 mov     edx, [ebp+var_1C]
.text:00567F7F                 lea     eax, [ebp+var_8]
.text:00567F82                 call    sub_407530
.text:00567F87                 mov     eax, [ebp+var_4]
.text:00567F8A                 xor     edx, edx
.text:00567F8C                 call    sub_407110
.text:00567F91                 jz      short loc_567FF2
.text:00567F93                 mov     eax, [ebp+var_8]
.text:00567F96                 xor     edx, edx
.text:00567F98                 call    sub_407110
.text:00567F9D                 jz      short loc_567FF2
.text:00567F9F                 lea     eax, [ebp+var_24]
.text:00567FA2                 mov     edx, [ebp+var_8]
.text:00567FA5                 call    sub_40751C
; Перевести серийный номер из строки в число
.text:00567FAA                 mov     eax, [ebp+var_24]
.text:00567FAD                 lea     edx, [ebp+var_10]
.text:00567FB0                 call    sub_4144B8
; Серийный номер был числовой?
.text:00567FB5                 test    al, al
.text:00567FB7                 jz      short loc_567FF2
; Начинается функция подсчета правильного серийника для введенного имени
.text:00567FB9                 xor     esi, esi
.text:00567FBB                 mov     eax, [ebp+var_4]
.text:00567FBE                 test    eax, eax
.text:00567FC0                 jz      short loc_567FC9
; Получить длину строки регистрационного имени с учетом юникода
.text:00567FC2                 sub     eax, 4
.text:00567FC5                 mov     eax, [eax]
.text:00567FC7                 shr     eax, 1
.text:00567FC9 loc_567FC9:
.text:00567FC9                 test    eax, eax
.text:00567FCB                 jle     short loc_567FE3
; Счетчик символов - с первой позиции
.text:00567FCD                 mov     ecx, 1
.text:00567FD2 loc_567FD2:
; Указатель на строку регистрационного имени
.text:00567FD2                 mov     edx, [ebp+var_4]
; Получить следующий байт
.text:00567FD5                 movzx   edx, byte ptr [edx+ecx*2-2]
; Умножить его на 0Bh (11 десятичное)
.text:00567FDA                 imul    edx, 0Bh
; Добавить к счетчику
.text:00567FDD                 add     esi, edx
; Передвинуть указатель на следующий символ строки
.text:00567FDF                 inc     ecx
.text:00567FE0                 dec     eax
; Все символы обработали?
.text:00567FE1                 jnz     short loc_567FD2
.text:00567FE3 loc_567FE3:
; Умножить счетчик на 409h
.text:00567FE3                 imul    eax, esi, 409h
; Результат равен введенному серийнику?
.text:00567FE9                 cmp     eax, [ebp+var_10]
.text:00567FEC                 jnz     short loc_567FF2
; Да, записать в переменную флажок "зарегистрировано"
.text:00567FEE                 mov     [ebp+var_9], 1
.text:00567FF2 loc_567FF2:
.text:00567FF2                 xor     eax, eax
.text:00567FF4                 pop     edx
.text:00567FF5                 pop     ecx
.text:00567FF6                 pop     ecx
.text:00567FF7                 mov     fs:[eax], edx
.text:00567FFA                 push    offset loc_568021
.text:00567FFF loc_567FFF:
.text:00567FFF                 lea     eax, [ebp+var_24]
.text:00568002                 mov     edx, 5
.text:00568007                 call    sub_4072AC
.text:0056800C                 lea     eax, [ebp+var_8]
.text:0056800F                 mov     edx, 2
.text:00568014                 call    sub_406E08
.text:00568019                 retn

Теперь вы можете пройти функцию в пошаговом режиме под отладчиком, чтобы уяснить для себя все ключевые моменты. Я сразу прокомментировал код для удобства восприятия.

Что у нас в итоге получается? Введенный регистрационный код переводится в численное представление, затем на основании регистрационного имени рассчитывается правильный серийник, после чего эти два значения сравниваются. Алгоритм расчета следующий: ASCII-код каждого символа регистрационного имени умножается на 0Bh, значения суммируются. Результат сложения умножается на число 409h, а полученное значение в десятичной системе счисления и будет искомым серийный номером. Так, например, рабочей парой будет регистрационное имя "ManHunter / PCL" и код 14181024. Кнопку "Ok" даже не пришлось нажимать, сообщение об успешной регистрации появляется сразу же после ввода правильных данных.


Программа успешно зарегистрирована

Вот такой пример несложной защиты, но с интересной реализацией проверки введенных данных. Рабочий кейген напишете самостоятельно, никаких трудностей возникнуть не должно.
Теги:
защита, взлом
Добавлено: 18 Сентября 2013 03:10:24 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...