
Скриншот программы PDF Password Remover
PDF Password Remover от компании VeryPDF Inc - очень нужная и полезная в компьютерном хозяйстве программа. Она позволяет снимать с PDF-файлов защиту паролем и различные ограничения, например, запрет копирования текста или печати, которые особо одаренные аффтары устанавливают для защиты своих "аффтарских прав". И все бы хорошо, но PDF Password Remover за свои услуги требует денег, а в пробном режиме запускается всего 50 раз.
Скачиваем с сайта дистрибутив (последняя версия на момент публикации 3.1), запускаем, устанавливаем. Предварительный осмотр пациента указывает, что исполняемый файл упакован обычным UPX, но после упаковки в нем были затерты названия секций и служебные данные упаковщика, так что простым "upx -d" тут дело не обойдется. Можно распаковать вручную, или же воспользоваться каким-нибудь универсальным или специализированным распаковщиком, выбор оружия оставляю за вами. Готовый распакованный файл загоняем в дизассемблер, параллельно запустим его, чтобы посмотреть на внешние проявления защиты.

Окно ввода регистрационных данных
Вах, "Machine ID"! Как все серьезно! :) По идее, такой способ регистрации используется, чтобы привязывать каждую копию программы к определенной уникальной машине. Расстраиваться не будем, посмотрим что нам скажет программа на ввод какого-нибудь левака. "Your registration key is wrong, please double check it and try again." Ну да, вполне ожидаемо. Теперь поищем нехорошую строку в распакованном файле.

