
Скриншот программы ScriptCryptor Compiler
Программа ScriptCryptor Compiler от Abyssmedia предназначена для компиляции скриптов VBScript и JavaScript в самостоятельные EXE-файлы, при этом их исходные тексты шифруются. Откомпилированные скрипты могут работать без вспомогательных системных файлов cscript.exe и wscript.exe, при выполнении не создается никаких временных файлов, все данные берутся из памяти. Об остальных функциях, если интересно, можете почитать на сайте. Ну а поскольку программа ScriptCryptor Compiler появилась в этом разделе блога, значит что-то с ней не так.
Качаем дистрибутив, устанавливаем, запускаем. Незарегистрированная версия компилятора создает EXE-файлы с напоминалкой, что они сделаны триальной версией. Не смертельно, конечно, но напрягает. Открываем окно регистрации, вводим левые данные. Здесь разработчики не наступили на традиционные грабли, программа молча принимает любое имя и серийник без всяких сообщений, даже куда-то их добросовестно сохраняет. Теперь посмотрим исполняемый файл. Он упакован UPX без всяких модификаций, распаковывается обычным "upx -d". Переходим к внутренностям файла. Единственная видимая зацепка для исследования - триальная надпись "UNREGISTERED COPY" в строке статуса программы, но она находится не в коде, а в ресурсах, причем не просто в ресурсах, а в формах Delphi. Можно потратить некоторое время, гоняя программу в DeDe, а можно наудачу предположить, что где-то есть строка типа "REGISTERED COPY" или типа того. И действительно, по паттерну "registered" находится что-то похожее:

