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


Скриншот программы Pano2VR

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

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


Окно ввода серийного номера

Сразу после запуска открывается окно ввода серийного номера, а также два варианта работы в триальном режиме - просто Trial и Trial Pro. Причем во втором варианте открываются все самые вкусные плюшки программы - создание виртуальных туров из нескольких панорам, добавление своих данных в готовый ролик и удаление информации о самой программе (так называемая unbranded-лицензия), и еще много всего полезного. В обычной версии эти функции недоступны. Значит наша задача получить на выходе именно версию Pro. Поищем в файле какие-нибудь упоминания о лицензиях, триале и тому подобном:


Информация о лицензии

Как много всего! Тут и лицензии для студентов, и образовательные, и еще какие-то ограниченные по времени. Посмотрим, как и на основании чего они подразделяются:
    ...
    ; Если DWORD по адресу B74CE0 равен 92h, то лицензия "NFR"
    .text:0049EE82                 cmp     dword_B74CE0, 92h
    .text:0049EE8C                 jnz     loc_49EF16
    .text:0049EE92                 push    0
    .text:0049EE94                 lea     ecx, [esp+0C8h+var_50]
    .text:0049EE98                 push    offset aNfr     ; "NFR"
    .text:0049EE9D                 push    ecx
    .text:0049EE9E                 call    sub_494220
    ...
    ...
    ; Если DWORD по адресу B74CE0 равен 93h, то лицензия "Student"
    .text:0049EF16                 cmp     dword_B74CE0, 93h
    .text:0049EF20                 jnz     loc_49EFAA
    .text:0049EF26                 push    0
    .text:0049EF28                 lea     eax, [esp+0C8h+var_50]
    .text:0049EF2C                 push    offset aStudent ; "Student"
    .text:0049EF31                 push    eax
    .text:0049EF32                 call    sub_494220
    ...

На основании этих фрагментов кода мы выяснили, что за тип лицензии отвечает DWORD по адресу 00B74CE0. Посмотрим по перекрестным ссылкам, где же инициализируется это значение.


Перекрестные ссылки на DWORD

Как видите, все изменения значения типа лицензии происходят в процедуре по адресу 004849D0. Она большая, я ее тут не буду приводить. Первая мысль - пропатчить все "неправильные" значения инициализации, например, на "студенческую" лицензию, то есть 93h. Логично, но в данном случае абсолютно неэффективно. Программа в этом случае все равно работает в триальном режиме. Похоже, что если вообще никакого ключа у программы нет, то при проверке типа лицензии срабатывают какие-то иные ветки алгоритма. Надо искать другой путь. Посмотрим еще раз на перекрестные ссылки. Кроме инициализации в коде выполняется куча проверок типа лицензии. Так почему бы нам не заменить одну из проверок на инициализацию типа лицензии нужным нам значением? Тем более, что раз код инициализации уже отработал, то ничего наше предустановленное значение больше не изменит.


Ставим точку останова на доступ к памяти

Поставим в отладчике точку останова на адрес памяти 00B74CE0. Теперь при любом обращении программы к этой ячейке памяти мы об этом сразу же узнаем. Запускаем программу. Первая остановка будет на коде инициализации:
    ...
    .text:004856AC                 mov     ecx, offset dword_B74CF8
    .text:004856B1                 mov     dword_B74CE0, 1
    .text:004856BB                 call    ds:??4QString@@QAEAAV0@PBD@Z
    ...

Пропускаем ее и снова запускаем программу. Вторая остановка гораздо интереснее:
    ...
    .text:0049EBE6                 mov     ecx, [esi+88h]
    .text:0049EBEC                 lea     edx, [esp+0C4h+var_78]
    .text:0049EBF0                 push    edx
    .text:0049EBF1                 call    ds:?setText@QLabel@@QAEXABVQString@@@Z
    ; Если DWORD по адресу B74CE0 меньше 80h, то лицензии нет или она
    ; ограничена по времени
    .text:0049EBF7                 cmp     dword_B74CE0, 80h
    .text:0049EC01                 jl      loc_49F1F2
    .text:0049EC07                 push    0
    .text:0049EC09                 lea     eax, [esp+0C8h+var_68]
    .text:0049EC0D                 push    offset aLicense ; "License:"
    .text:0049EC12                 push    eax
    .text:0049EC13                 call    sub_494220
    .text:0049EC18                 add     esp, 0Ch
    .text:0049EC1B                 mov     eax, ds:?shared_null@QString@@0UData@1@A
    .text:0049EC20                 mov     [esp+0C4h+var_7C], eax
    .text:0049EC24                 mov     ecx, 1
    .text:0049EC29                 lock xadd [eax], ecx
    ; Если байт по адресу B74CE4 не равен 0, то лицензия "Pro"
    .text:0049EC2D                 cmp     byte_B74CE4, 0
    .text:0049EC34                 mov     byte ptr [esp+0C4h+var_4], 7Bh
    .text:0049EC3C                 jz      loc_49ECC6
    .text:0049EC42                 push    0
    .text:0049EC44                 lea     edx, [esp+0C8h+var_50]
    .text:0049EC48                 push    offset aPro     ; "pro"
    .text:0049EC4D                 push    edx
    .text:0049EC4E                 call    sub_494220
    .text:0049EC53                 push    offset unk_6D4C50
    ...

