Исследование защиты программы STOIK Panorama Maker


Скриншот программы STOIK Panorama Maker

STOIK Panorama Maker - очень удобная программа для создания панорамных снимков из нескольких отдельных кадров, причем можно делать как горизонтальные, так и вертикальные панорамы. Все операции выполняются буквально за несколько кликов мышкой, а результат получается потрясающий. Более простой и удобной программы для создания панорам я пока не встречал. А любой хороший софт просто обязан быть (или стать) бесплатным.

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


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

Аффтары пишут, что программа в течение 15-дневного триального срока полнофункциональная. На самом деле это не так. В бесплатном режиме результат невозможно сохранить, и, как вы уже догадались, сейчас мы будем обходить все эти ограничения. На ввод неправильных регистрационных данных программа реагирует сообщением "Wrong activation key!", поищем его в исполняемом файле.


Строка сообщения в ресурсах

Строка сообщения в ресурсах файла имеет индекс 346 или 15Ah в шестнадцатеричной системе счисления. С похожими ситуациями мы уже неоднократно сталкивались, поэтому как и в прошлые разы поищем это значение в листинге дизассемблера.
.text:004AD136                 mov     ecx, esp
.text:004AD138                 mov     [ebp+108h+var_120], esp
.text:004AD13B                 push    edx
.text:004AD13C                 call    ds:mfc90_300
.text:004AD142                 lea     eax, [ebp+108h+var_120]
.text:004AD145                 push    eax
; Вызвать функцию проверки
.text:004AD146                 call    loc_4A1D80
.text:004AD14B                 add     esp, 8
; По умолчанию будем считать, что программа не зарегистрирована (BL=1)
.text:004AD14E                 mov     ebx, 1
.text:004AD153                 push    offset aOk      ; "Ok"
.text:004AD158                 mov     ecx, eax
.text:004AD15A                 mov     byte ptr [ebp+108h+var_10C], bl
.text:004AD15D                 mov     [ebp+108h+var_134], ebx
; Сравнить результат функции проверки со строкой 'Ok'
.text:004AD160                 call    ds:mfc90_1603
.text:004AD166                 test    eax, eax
.text:004AD168                 jnz     short loc_4AD188
; Если совпадает, то программа действительно зарегистрирована
.text:004AD16A                 push    ecx
.text:004AD16B                 lea     edx, [ebp+108h+var_11C]
.text:004AD16E                 mov     ecx, esp
.text:004AD170                 mov     [ebp+108h+var_12C], esp
.text:004AD173                 push    edx
.text:004AD174                 call    ds:mfc90_300
.text:004AD17A                 call    sub_4ACDC0
.text:004AD17F                 add     esp, 4
.text:004AD182                 cmp     eax, esi
.text:004AD184                 jnz     short loc_4AD188
; После успешного прохождения всех проверок обнулить регистр BL
.text:004AD186                 xor     bl, bl
.text:004AD188 loc_4AD188:
.text:004AD188                 lea     ecx, [ebp+108h+var_120] ; void *
.text:004AD18B                 mov     [ebp+108h+var_10C], esi
.text:004AD18E                 call    ds:mfc90_601
; Программа зарегистрирована (BL=0) ?
.text:004AD194                 test    bl, bl
.text:004AD196                 jz      short loc_4AD1AA
.text:004AD198 loc_4AD198:
; Вывести сообщение "Wrong activation key!"
.text:004AD198                 push    0FFFFFFFFh
.text:004AD19A                 push    esi
.text:004AD19B                 push    15Ah
.text:004AD1A0                 call    mfc90_1182
.text:004AD1A5                 jmp     loc_4AD2DA

Теперь посмотрим на вызываемую функцию проверки. Мы помним, что она должна вернуть строчку "Ok", поэтому поищем все, что так или иначе связано с этой строкой. Найдется вот такой кусочек кода:
; Выполнить сравнение
.text:004A1EAE                 cmp     edx, eax
; Если не равно, то переход на возврат значения "Error"
.text:004A1EB0                 jnz     short loc_4A1F15
.text:004A1EB2                 inc     ecx
.text:004A1EB3                 cmp     ecx, 6
.text:004A1EB6                 jl      short loc_4A1E73
.text:004A1EB8                 add     ebp, 7
.text:004A1EBB                 cmp     ebp, 1Ch
.text:004A1EBE                 jl      short loc_4A1E6D
; Вернуть значение "Ok"
.text:004A1EC0                 mov     esi, [esp+18h]
.text:004A1EC4                 push    offset aOk      ; "Ok"
.text:004A1EC9                 mov     ecx, esi
.text:004A1ECB                 call    ds:mfc90_310
...
; Часть второстепенного кода опущена
...
.text:004A1F15 loc_4A1F15:
.text:004A1F15                 mov     esi, [esp+18h]
; Вернуть значение "Error"
.text:004A1F19                 push    offset aError_0 ; "Error"
.text:004A1F1E                 mov     ecx, esi
...

Простыми патчами на возврат EAX=1 тут не обойтись, но достаточно забить командами NOP единственный условный переход по адресу 004A1EB0, ведущий к возврату ошибки. После этого программа примет любой регистрационный ключ как родной. Код около места патча достаточно "приметный", так что можно легко выделить сигнатуру для поиска и сделать универсальный патч для всех последующих версий программы.

При анализе ассемблерного листинга в районе вызова проверок серийного номера я несколько раз натыкался на файл под названием "stpnm2.key". Поискав на диске этот файл я обнаружил, что он находится в папке %ProgramData%\stpnm2.key и в него в текстовом виде записывается введенный регистрационный ключ плюс первым байтом длину ключа. То есть мало того, что программа гадит в реестре, так еще и распихивает свои файлы куда ни попадя. Причем при деинсталляции файл ключа не удаляется. В который раз убеждаюсь, что хорошие манеры программирования сейчас большая редкость. Но может быть эта информация будет полезной для любителей делать портативные сборки разного софта.

