Защита .NET-приложений при помощи Sentinel LDK Envelope

    Утилита Sentinel LDK Envelope, о которой пойдет речь в этой статье, предназначена для установки навесной защиты на исполняемые модули (EXE и DLL) для платформ Win32, Windows x64, .NET, а так же, на Java-приложения (JAR и WAR). Защита осуществляется путем «привязывания» кода приложения к ключу защиты Sentinel (новое поколение ключей HASP), причем, ключ может быть как аппаратным (HL), так и программным (SL). Обработанный таким образом исполняемый модуль, будет работать только в присутствии требуемого ключа со всеми необходимыми лицензиями. Помимо проверки наличия ключа, внедренный в приложение код защиты, так же, обеспечит активное противодействие отладке и затруднит реверс-инжиниринг приложения, включая статический анализ кода.

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

    С точки зрения автоматизации процесса защиты приложения, наилучшим вариантом являлась бы реализация защиты на уровне исходного кода приложения с использованием функций Sentinel LDK API. При такой реализации разработчик самостоятельно решает, как будет защищен тот или иной класс или метод, и, при повторной сборке проекта, в случае внесения изменений в исходный код, вся ранее проделанная работа по реализации защиты автоматически учитывается естественным образом, не требуя переделки. Однако, особенности платформы .NET не позволяют использовать множество техник защиты, которые могут применяться в нативном коде, например, динамическое шифрование кода во время работы приложения, контроль целостности кода, восстановление кода с использованием помехозащищенного кодирования и т.д. Вернее, скажем так, реализовать все это можно, но это будет уже, скорее всего, не комфортная работа на C#, а системное программирование на более «мощных» языках, низкоуровневая работа с CIL-кодом, а так же, необходимость сопровождения и актуализации использованных приемов работы в случае каких-либо изменений внутри .NET, например, при выходе новой версии данной платформы.

    С другой стороны, можно воспользоваться навесной защитой. Это потребует гораздо меньше времени на реализацию, квалификации разработчика может бть не столь высока (не нужны знание других языков программирования, владение CIL, …). Однако здесь тоже не всё безоблачно, полной автоматизации процесса установки защиты достичь трудно. Сначала мы подробно рассмотрим использование навесной защиты при обычном применении, после чего подумаем, как все это максимально автоматизировать. Предположим, что мы создаем изначально незащищенное приложение, не используя Sentinel LDK API, после чего обработаем сборку при помощи Envelope. Технологический цикл создания защищенного приложения в данном случае будет выглядеть так:
    1) Построение незащищенной сборки в среде разработки, например в Visual Studio.
    2) Защита построенной сборки в процессе интерактивной работы с Envelope.
    Рассмотрим более подробно второй этап и разберемся, какие способы защиты может нам предоставить Envelope. Процесс защиты состоит из следующих операций:
    1. Загрузив первый раз Envelope, мы увидим пустой проект. Сразу же укажем, с какой серией ключей будет работать наша защищенная сборка (см. рис. 1):
    image
    Рис. 1 – Выбор кода серии ключей

    2. Установим общие параметры защиты, которые будут применяться ко всем входящим в проект сборкам, подлежащим защите (см. рис. 2):
    image
    Рис. 2 – Установка общих параметров защиты

    Назначение параметров следующее:
    String encryption – зашифрование всех данных, хранящихся в куче #US, содержащей в стандарте UNICODE все строки, которые разработчик определил в своем исходном коде. Вот так, к примеру, в .NET Reflector’е выглядит код с незашифрованными строками:
    image
    А вот так – с зашифрованными:
    image
    Periodic Background checks – фоновый опрос ключа с заданным интервалом.
    Apply compression – сжатие при размещении в сборке тел классов и методов, помеченных как защищенные. Реально на стойкость защиты не влияет, .NET Reflector, например, это сжатие в упор не замечает, независимо от параметров данной опции.
    Obfuscate Symbols – обфускация данных, хранящихся в куче #Strings, содержащей символьную информацию, такую, как имена классов, методов, полей, и т.д. Возможна полная обфускация или частичная, когда не изменяются имена ресурсов.
    Класс Program до обфускации:
    image
    После обфускации:
    image

    3. Включим одну или несколько готовых сборок в проект утилиты Envelope (см. рис. 3).
    image
    Рис. 3 – Добавление целевых файлов в проект

    4. Для каждой входящей в проект сборки задаем пути (откуда брать исходный файл и куда класть защищенный), с какими ключами и как работать, какую лицензию использовать (см. рис. 4):
    image
    Рис. 4 – Установка параметров сборки

    5. При необходимости для каждой конкретной сборки можно установить свои параметры защиты, перекрывающие общие (см. рис. 5), и более тонко настроить работу защитных механизмов (см. рис. 6):
    image
    Рис. 5

    image
    Рис. 6

    Параметры на рис. 5 уже рассматривались в п.2. Из представляющих интерес параметров (с точки зрения защиты) на рис. 6 можно отметить следующие:
    LOCKING_TYPE – определяет типы ключей, с которыми будет работать защита данной сборки. Возможные варианты:
    image
    где:
    HL – аппаратный ключ
    SL-UserMode – программный ключ, умеющий работать без драйвера
    SL-AdminMode – программный ключ, работающий совместно с драйвером
    SL – SL-UserMode + SL-AdminMode

    MIN_CODE_SIZE – минимальный размер CIL-кода метода в байтах, начиная с которого он будет защищаться Envelope в случае групповой операции, например, когда для защиты отмечается весь класс целиком.

    6. Осталась самая трудоёмкая часть работы – определить классы и методы, подлежащие защите (рис. 7-1), и для каждого выбранного класса/метода задать параметры защитных механизмов (рис. 7-2):
    image
    Рис. 7 – Управление защитой методов и классов

    Чтобы выбрать для защиты класс или метод, просто поставьте галочку напротив его имени в окне 1. При этом в окне 2 отобразятся текущие параметры защиты для выбранного объекта. Назначение параметров защиты:

    Feature ID – номер лицензии из ключа. Для разных классов/методов может различаться в случае их раздельного лицензирования.
    Frequency – как часто будет проверяться ключ для защищенного класса/метода. Возможные варианты:
    image
    где:
    Once per program – один раз за все время работы приложения
    Once per class instance – каждый раз при создании экземпляра класса
    Every time – при каждом проходе управления через защищенный код

    • Symbol obfuscation – обфускация символьной информации, может быть применена к методу, даже если он не отмечен, как подлежащий защите, т.к. не связана с ключом. Возможные варианты:
    image
    где:
    Use global definition – используется текущее значение параметра Obfuscate Symbols, см. п. 2 и п. 5.
    Enabled – обфускация производится всегда, независимо от глобальных установок.
    Disabled – обфускация никогда не выполняется, независимо от глобальных установок.

    Code obfuscation – control_flow-обфускация CIL-кода, может быть применена к методу, даже если он не отмечен, как подлежащий защите, т.к. не связана с ключом. Так выглядит в IDA фрагмент кода функции после обфускации:
    image

    А это – граф передачи управления в функции после обфускации:
    image

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

    Code encryption – зашифрование CIL-кода выбранного метода через ключ, с использованием алгоритма AES. При этом способе защиты оригинальный CIL-код извлекается из тела метода, зашифровывается через ключ, и помещается в ресурсы сборки в зашифрованном виде. На его место вставляется короткий переходник, обеспечивающий загрузку, расшифрование, динамическую компиляцию и передачу управления на код защищенного метода. После окончания выполнения метода, его код удаляется из памяти. Вот так выглядит фрагмент тела защищенного метода в IDA:
    image
    Инструкция call class [mscorlib]System.Reflection.Emit.DynamicMethod определяет и представляет динамический метод, который будет скомпилирован в памяти, выполнен и впоследствии удален. Инструкция callvirt instance object [mscorlib]System.Reflection.MethodBase::Invoke осуществляет передачу управления на метод в случае его успешной компиляции. Данный способ защиты практически не влияет на быстродействие защищаемого кода, обеспечивая вполне приличную стойкость.

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

    Теперь вернемся к вопросу автоматизации процесса. Очевидно, что только что проделанная нами работа слабо автоматизирована. Да, мы сохранили всё, что сделали, в виде проекта, и, в следующий раз, когда потребуется защищать сборку, можно им воспользоваться, сразу загрузив в Envelope. Однако, весь технологический цикл создания защищенного приложения, по-прежнему состоит из двух слабо связанных этапов, которые выполняются в разных программах. Попробуем их связать, сделав защиту прозрачным для разработчика процессом. Для этого воспользуемся консольной версией Envelope, которая называется envelope.com (пусть вас не пугает расширение, это нормальный exe-файл, а не привет из древней ms-dos). Итак, при запуске с ключом –h нам сообщается, что:
    image

    Будем использовать утилиту с ключом –p на этапе «событие после построения» в Visual Studio. Можно сделать, например, так:
    image

    Вся работа с envelope.com вынесена в командный файл envelope.cmd, находящийся в каталоге проекта и содержащий следующие команды:

    ..\..\LDK\VendorTools\VendorSuite\envelope.com -n --protect %1
    move /Y %2.protect %2

    Для того, чтобы все правильно работало, нужно указать корректный путь до каталога с утилитой envelope.com (можно использовать абсолютный путь, а не относительный, как здесь), и внести небольшое изменение в проект защиты в Envelope, изменив расширение выходного файла, для того, чтобы нормально отработала команда move:
    image

    Работу утилиты можно проверить по логу в окне вывода Visual Studio:
    image

    После окончания процесса создания сборки в среде Visual Studio, мы сразу получаем защищенное приложение без лишних телодвижений.
    Вроде бы, все получилось неплохо, но как быть, если в исходный код приложения вносятся значительные изменения? Например, если появляются новые классы/методы, подлежащие защите, удаляются старые или планируется изменить способ защиты существующих классов/методов? При традиционном подходе придется открывать проект защиты в Envelope и вносить все накопившиеся изменения, как ранее описывалось в п. 6, что печально, поскольку это ставит крест на автоматизации процесса защиты при любых сколько-нибудь серьёзных изменениях в исходном коде приложения.
    Однако, есть возможность автоматизировать актуализацию таких изменений в исходном коде. Сделать это можно посредством использования пользовательских атрибутов в исходном коде приложения. Объектом защиты при помощи пользовательских атрибутов может быть метод, класс или вся сборка — в зависимости от того, где размещается тот или иной набор атрибутов. Список доступных атрибутов для защиты объекта полностью повторяет набор параметров защиты для классов/методов, описанный в п. 6. Вот список доступных атрибутов:
    Protect — тип BOOL, возможные значения — TRUE/FALSE, указывает нужно ли защищать объект с использованием ключа.
    FeatureId — тип int, возможные значения — [0;65535], указывает номер лицензии (Feature ID), которая будет использована при защите объекта. Принимается к исполнению, только если Protect == TRUE.
    Encrypt — тип BOOL, возможные значения — TRUE/FALSE, указывает нужно ли зашифровывать CIL-код объекта. Принимается к исполнению, только если Protect == TRUE.
    CodeObfuscation — тип BOOL, возможные значения — TRUE/FALSE, указывает нужно ли выполнять control_flow-обфускацию CIL-кода объекта. Принимается к исполнению независимо от значения Protect. Данный метод защиты приводит к снижению скорости работы защищенного кода.
    Frequency — перечисляемый тип EnvelopeMethodProtectionFrequency, указывает, как часто будет проверяться лицензия для защищаемого объекта. Принимается к исполнению, только если Protect == TRUE. Возможные значения:
    CheckOncePerApplicaton — однократная проверка в процессе работы приложения.
    CheckOncePerInstance — однократная проверка для каждого экземпляра объекта.
    CheckEveryTime — проверка при каждом проходе управления через код объекта.
    SymbolObfuscation — перечисляемый тип EnvelopeSymbolObfuscation, указывает на метод обфускации символьной информации в защищаемом объекте. Принимается к исполнению независимо от значения Protect. Возможные значения:
    ObfuscateSkip — полный запрет на обфускацию всей символьной информации.
    ObfuscateForce — принудительное выполнение обфускации для всей символьной информации.
    ObfuscateDefault — выполнять обфускацию для всей символьной информации, кроме public-имен, а так же, объектов с модификаторами virtual и protected.

    Для того, чтобы Envelope мог получить атрибуты из исходного кода, необходимо при помощи директивы using включить в него пространства имен Aladdin.HASP.Envelope и Aladdin.HASP.EnvelopeRuntime. Естественно, перед этим необходимо добавить файлы Aladdin.HASP.Envelope.DLL и Aladdin.HASP.EnvelopeRuntime.DLL в ссылки проекта Visual Studio. Распространять эти файлы с защищенным приложением не нужно, они требуются только на этапе установки защиты на сборку. Ниже приведен пример защиты из исходного кода с помощью пользовательских атрибутов:
    image

    Вообще говоря, все изменения, связанные с защитой сборки можно условно разделить на две категории:
    1. Глобальные изменения, которые происходят довольно редко. Описаны в п. 1 – 5, управлять ими из исходного кода нельзя.
    2. Изменения в исходном коде, связанные с появлением или удалением классов/методов, подлежащих защите, происходят значительно чаще. Описаны в п. 6. Теперь можно полностью актуализировать такие изменения из исходного кода, не занимаясь правкой проекта защиты в Envelope.

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

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 29

      0
      с упором на максимальную автоматизацию процесса установки защиты

      Максимальная автоматизация — это когда сборки автоматически защищаются на билд-сервере. Вы такое настраивали?
        0
        Да, это стандартная функциональность, в пункте 7 как раз про это.
          0
          В пункте 7 показано, как это сделать в VS (с учетом того, что LDK установлен на компьютере разработчика). А вот вещи типа «как сделать, так, чтобы это выполнялось на билд-машине» — нет (равно как и вещи вроде «как сделать так, чтобы при разных конфигурациях проекта он собирался либо с защитой, либо без).
            –2
            Понял, я не настраивал, но знаю коллег, кто так сделал.
              0
              Ну тогда до максимальной автоматизации вам еще весьма далеко. Потому что с одной стороны, holy Grail автоматизации в разработке — это сборка всего на билд-машине, а с другой стороны, именно в этом месте огребается максимум проблем с подключением любых утилит.
                –2
                Автоматизация в данном случае касается именно частного примера с .NET, где описывается возможность разметить код, чтобы больше к вопросу настройки параметров защиты не возвращаться.
                  0
                  описывается возможность разметить код, чтобы больше к вопросу настройки параметров защиты не возвращаться.

                  … и явно завязать исходники на используемую технологию защиты? Нет уж, спасибо.

                  Не говоря уже о том, что это к автоматизации не имеет никакого отношения — с ее точки зрения нет ровным счетом никакой разницы, где настраивать такие вещи, в сборке или в проекте.
                    –1
                    Ну собственно о явной завязке на технологию защиты говорится сразу в заголовке. Ну и утверждать, что использование билд-сервера — это единственные метод автоматизации защиты Вы же всё-таки не будете?
                      0
                      Ну собственно о явной завязке на технологию защиты говорится сразу в заголовке.

                      Нет, не говорится. Защита чего-то с помощью еще не означает, что защищаемое знает о том, с помощью чего его защищают. При правильной реализации можно выкинуть одну защиту, заменить на другую, не трогать исходники (кроме, например, конкретного участка, считающего пользователей).

                      Ну и утверждать, что использование билд-сервера — это единственные метод автоматизации защиты Вы же всё-таки не будете?

                      Я буду утверждать, что в рамках практики continuous integration часть процесса превращения исходников в финальный продукт, которая не вынесена на билд-сервер, не является достаточно автоматизированной.
                        0
                        Так Вас никто не заставляет размечать код в принудительном порядке, не хотите — не надо, просто в этом случае Вам нужно будет полагаться на то, как Envelope автоматом разберёт ваш код или при изменении кода приложения менять настройки проекта.
                        Ну и собственно то что Envelope можно использовать на билд-сервере — я уже сказал, просто лично я этого не делал.
                          0
                          Ну и собственно то что Envelope можно использовать на билд-сервере — я уже сказал, просто лично я этого не делал.

                          Ну вот поэтому ваша статья и неполна. Самого интересного в ней нет.
                            0
                            Да собственно, если код не размечать — это будет обычный подход к работе с Envelope, а разметка кода — это работает только под .NET и я описал как это использовать.
                              0
                              разметка кода — это работает только под .NET и я описал как это использовать.

                              Вот только для автоматической защиты это не нужно.
                                0
                                А это кому как.
                                  0
                                  Никому и никак. Эти атрибуты одинаково работают как в ручном, так и в автоматическом режиме; и автоматический режим одинаково хорошо работает как с ними, так и без них. То, что вам удобнее настраивать Envelope с помощью атрибутов — прекрасно, но именно к автоматизации отношения не имеет.
                                    0
                                    Атрибуты позволяют не менять проект, не запускать Envelope и управлять защитой из кода, чем не автоматизация?
                                      0
                                      Атрибуты в коде появляются автоматически?
                                        0
                                        Расставить атрибуты — это мощный инструмент, который позволяет избежать проблем с тормозами автоматической обфускации и шифрования, и выбрать либо особа значимые участки кода, либо наименее ресурсоёмкие. Да и собственно ни одной сточки защитного кода писать не требуется, всего лишь расставить флажки — разве это не автоматизм?
                                          0
                                          Когда вы настраиваете защиту в проекте, вы тоже не пишете ни одной строчки кода.

                                          Проще говоря, разница между настройкой атрибутами и настройкой в проекте только в месте настройки; степень автоматизации процесса одна и та же.
                                            0
                                            Так если поменять код, добавить новый класс — придётся снова лесть в проект и всё настраивать, а здесь всего лишь подставить атрибут.
                                              0
                                              Подставить атрибут — не автоматическое действие, поэтому автоматизация здесь не при чем.

                                              Если вы хотите поговорить за «настройка атрибутами vs настройка в проекте», то все плюсы/минусы уже озвучены — «настройка близко к коду vs привязка к конкретной системе защиты».
          –2
          Не на тот уровень.
            0
            Быть может пропустил по тексту… Цена вопроса какова?
              0
              По цене вопроса лучше к Яндексу.
                0
                Насколько я помню наш опыт, там все любопытно: сам разработческий комплект (софт+разработческий ключ) стоит смешных денег, но он рассчитан на работу с ключами, а болванки ключей вы покупаете у поставщика. И вот тут, собственно, и начинается сам профит…
                  0
                  Т.е. бесплатно попробовать на губки не раскатывать? И льгот для стартапа тоже нихт?
                    0
                    Когда мы этим занимались, бесплатно попробовать было вполне реально. Про льготы ничего не знаю.
                0
                А с какими типами токенов это может работать?
                Только с Алладином, или ещё, например с гуардантом или с китайскими токенами?..

                  0
                  Только с SafeNet (бывший Aladdin).

                Only users with full accounts can post comments. Log in, please.