
Скриншот программы CollageIt
Программа
CollageIt - очень удобная утилита для создания красивых коллажей из множества фотографий. Буквально несколько кликов мышкой и вы получаете готовый результат. Программа почему-то платная, и надо выложить за лицензию почти двадцатку баксов.
Скачиваем
дистрибутив, устанавливаем, запускаем. В принципе, все работает даже в бесплатном варианте, за исключением того, что на готовые коллажи наносится некрасивый маркер, и при сохранении нам каждый раз предлагают зарегистрироваться. Хорошо, но сперва попробуем ввести какой-нибудь левый серийник. На это программа отреагирует вот таким сообщением:

Сообщение о неправильной регистрации
Эта строчка находится в ресурсах, код ресурса 106 в десятичной или 6Ah в шестнадцатеричной системе счисления.

Нехорошая строка найдена
Исходные данные для исследования защиты есть, переходим к исполняемому файлу. Он ничем не упакован, можно сразу же отправлять его на дизассемблирвание. Когда дизассемблер закончит свою работу, поищем место, где программа обращается к ресурсам с кодом 6Ah. Первое же вхождение все ставит на свои места:
...
.text:0040AC33 mov ecx, [ecx]
.text:0040AC35 lea eax, [ebp+var_1C]
.text:0040AC38 push eax
; Вызвать функцию проверки
.text:0040AC39 call ds:?CheckCode@CRegisterUtility
; CRegisterUtility::CheckCode(ATL::CStringT)
.text:0040AC3F movzx eax, al
; Если она вернула AL=0, то регистрационный код неправильный
.text:0040AC42 test eax, eax
; Вывести сообщение о неправильной регистрации
.text:0040AC44 jz short loc_40AC94
.text:0040AC46 mov esi, ds:?GetInstance@RegConfig@@SAPAV1@XZ
; RegConfig::GetInstance(void)
.text:0040AC4C lea edx, [ebp+var_14]
.text:0040AC4F push edx
.text:0040AC50 call esi
; RegConfig::GetInstance(void) ; RegConfig::GetInstance(void)
.text:0040AC52 mov ecx, eax
.text:0040AC54 add ecx, 8
.text:0040AC57 call ds:mfc90u_811
.text:0040AC5D call esi
; RegConfig::GetInstance(void) ; RegConfig::GetInstance(void)
.text:0040AC5F mov ecx, eax
.text:0040AC61 call ds:?SaveData@RegConfig@@QAEXXZ
; RegConfig::SaveData(void)
.text:0040AC67 lea eax, [ebp+var_18]
; Загрузить сообщение об успешной регистрации
.text:0040AC6A push 69h
.text:0040AC6C push eax
.text:0040AC6D call sub_417570
.text:0040AC72 mov byte ptr [ebp+var_4], 2
.text:0040AC76 mov ecx, [ebp+var_18]
.text:0040AC79 push 0
.text:0040AC7B push ecx
.text:0040AC7C call ds:?BCGPMessageBox@@YAHPBGI@Z
; BCGPMessageBox(ushort const *,uint)
.text:0040AC82 mov ecx, [ebp+var_20]
.text:0040AC85 mov edx, [ecx]
.text:0040AC87 mov eax, [edx+160h]
.text:0040AC8D add esp, 10h
.text:0040AC90 call eax
.text:0040AC92 jmp short loc_40ACB1
.text:0040AC94 ; --------------------------------------------
.text:0040AC94 loc_40AC94:
.text:0040AC94 lea ecx, [ebp+var_18]
; Загрузить строку ресурсов с кодом 6Ah
.text:0040AC97 push 6Ah
.text:0040AC99 push ecx
.text:0040AC9A call sub_417570
.text:0040AC9F mov byte ptr [ebp+var_4], 3
.text:0040ACA3 mov edx, [eax]
.text:0040ACA5 push 0
.text:0040ACA7 push edx
; Вывести сообщение о неправильной регистрации
.text:0040ACA8 call ds:?BCGPMessageBox@@YAHPBGI@Z
; BCGPMessageBox(ushort const *,uint)
.text:0040ACAE add esp, 10h
...
Дизассемблер показал даже имя вызываемой функции, и это неспроста. Это имя содержится в секции импорта, значит сама функция проверки содержится в какой-то другой внешней библиотеке. В какой именно - тоже покажет дизассемблер.