С патчами все понятно, теперь попробуем разобрать алгоритм проверки и, соответственно, алгоритм генерации правильного серийного номера. Для этого поставим точку останова на начало уже знакомой нам функции проверки по адресу 004A1D80 и пройдем ее под отладчиком, введя какой-нибудь неправильный серийный номер. Еще в листинге дизассемблера бросается в глаза интересная конструкция, в которой из строки получаются значения по определенной маске.
...
.text:004A1E03                 push    edx
.text:004A1E04                 push    offset a6c6c6c6c6c6c6c
; "%6c-%6c-%6c-%6c-%6c-%6c-%6c-%6c"
.text:004A1E09                 lea     ecx, [esp+0A4h]
.text:004A1E10                 call    ds:mfc90_910
.text:004A1E16                 push    eax
.text:004A1E17                 call    ds:sscanf
...

Теперь мы знаем формат серийного номера - 8 групп по 6 символов, разделенных черточками. Что интересно, полученные после этой операции данные нигде не используются и даже не проверяются на валидность. Ладно, зная формат, придумаем какой-нибудь серийный номер заведомо неправильный, но подходящий по формату, например, "AAAAAA-BBBBBB-CCCCCC-DDDDDD-EEEEEE-FFFFFF-GGGGGG-HHHHHH". Стартуем программу под отладчиком, вводим его в окно регистрации и в пошаговом режиме доходим до уже знакомого кода, но на этот раз уже с целью добиться понимания алгоритма проверки:
...
; Взять следующий символ из серийного номера
.text:004A1E73                 movsx   eax, byte ptr [esi+ecx]
; Умножить на 343FDh
.text:004A1E77                 imul    eax, 343FDh
; Прибавить 269EC3h
.text:004A1E7D                 add     eax, 269EC3h
.text:004A1E82                 mov     dword_504F84, eax
; Двоичный сдвиг
.text:004A1E87                 shr     eax, 10h
; Сброс всех битов кроме первых 8
.text:004A1E8A                 and     eax, 7FFFh
; Обнулиить EDX
.text:004A1E8F                 cdq
; Поделить на 24h (значение видно под отладчиком)
.text:004A1E90                 idiv    edi
; Если результат вышел за некие границы, то серийник неправильный
.text:004A1E92                 test    edx, edx
.text:004A1E94                 jl      loc_4A1F35
.text:004A1E9A                 cmp     edx, edi
.text:004A1E9C                 jge     loc_4A1F35
; Взять символ из проверочного массива и сравнить его с символом из серийника
; Проверочный массив - строка "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
; Индекс элемента - значение регистра EDX
.text:004A1EA2                 movzx   edx, byte ptr [ebx+edx]
.text:004A1EA6                 lea     eax, [ecx+ebp]
.text:004A1EA9                 movsx   eax, byte ptr [esp+eax+4Ch]
.text:004A1EAE                 cmp     edx, eax
; Не совпало - серийный номер неправильный
.text:004A1EB0                 jnz     short loc_4A1F15
; Следующий символ
.text:004A1EB2                 inc     ecx
.text:004A1EB3                 cmp     ecx, 6
; Группа символов обработана?
.text:004A1EB6                 jl      short loc_4A1E73
; Следующая группа
.text:004A1EB8                 add     ebp, 7
.text:004A1EBB                 cmp     ebp, 1Ch
.text:004A1EBE                 jl      short loc_4A1E6D
; Все символы соответствуют, серийный номер правильный
.text:004A1EC0                 mov     esi, [esp+18h]
.text:004A1EC4                 push    offset aOk      ; "Ok"
.text:004A1EC9                 mov     ecx, esi
...

Что получается? Серийный номер условно разделяется на две половины по 4 группы символов. При этом каждый символ из первой половины преобразуется в определенное значение (индекс элемента массива) по описанному выше алгоритму. Затем по этому индексу берется символ (элемент) из проверочного массива "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" и он сравнивается с соответствующим символом из той же позиции второй половины серийника. В нашем примере для латинской буквы "A" из первой половины серийника в проверочном массиве будет соответствовать символ "8", это хорошо видно в отладчике. Контрольное значение "8" сравнивается с введенным символом "E" из второй половины серийника:


Сравнение символа со значением из массива

Формула получения индекса в более привычной записи будет выглядеть примерно так:

index = ((serial[i] * 343FDh + 269EC3h) >> 10h) & 7FFFh) mod 24h

Выяснив одно единственное совпадение, можно сразу же узнать один из валидных серийный номеров, в котором будет первая и вторая половина будут состоять из одинаковых символов. Например, правильным серийным номером для регистрации будет "AAAAAA-AAAAAA-AAAAAA-AAAAAA-888888-888888-888888-888888". Попробуем зарегистрировать программу с этим ключом. Никаких сообщений о правильной регистрации не появится, однако надпись "TRIAL" в заголовке программы пропадет. Если теперь попробовать сделать панораму и сохранить результат, то и тут никаких ограничений не будет. Окно регистрации при запуске программы также пропало. То есть регистрационный ключ подобран правильно. Рабочий кейген теперь вы можете написать самостоятельно. Подобным образом обходится защита и других программ от STOIK. Новых побед вам в реверсинге и творческих успехов в фототворчестве!
Теги:
защита, взлом
Добавлено: 16 Сентября 2013 02:00:36 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...