Общая информация

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

Обфускация (запутывание) предполагает усложнение понимания злоумышленником того, как выполняется код. Это достигается изменением имен символов, мутацией кода, добавлением лишнего (заведомо не выполняющегося) кода, усложнением логики переходов между инструкциями и блоками кода, иными способами.

Шифрование применяется к различным данным, хранящимся в файлах программ, особенно к строковым и числовым константам и ресурсам. Без ключа шифрования злоумышленник не сможет получить доступ к защищаемым данным. Также возможно шифрование кода, поскольку инструкции представимы в виде массива байт. При необходимости выполнения участка кода такой массив расшифровывается в отдельной области памяти, после чего управление передается на него.

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

Нативные программы

Конверт

Конверт является представлением файла программы в упакованном и зашифрованном виде. В процессе запуска защищенной программы специальный загрузчик производит распаковку и расшифровку файла перед тем, как управление будет передано соответствующей точке входа. В результате оказываются зашифрованы секции файла программы, которые содержат, среди прочего, код программы, строковые и числовые константы, ресурсы. Также для некоторых файлов программ поддерживается скрытие импортов, что позволяет убрать из файла программы информацию о том, какие разделяемые библиотеки необходимы для работы программы и какие функции этих библиотек будут вызываться. Кроме того, прямые вызовы импортируемых функций заменяются косвенными, что также затрудняет понимание логики программы.

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

Защита функций

Для защиты конкретных функций программы используются нескольких техник одновременно. Все они основаны на преобразовании кода функции тем или иным способом.

Виртуализация кода

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

Естественно, технология виртуализации требует дополнительных вычислений, что дает существенное замедление выполнения виртуального кода, по сравнению с машинным кодом, а также увеличение размера файла программы. По этой причине применять виртуализацию рекомендуется не повсеместно, а лишь к отдельным функциям.

Мутация кода

Код программы может быть преобразован таким образом, чтобы затруднить отладку, без использования виртуализации. Техника защиты программы такого рода относятся к обфускации (запутыванию) кода. Такое преобразование кода может быть выполнено различными способами, может быть осуществлено без шифрования или с шифрованием.

Как правило, для этого в код программы добавляется заведомая избыточность (в плане объема исполняемого кода), что негативно сказывается на скорости исполнения защищаемых функций.

Извлечение кода

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

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

Программы .NET

Программы (сборки) .NET имеют некоторые особенности, по сравнению с нативными программами. Это связано с тем, что для выполнения кода используется среда .NET, использующая промежуточное представление в виде байт-кода на языке CIL (MSIL). В конечном итоге, байт-код интерпретируется на виртуальной машине .NET, которая преобразует байт-код в машинный код и выполняет его. Оказывается, что в файле программы .NET содержатся данные, аналогов которым нет в файле нативной программы.

Обфускация символов

Обфускация символов (символьная обфускация, лексическая обфускация, переименование символов) состоит в переименовании символов, определенных в файле программы .NET, в соответствии с определенной схемой. Суть этой техники состоит в том, чтобы символы программы – классы (типы), методы и их параметры, поля, свойства, события и т.д. – получили вместо значащих имен трудночитаемые или неосмысленные имена. Обычно используются непечатаемые символы (часто диакритические знаки), однобуквенные имена или имена, представляющие собой значения контрольной суммы в закодированном виде.

Обфускация символов, в отличие от остальных техник защиты, не требует наличия ключа Guardant для работы защищенного приложения.

Читать код становится значительно труднее, понять логику программы – сложнее. Как правило, это также приводит к трудностям при трассировке программы или при обнаружении строки исходного кода, в которой выбрасывается исключение, поскольку в стеке вызовов имена методов защищенной программы представлены в обфусцированном виде. При этом данная техника защиты не оказывает влияния на скорость выполнения кода в защищенной программе.

Если в защищаемой программе используется Reflection API, то работоспособность программы может быть нарушена. Это связано с тем, что имена символов изменяются в процессе защиты. В таких случаях следует исключить из символьной обфускации соответствующие символы.

Результат обфускации символов:

Необходимо принимать во внимание тот факт, что разные символы программы имеют различные уровни доступа. Так, некоторые символы – внешние интерфейсы (внешние символы) – должны оставаться видимыми извне файла программы (особенно это касается библиотек), в то время как остальные символы должны быть инкапсулированы и недоступны извне.

Обфускация потока управления

Поток управления программы представляет собой последовательность переходов между инструкциями, объединенными в блоки кода. Переходы между блоками кода осуществляются с помощью инструкций if-else, циклов, switch, вызовов функций. Обфускация потока управления заключается в разбиении блоков на более мелкие (вплоть до отдельных инструкций), перемешивании блоков, добавлении лишних блоков и скрытии порядка их выполнения (с помощью динамических переходов goto), а так же шифрования вспомогательного значения, идентифицирующего следующий блок кода, с использованием ключа Guardant.

Такая техника существенно затрудняет анализ выполнения кода, поскольку логика перехода между блоками кода становится запутанной. Для применения этой техники необходимо, чтобы обфусцируемый метод имел достаточное количество блоков кода и инструкций перехода. Замедление скорости выполнения кода в обфусцированном методе зависит от количества блоков кода и инструкций перехода.

Результат обфускации потока управления:

Шифрование строк

Строковые константы, хранящиеся в файле программы, могут содержать информацию, которая также подлежит защите. Например, в файле программы могут содержаться в текстовом виде API-ключи. Также некоторые строки могут дать подсказку потенциальному злоумышленнику насчет логики программы и облегчить поиск определенных фрагментов кода. Чтобы избежать раскрытия этих данных потенциальным злоумышленником, строки шифруются с использованием ключа Guardant.

Шифрование строк связано с замедлением работы программы, поэтому необходимо соблюсти компромисс между защищенностью и быстродействием программы. В программах, активно использующих фиксированные строки, например для формирования или парсинга данных в форматах JSON и XML, имеет смысл исключить из шифрования строк определенные классы, методы или поля.

Результат шифрования строк:

Защита методов

Во многом защита методов в программах .NET схожа с защитой функций в нативных программах. Код метода точно так же представим в виде массива байт. Он может быть извлечен из файла программы .NET и перемещен в библиотеку защиты, предварительно будучи зашифрованным с использованием ключа Guardant. Тело метода заменяется заглушкой в виде вызова делегата с аргументами, переданными защищенному методу в качестве параметров.

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

  • No labels