
Скриншот программы 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" даже не пришлось нажимать, сообщение об успешной регистрации появляется сразу же после ввода правильных данных.

Программа успешно зарегистрирована
Вот такой пример несложной защиты, но с интересной реализацией проверки введенных данных. Рабочий кейген напишете самостоятельно, никаких трудностей возникнуть не должно.