Список импортируемых функций
В папке программы лежит файл AlbumDesignCore.dll, теперь загоняем в дизассемблер его, а основную программу запускаем под отладчиком. Затем в отлаживаемой программе надо поставить точку останова на начало функции CheckCode:
.text:10043690 ; public: bool __thiscall CRegisterUtility::CheckCode
.text:10043690 push ebp
.text:10043691 mov ebp, esp
.text:10043693 push ecx
.text:10043694 mov [ebp+var_4], ecx
.text:10043697 mov eax, [ebp+var_4]
.text:1004369A add eax, 8
.text:1004369D push eax
.text:1004369E mov ecx, [ebp+var_4]
.text:100436A1 mov edx, [ecx+4]
.text:100436A4 push edx
.text:100436A5 mov eax, [ebp+arg_0]
.text:100436A8 push eax
.text:100436A9 call ?CheckRandomSerialCode
.text:100436AE add esp, 0Ch
.text:100436B1 mov esp, ebp
.text:100436B3 pop ebp
.text:100436B4 retn 4
Как видите, основная проверка выполняется в функции CheckRandomSerialCode той же самой библиотеки. И вот тут начинается самое интересное, а именно алгоритм проверки правильности серийного номера.
...
.text:100431FC call ds:mfc90u_290
.text:10043202 mov [ebp+var_4], 0
.text:10043209 lea ecx, [ebp+var_14]
.text:1004320C call ds:mfc90u_3185
; Первая проверка - длина серийника должна быть 1Dh (29) символов
.text:10043212 cmp eax, 1Dh
; Переход, если длина соответствует
.text:10043215 jz short loc_10043233
.text:10043217 mov [ebp+var_2D], 0
.text:1004321B mov [ebp+var_4], 0FFFFFFFFh
.text:10043222 lea ecx, [ebp+var_14]
.text:10043225 call ds:mfc90u_600
.text:1004322B mov al, [ebp+var_2D]
.text:1004322E jmp loc_100433FF
.text:10043233 ; --------------------------------------------
.text:10043233 loc_10043233:
; Выполняется проверка формата серийного номера
.text:10043233 push 5
.text:10043235 lea ecx, [ebp+var_14]
.text:10043238 call ds:mfc90u_2676
.text:1004323E movzx eax, ax
; В позиции 05h должен быть символ '-'
.text:10043241 cmp eax, 2Dh
.text:10043244 jnz short loc_1004327F
.text:10043246 push 0Bh
.text:10043248 lea ecx, [ebp+var_14]
.text:1004324B call ds:mfc90u_2676
.text:10043251 movzx ecx, ax
; В позиции 0Bh должен быть символ '-'
.text:10043254 cmp ecx, 2Dh
.text:10043257 jnz short loc_1004327F
.text:10043259 push 11h
.text:1004325B lea ecx, [ebp+var_14]
.text:1004325E call ds:mfc90u_2676
.text:10043264 movzx edx, ax
; В позиции 11h должен быть символ '-'
.text:10043267 cmp edx, 2Dh
.text:1004326A jnz short loc_1004327F
.text:1004326C push 17h
.text:1004326E lea ecx, [ebp+var_14]
.text:10043271 call ds:mfc90u_2676
.text:10043277 movzx eax, ax
.text:1004327A cmp eax, 2Dh
; В позиции 17h должен быть символ '-'
.text:1004327D jz short loc_1004329B
.text:1004327F loc_1004327F:
.text:1004327F mov [ebp+var_2E], 0
.text:10043283 mov [ebp+var_4], 0FFFFFFFFh
.text:1004328A lea ecx, [ebp+var_14]
.text:1004328D call ds:mfc90u_600
.text:10043293 mov al, [ebp+var_2E]
.text:10043296 jmp loc_100433FF
...
После разбора первых двух проверок мы знаем длину и формат серийного номера: это должно быть что-то типа "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX". Смотрим дальше в режиме пошаговой трассировки.
...
.text:1004329B push 5
.text:1004329D push 0
.text:1004329F lea ecx, [ebp+var_34]
.text:100432A2 push ecx
.text:100432A3 lea ecx, [ebp+var_14]
; Отрезать от серийника первые 5 символов
.text:100432A6 call ds:mfc90u_4519
.text:100432AC mov [ebp+var_44], eax
.text:100432AF mov edx, [ebp+var_44]
.text:100432B2 mov [ebp+var_48], edx
.text:100432B5 mov byte ptr [ebp+var_4], 1
.text:100432B9 mov ecx, [ebp+var_48]
.text:100432BC call ds:mfc90u_909
.text:100432C2 push eax
.text:100432C3 lea ecx, [ebp+var_10]
.text:100432C6 call ds:mfc90u_306
.text:100432CC mov byte ptr [ebp+var_4], 3
.text:100432D0 lea ecx, [ebp+var_34]
.text:100432D3 call ds:mfc90u_600
.text:100432D9 mov eax, [ebp+arg_8]
.text:100432DC push eax
.text:100432DD lea ecx, [ebp+var_10]
.text:100432E0 push ecx
; Сравнить символы со строкой "CLGIT"
.text:100432E1 call sub_10043730
.text:100432E6 add esp, 8
.text:100432E9 movzx edx, al
.text:100432EC test edx, edx
.text:100432EE jz short loc_10043319
...
В этой проверке выполняется сравнение первых 5 символов серийного номера со строкой "CLGIT", если они не совпадают, то серийный номер неправильный. Трассируем дальше:
...
.text:10043322 loc_10043322:
.text:10043322 mov eax, [ebp+var_28]
.text:10043325 add eax, 1
.text:10043328 mov [ebp+var_28], eax
.text:1004332B loc_1004332B:
.text:1004332B cmp [ebp+var_28], 4
.text:1004332F jge short loc_1004339F
.text:10043331 push 5
.text:10043333 mov ecx, [ebp+var_28]
.text:10043336 imul ecx, 6
.text:10043339 add ecx, 6
.text:1004333C push ecx
.text:1004333D lea edx, [ebp+var_3C]
.text:10043340 push edx
.text:10043341 lea ecx, [ebp+var_14]
.text:10043344 call ds:mfc90u_4519
.text:1004334A mov [ebp+var_4C], eax
.text:1004334D mov eax, [ebp+var_4C]
.text:10043350 mov [ebp+var_50], eax
.text:10043353 mov byte ptr [ebp+var_4], 4
.text:10043357 mov ecx, [ebp+var_50]
.text:1004335A call ds:mfc90u_909
.text:10043360 push eax
.text:10043361 lea ecx, [ebp+var_2C]
.text:10043364 call ds:mfc90u_306
.text:1004336A mov byte ptr [ebp+var_4], 6
.text:1004336E lea ecx, [ebp+var_3C]
.text:10043371 call ds:mfc90u_600
.text:10043377 lea ecx, [ebp+var_2C]
.text:1004337A call ds:mfc90u_2697
.text:10043380 push eax ; Str
; Преобразовать строку в шестнадцатеричное число
.text:10043381 call ?HexToInt@@YAHPBD@Z
.text:10043386 add esp, 4
.text:10043389 mov ecx, [ebp+var_28]
.text:1004338C mov [ebp+ecx*4+var_24], eax
.text:10043390 mov byte ptr [ebp+var_4], 3
.text:10043394 lea ecx, [ebp+var_2C]
.text:10043397 call ds:mfc90u_601
.text:1004339D jmp short loc_10043322
...
В этом участке кода последовательно преобразуются оставшиеся 4 пятисимвольных блока цифр серийного номера в их шестнадцатеричные значения. Результат конвертирования записывается в стеке. И, наконец, последняя проверка:
; Взять первое число
.text:1004339F mov edx, [ebp+var_24]
; Добавить к нему третье число
.text:100433A2 add edx, [ebp+var_1C]
; Сумма должна быть равна 17CB6h
.text:100433A5 cmp edx, [ebp+arg_4]
; Если не равно, то серийный номер неправильный
.text:100433A8 jnz short loc_100433B5
; Взять второе число
.text:100433AA mov eax, [ebp+var_20]
; Добавить к нему четвертое число
.text:100433AD add eax, [ebp+var_18]
; Сумма должна быть равна 17CB6h
.text:100433B0 cmp eax, [ebp+arg_4]
; Если равно, то серийный номер правильный
.text:100433B3 jz short loc_100433DB
Вот и вся проверка. Подведем итог. Серийный номер состоит из 5 блоков по 5 символов, разделенных дефисом "-". Первый блок всегда равен "CLGIT", остальные четыре являются числами и состоят из шестнадцатеричных цифр. Сумма первого числа с третьим, а также второго с четвертым числом в сумме должны давать 17CB6h. Так, валидным серийным номером будет "CLGIT-17CB6-17CB6-00000-00000".

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