"Нехорошая" строка найдена
И посмотрим код, который на нее ссылается. Тут все просто и понятно, никаких сюрпризов нет.
seg000:00405375 mov edi, 4B94F0h
seg000:0040537A push 4B94F0h
seg000:0040537F push 3FBh
seg000:00405384 push esi
seg000:00405385 rep stosd
seg000:00405387 call ds:dword_4883FC
seg000:0040538D push 4B94F0h
; Вызвать процедуру проверки введенных регистрационных данных
seg000:00405392 call sub_404BF0 ; <---- Проверка
seg000:00405397 add esp, 4
; Если она вернула EAX=0, то введенный код неправильный
seg000:0040539A test eax, eax
seg000:0040539C jz short loc_4053E2
seg000:0040539E push 40h
; Сообщение об успешной регистрации "Thank you."
seg000:004053A0 push offset aThankYou_
; "Thanks for purchasing the PDF Password "...
seg000:004053A5 push offset aThanksForPurch
seg000:004053AA push esi
seg000:004053AB call ds:dword_488404
seg000:004053B1 push ecx
seg000:004053B2 mov ecx, esp
seg000:004053B4 mov [esp+0E8h+var_D8], esp
seg000:004053B8 push (offset byte_4B9418+0D8h) ; Src
seg000:004053BD call sub_47BC59
seg000:004053C2 call sub_404FC0
seg000:004053C7 add esp, 4
seg000:004053CA mov dword ptr ds:byte_4B9418+1A0h, 1
seg000:004053D4 push 1
seg000:004053D6 push esi
seg000:004053D7 call ds:dword_488400
seg000:004053DD jmp loc_405665
seg000:004053E2 ; -------------------------------------------------------
seg000:004053E2 loc_4053E2:
seg000:004053E2 push 10h
seg000:004053E4 push 0
; "Your registration key is wrong, please "...
seg000:004053E6 push offset aYourRegistrati
seg000:004053EB push esi
seg000:004053EC call ds:dword_488404
seg000:004053F2 push 3FBh
seg000:004053F7 push esi
Теперь есть два пути. Первый - просто пропатчить процедуру проверки ключа, чтобы она всегда возвращала EAX=1, или же попробовать разобрать алгоритм проверки. Плюсы есть в каждом способе. Пропатченная версия становится портабельной и замечательно работает с любых съемных носителей. Зарегистрированная версия хорошо ставится на стационарную машину и не требует никаких вмешательств в исполняемый код. Посмотрим саму процедуру проверки одновременно под отладчиком и в дизассемблере. Сразу бросается в глаза, что ей передается только введенный код, а грозный "Machine ID" вообще нигде не используется. Получается, что привязка к железу - простая пустышка для создания видимости навороченной защиты.
; Если будете патчить, то это делается здесь
seg000:00404BF0 sub esp, 18h ; <-- MOV AL,1; RET
seg000:00404BF3 or ecx, 0FFFFFFFFh
seg000:00404BF6 xor eax, eax
seg000:00404BF8 push ebx
seg000:00404BF9 push esi
seg000:00404BFA mov esi, [esp+20h+arg_0]
seg000:00404BFE push edi
seg000:00404BFF mov edi, esi
seg000:00404C01 repne scasb
; Собственная функция для подсчета длины строки
seg000:00404C03 not ecx
seg000:00404C05 dec ecx
; Длина введенного серийника должны быть 14h (десятичное 20) символов
seg000:00404C06 cmp ecx, 14h
seg000:00404C09 jz short loc_404C12
; Иначе EAX=0 и на выход
seg000:00404C0B pop edi
seg000:00404C0C pop esi
seg000:00404C0D pop ebx
seg000:00404C0E add esp, 18h
seg000:00404C11 retn
Теперь мы знаем длину правильного серийного номера - ровно 20 символов.
seg000:00404C12 loc_404C12:
; Взять первый и второй символ
seg000:00404C12 mov al, [esi]
seg000:00404C14 mov cl, [esi+1]
seg000:00404C17 lea edx, [esp+24h+Str]
seg000:00404C1B xor bl, bl
seg000:00404C1D push edx ; Str
seg000:00404C1E mov [esp+28h+var_C], al
seg000:00404C22 mov [esp+28h+var_B], bl
seg000:00404C26 mov [esp+28h+Str], cl
seg000:00404C2A mov [esp+28h+var_17], bl
; Перевести второй символ в цифру
seg000:00404C2E call _atoi
; Записать его в EDI
seg000:00404C33 mov edi, eax
seg000:00404C35 lea eax, [esp+28h+var_C]
seg000:00404C39 push eax ; Str
; Перевести первый символ в цифру
seg000:00404C3A call _atoi
; Прибавить ко второму
seg000:00404C3F add edi, eax
seg000:00404C41 add esp, 8
; Результат = 10?
seg000:00404C44 cmp edi, 0Ah
; Если да, то продолжаем
seg000:00404C47 jz short loc_404C52
; Иначе EAX=0 и на выход
seg000:00404C49 pop edi
seg000:00404C4A pop esi
seg000:00404C4B xor eax, eax
seg000:00404C4D pop ebx
seg000:00404C4E add esp, 18h
seg000:00404C51 retn
Первая проверка: берутся первый и второй символ серийника, затем преобразуются в цифры и складываются. В сумме должно получиться 10.
seg000:00404C52 loc_404C52:
; Взять последний и предпоследний символы
seg000:00404C52 mov cl, [esi+12h]
seg000:00404C55 mov dl, [esi+13h]
seg000:00404C58 lea eax, [esp+24h+Str]
seg000:00404C5C mov [esp+24h+var_C], cl
seg000:00404C60 push eax ; Str
seg000:00404C61 mov [esp+28h+var_B], bl
seg000:00404C65 mov [esp+28h+Str], dl
seg000:00404C69 mov [esp+28h+var_17], bl
; Преобразовать последний символ в цифру
seg000:00404C6D call _atoi
seg000:00404C72 lea ecx, [esp+28h+var_C]
seg000:00404C76 mov edi, eax
; Преобразовать предпоследний символ в цифру
seg000:00404C78 push ecx ; Str
seg000:00404C79 call _atoi
seg000:00404C7E add edi, eax
seg000:00404C80 add esp, 8
; Сумма цифр = 12?
seg000:00404C83 cmp edi, 0Ch
; Если да, то продолжаем
seg000:00404C86 jz short loc_404C91
; Иначе EAX=0 и на выход
seg000:00404C88 pop edi
seg000:00404C89 pop esi
seg000:00404C8A xor eax, eax
seg000:00404C8C pop ebx
seg000:00404C8D add esp, 18h
seg000:00404C90 retn
Вторая проверка: последний и предпоследний символ переводятся в числовое значение, их сумма должна быть равна 12.
seg000:00404C91 loc_404C91:
; Взять 6-й и 14-й символ (здесь нумерация символов идет с 1)
seg000:00404C91 mov dl, [esi+5]
seg000:00404C94 mov al, [esi+0Dh]
seg000:00404C97 lea ecx, [esp+24h+Str]
seg000:00404C9B mov [esp+24h+var_C], dl
seg000:00404C9F push ecx ; Str
seg000:00404CA0 mov [esp+28h+var_B], bl
seg000:00404CA4 mov [esp+28h+Str], al
seg000:00404CA8 mov [esp+28h+var_17], bl
seg000:00404CAC call _atoi
seg000:00404CB1 lea edx, [esp+28h+var_C]
seg000:00404CB5 mov edi, eax
seg000:00404CB7 push edx ; Str
seg000:00404CB8 call _atoi
seg000:00404CBD add edi, eax
seg000:00404CBF add esp, 8
; Их сумма должна быть равна 11
seg000:00404CC2 cmp edi, 0Bh
; Если да, то продолжаем
seg000:00404CC5 jz short loc_404CD0
seg000:00404CC7 pop edi
seg000:00404CC8 pop esi
seg000:00404CC9 xor eax, eax
seg000:00404CCB pop ebx
seg000:00404CCC add esp, 18h
seg000:00404CCF retn
Третья проверка: 6-й и 14-й символ в сумме должны давать 11.
seg000:00404CD0 loc_404CD0:
; 13-й символ должен быть = 'K'
seg000:00404CD0 cmp byte ptr [esi+0Ch], 4Bh
seg000:00404CD4 jz short loc_404CDF
seg000:00404CD6 pop edi
seg000:00404CD7 pop esi
seg000:00404CD8 xor eax, eax
seg000:00404CDA pop ebx
seg000:00404CDB add esp, 18h
seg000:00404CDE retn
seg000:00404C12 ; ------------------------------------------------------
seg000:00404CDF loc_404CDF:
; 15-й символ должен быть = '3'
seg000:00404CDF cmp byte ptr [esi+0Eh], 33h
seg000:00404CE3 jz short loc_404CEE
seg000:00404CE5 pop edi
seg000:00404CE6 pop esi
seg000:00404CE7 xor eax, eax
seg000:00404CE9 pop ebx
seg000:00404CEA add esp, 18h
seg000:00404CED retn
seg000:00404C12 ; ------------------------------------------------------
seg000:00404CEE loc_404CEE:
; 16-й символ должен быть = '1'
seg000:00404CEE mov cl, [esi+0Fh]
seg000:00404CF1 xor eax, eax
seg000:00404CF3 cmp cl, 31h
seg000:00404CF6 pop edi
seg000:00404CF7 pop esi
seg000:00404CF8 pop ebx
seg000:00404CF9 setz al
seg000:00404CFC add esp, 18h
seg000:00404CFF retn
Теперь у нас есть алгоритм генерации правильного серийного номера. Первый и второй символы в сумме должны давать 10, последний и предпоследний - 12, 6-й и 14-й символ в сумме 11, 13-й символ должен быть 'K', 15-й и 16-й символы, соответственно, '3' и '1'. Остальные символы серийного номера вообще нигде не учитываются и могут заполняться как вам нравится. Готовый серийник может быть, например, таким: 55PCL5RULEZ!K631MH66.

Программа успешно зарегистрирована
Программа его с радостью примет и поблагодарит за покупку. Ну а рабочий кейген теперь вы можете написать самостоятельно.