Ну просто подарок на блюдечке. Тут нам и проверка типа лицензии, и проверка на версию "Pro", которая, оказывается, хранится в байте по адресу 00B74CE4. Приступаем к патчу.


Патчим проверки

Заменяем две проверки на команды MOV и нужные нам значения, а чтобы не нарушалась логика работы, также забиваем последующие команды условных переходов на NOP. Сохраняем изменения. Запускаем программу и видим окно с расширенными функциями и греющую душу надпись в правом нижнем углу:


"Студенческая лицензия Pro"

Запускаем, проверяем, все расширенные функции программы работают. Можно на этом остановиться, но есть еще одно маленькое "но". У нас получилась программа, зарегистрированная на именную персональную лицензию, информация о которой будет добавляться в контекстное меню всех создаваемых Flash-роликов, причем эта информация до кучи является ссылкой на сайт программы. Оно вам надо? Вот и мне не надо. Поэтому сейчас будем расширять нашу лицензию до максимально возможной, то есть "unbranded". Никаких прямых упоминаний об этом типе в коде программы нет, поэтому надо подумать и снова призвать на помощь логику. Раз этот пункт меню добавляется в панораму, значит он где-то в программе должен обрабатываться. Поищем что-то подобное в листинге дизассемблера, используя в качестве сигнатуры уже известную нам строчку "Student".
    ...
    .text:004732F1                 push    offset dword_B74CF8
    ; jumptable 004731D9 case 3
    .text:004732F6                 lea     eax, [esp+4Ch+var_18]
    .text:004732FA                 push    offset aStudentLicense
    ; "Student License: "
    .text:004732FF                 push    eax
    .text:00473300                 call    sub_40AAE0
    .text:00473305                 add     esp, 0Ch
    .text:00473308                 push    eax
    .text:00473309                 lea     ecx, [esp+4Ch+var_2C]
    .text:0047330D                 mov     byte ptr [esp+4Ch+var_4], 0Eh
    .text:00473312                 call    ds:??4QString@@QAEAAV0@ABV0@@Z
    ; QString::operator=(QString const &)
    .text:00473318                 mov     ecx, [esp+48h+var_18]
    .text:0047331C                 mov     byte ptr [esp+48h+var_4], 0Ah
    .text:00473321                 or      edx, 0FFFFFFFFh
    .text:00473324                 lock xadd [ecx], edx
    .text:00473328                 jnz     short loc_473338
    .text:0047332A                 mov     eax, [esp+48h+var_18]
    .text:0047332E                 push    eax
    .text:0047332F                 call    ds:?free@QString@@CAXPAUData@1@@Z
    .text:00473335                 add     esp, 4
    .text:00473338 loc_473338:
    .text:00473338                 push    offset aHttpGardengn_5
    ; "http://gardengnomesoftware.com/pano2vr?"...
    .text:0047333D                 lea     ecx, [esp+4Ch+var_28]
    .text:00473341                 call    ds:??4QString@@QAEAAV0@PBD@Z
    ...

Первое же совпадение и сразу удача. Тут сразу же видны и манипуляции со строкой лицензии, и ссылку на сайт, которая добавляется в контекстное меню, в общем, копать надо здесь. Смотрим на код, с которого выполняется переход:
    ...
    .text:004731C1                 mov     eax, dword_B74CE0
    .text:004731C6                 add     eax, 0FFFFFF70h
    .text:004731CB                 mov     byte ptr [esp+48h+var_4], 0Ah
    .text:004731D0                 cmp     eax, 3          ; switch 4 cases
    .text:004731D3                 ja      loc_473349      ; default
    .text:004731D9                 jmp     ds:off_473610[eax*4] ; switch jump
    ...

Код короткий, но все-таки требует пояснения. Сперва в регистр EAX записывается значение типа лицензии, затем к нему прибавляется константа 0FFFFFF70h. Если полученный результат больше 3, то выполняется прыжок в обход всех вариантов лицензии (unbranded-версия), иначе в зависимости от полученного значения в меню добавляется информация о лицензии. Теперь чуток математики: 93h + 0FFFFFF70h = 3h. Значит, чтобы получить значение больше 3, надо тип лицензии увеличить как минимум на единицу, то есть в коде инициализации вписывать не 93h, а 94h. Проверим нашу догадку на практике.


Финальный патч

Сохраняем изменения, запускаем программу. Вот теперь действительно полный порядок. У нас получилась unbranded-версия программы со всем доступными опциями режима Pro. Я даже боюсь представить, сколько это стоило бы в евро, если вдруг кто-то захотел бы ее купить.


"Лицензия Unbranded Pro"

В принципе, можно даже выделить сигнатуру для универсального патча, чтобы потом не заморачиваться с обновлениями. Автору спасибо за классную программу и интересную защиту, а всем вам спасибо за внимание и успехов в творчестве.
Добавлено: 14 Сентября 2013 10:06:20 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...