Одним из наиболее популярных способов взлома программ является поиск проверки серийного номера и последующего условного перехода. Если номер корректен, то ход выполнения программы идет по одной ветке, если некорректен, то по другой. Хакер находит этот переход и заменяет его на безусловный переход на "правильную" ветку. Давайте "взломаем" таким образом нашу тестовую программу. На уровне исходников, конечно же. "Отключим" условный переход:
char *serial = read_serial("serial.txt");
int res = VMProtectSetSerialNumber(serial);
delete [] serial;
if (false && res)
{
Теперь наша программа примет любой серийный номер и будет работать как ни в чем не бывало. Безусловно, если файл защищен VMProtect, то даже опытному хакеру потребуется не один месяц, чтобы найти и исправить этот переход так, как сделали это мы. А с учетом того, что правильно защищенная программа проверяет серийный номер не в одном месте и не всегда, то даже такая простая проверка уже достаточно надежна. Однако мы пойдем дальше.
Привяжем код к серийному номеруВнимание! демо-версия VMProtect имеет ограничение на количество обрабатываемых функций: обрабатывается только одна функция. Поэтому в случае использования демо-версии, необходимо включать в проект только функцию foo(), т.к. в противном случае демо-версия VMProtect может выбрать для защиты функцию main() и привязка кода к серийному номеру не сработает.
Система лицензирования VMProtect позволяет привязать код одной или нескольких функций к серийному номеру так, что они не будут работать без корректного номера. Тело функции виртуализируется, потом шифруется и может быть расшифровано только при наличии корректного серийного номера. То есть даже если хакер найдет и исправит условный переход в коде проверки номера, функции, привязанные к номеру, все равно не будут работать. Давайте попробуем. В VMProtect в разделе "Функции" выберем функцию foo() и в правой панели изменим значение опции "Привязать к серийному номеру" на "Да".
После этого защитим наше приложение. С учетом того, что оно "взломано", поместим в файл serial.txt произвольный текст и запустим приложение. В консоли появится текст:
C:\test>dummy_app.vmp.exe serial number is correct, calling foo()
То есть хакер "исправил" нужный условный переход и программа пошла по "правильной" ветке. Однако в момент вызова foo() программа выводит сообщение:
Так как мы привязали функцию foo() к серийному номеру, а у хакера его нет, то при попытке расшифровать код функции модуль лицензирования выдал сообщение о невозможности продолжить выполнение программы. После нажатия кнопки "ОК" программа завершится и в консоли мы не увидим строчки "done".
Что привязывать к серийному номеру?Привязывать к серийному номеру имеет смысл те функции, которые вызываются только в зарегистрированной версии программы. Так как привязка возможна только при использовании виртуализации, то необходимо принимать во внимание возможное падение производительности. Скажем, если текстовый редактор не позволяет сохранять результаты своей работы в демо-версии, то к серийному номеру можно привязать функцию сохранения документа. Если эта функция вызывает ряд других в процессе работы, то их привязывать уже не обязательно, так как без основной функции от них будет немного толку.
Также необходимо помнить, что вызов привязанной функции без серийного номера приведет к завершению работы программы без возможности сохранения результатов работы, поэтому тщательно тестируйте защищаемую программу, чтобы она не позволяла вызвать такие функции в демо-режиме. В примере с текстовым редактором убедитесь, что в демо-режиме пункт меню "Сохранить" всегда неактивен, что комбинация Ctrl+S не срабатывает и что при закрытии программы также не предлагается сохранить изменения. В противном случае пользователь может составить негативное мнение о вашей программе.
Привязка к серийному номеру и нерабочие номераПри вызове функции VMProtectSetSerialNumber() модуль лицензирования проверяет переданный серийный номер и зашифрованные участки кода будут выполняться только в том случае, если на момент проверки серийный номер был полностью корректным - не занесенным в черный список, с правильным идентификатором оборудования, с неистекшим сроком годности и т.п. В этом случае все зашифрованные процедуры будут выполняться до завершения работы приложения или до нового вызова VMProtectSetSerialNumber().
Некоторые ограничения могут "сработать" в процессе выполнения программы: например, может истечь время работы программы или окончится срок действия серийного номера. В этом случае модуль лицензирования продолжит расшифровывать и выполнять функции, привязанные к серийному номеру. Это делается потому, что защищенному приложению будет сложно отследить момент срабатывания ограничений и соответствующим образом изменить модель своего поведения (заблокировать пункты меню и т.п). Если модуль лицензирования неожиданно для приложения перестанет выполнять привязанные к серийному номеру участки кода, это с большой вероятностью приведет к неработоспособности приложения. Поэтому все решается в момент установки серийного номера и именно в этот момент определяется режим, в котором будет работать приложение.