Строка о зарегистрированности программы
На нее ссылается следующий код:
.text:00504963 mov edx, off_534538
.text:00504969 mov edx, [edx+4]
.text:0050496C mov eax, off_534538
.text:00504971 mov eax, [eax]
; Вызвать какую-то функцию проверки
.text:00504973 call sub_528688
; Если она вернула в AL не ноль, то программа считается зарегистрированной
.text:00504978 test al, al
.text:0050497A jz short loc_5049D3
.text:0050497C mov edx, off_534538
.text:00504982 mov edx, [edx]
.text:00504984 mov eax, [ebx+380h]
.text:0050498A call sub_45F530
.text:0050498F mov edx, offset aRegisteredVe_1
; Строка "Registered Version"
.text:00504994 mov eax, [ebx+388h]
.text:0050499A call sub_45F530
.text:0050499F xor edx, edx
.text:005049A1 mov eax, [ebx+370h]
.text:005049A7 call sub_45F420
.text:005049AC xor edx, edx
.text:005049AE mov eax, [ebx+360h]
.text:005049B4 mov ecx, [eax]
Теперь посмотрим процедуру проверки в дизассемблере и параллельно пройдем ее под отладчиком. Обратите внимание, что эта процедура вызывается только из одного места, и причем даже не при старте программы, а при открытии окна регистрации. Значит это не единственная проверка, и просто пропатчить ее нельзя. Я прокомментировал в коде все важные участки.
.text:00528688 push ebp
.text:00528689 mov ebp, esp
.text:0052868B xor ecx, ecx
.text:0052868D push ecx
.text:0052868E push ecx
.text:0052868F push ecx
.text:00528690 push ecx
.text:00528691 push ebx
.text:00528692 push esi
.text:00528693 push edi
.text:00528694 mov [ebp+var_4], edx
.text:00528697 mov eax, [ebp+var_4]
.text:0052869A call sub_40541C
.text:0052869F xor eax, eax
.text:005286A1 push ebp
.text:005286A2 push offset loc_528714
.text:005286A7 push dword ptr fs:[eax]
.text:005286AA mov fs:[eax], esp
.text:005286AD xor ebx, ebx
; Вот здесь ссылка на интересный блок данных. Он представляет собой большое
; количество шестнадцатеричных цифр.
.text:005286AF mov esi, offset aE9210182c802b9
; "e9210182c802b90244f6d8b9c2f56036ca39a17"...
.text:005286B4 lea eax, [ebp+var_8]
.text:005286B7 call sub_404F9C
.text:005286BC lea ecx, [ebp+var_C]
.text:005286BF mov edx, [ebp+var_4]
.text:005286C2 mov eax, 103h
; Если дошагать сюда под отладчиком, то видно, что берется введенный
; серийный номер, и из него вычисляется 64-битное число. Его видно в
; стеке. Анализаторы криптоалгоритмов показывают наличие в коде функции
; расчета MD5, и этот расчет выполняется в следующей функции.
.text:005286C7 call sub_481870
; Проверим нашу догадку, рассчитав MD5 от введенного "левого" серийника
; при помощи любой сторонней программы, строка будет в точности равна
; вычисленной в предыдущей функции. Значит для проверки берется хэш MD5
; от введенного серийного номера.
.text:005286CC xor edi, edi
.text:005286CE loc_5286CE:
.text:005286CE lea eax, [ebp+var_10]
.text:005286D1 mov edx, edi
.text:005286D3 add edx, edx
.text:005286D5 add edx, edx
.text:005286D7 lea edx, [esi+edx*8]
.text:005286DA mov ecx, 20h
; Взять от блока данных 32 символа
.text:005286DF call sub_40520C
.text:005286E4 mov eax, [ebp+var_10]
.text:005286E7 mov edx, [ebp+var_C]
; Сравнить блок с рассчитанным хэшем от серийного номера
.text:005286EA call sub_4053C4
; Если не совпадает, то взять сделующие 32 символа и т.д. до окончания
; блока данных
.text:005286EF jnz short loc_5286F3
; Найдено совпадение
.text:005286F1 mov bl, 1
.text:005286F3 loc_5286F3:
.text:005286F3 inc edi
.text:005286F4 cmp edi, 65h
; Проверено 65h блоков по 32 байта?
.text:005286F7 jnz short loc_5286CE
; Совпадений не найдено, EAX=0 и на выход
.text:005286F9 xor eax, eax
.text:005286FB pop edx
.text:005286FC pop ecx
.text:005286FD pop ecx
.text:005286FE mov fs:[eax], edx
.text:00528701 push offset loc_52871B
.text:00528706 loc_528706:
.text:00528706 lea eax, [ebp+var_10]
.text:00528709 mov edx, 4
.text:0052870E call sub_404FC0
.text:00528713 retn
.text:00528714 ; --------------------------------------------------
.text:00528714 loc_528714:
.text:00528714 jmp loc_404830
.text:00528719 ; --------------------------------------------------
.text:00528719 jmp short loc_528706
.text:0052871B ; --------------------------------------------------
.text:0052871B loc_52871B:
.text:0052871B mov eax, ebx
.text:0052871D pop edi
.text:0052871E pop esi
.text:0052871F pop ebx
.text:00528720 mov esp, ebp
.text:00528722 pop ebp
.text:00528723 retn
Из этого кода можно узнать следующее: сперва от введенного серийного номера вычисляется хэш MD5. В самой программе хранится несколько десятков заранее сгенерированных хэшей от правильных серийных номеров, и проверка регистрации сводится к поочередному сравнению рассчитанного хэша с готовыми. Найдено совпадение - программа зарегистрирована, если не найдено - работает в триальном режиме. Такая схема регистрации используется в некоторых других программах, и определенная логика тут тоже есть. Алгоритм MD5 необратим, и взламывается только прямым перебором, а при достаточной длине серийного номера его подбор даже на современных компьютерах займет недопустиимо длительное время. Обход защиты в нашем случае будет заключаться в том, что мы подменим первые 32 байта блока готовых хэшей на нужные нам данные. Придумаем какой-нибудь красивый серийник, например, "PERM-CRACK-LABORATORY-RULEZ", и посчитаем от него хэш MD5. Получится текстовая строка "7dd47442009f46f61db100b3e3b1a510". Заменим ей первые 32 символа блока:

Заменяем первый хэш в блоке
Сохраним изменения, запустим файл, и попытаемся его зарегистрировать с нашим красивым серийным номером. Получаем радостное сообщение "Registration Successful!", а текст в строке состояния меняется на "Registered Version". Что, собственно, нам и надо было получить.

Регистрация программы
При повторном запуске кнопка регистрации уже неактивна.

Программа успешно зарегистрирована
Для очистки совести создадим какой-нибудь простой скрипт, откомпилируем его и запустим. Никаких сообщений о триальности больше нет. По такой же схеме с заменой хэша обходится защита и других программ компании Abyssmedia.