Pull to refresh

Облачная защита для .NET приложений

Reading time 6 min
Views 3.9K
просто облачко
Пожалуй, защита ПО всегда была для меня одной из самых любимых тем. Я обожал придумывать сложные хитроумные проверки лицензионности программы, и с упоением реализовывал их. Я всегда держался принципа, что хакер, чтобы взломать защиту, должен изучить максимум технологий использующихся в программе. Пусть думает о синхронизации потоков, если ему захотелось поставить бряк в алгоритме проверки ключа. Пусть изучает вопросы подсчета COM ссылок, если он хочет вмешаться в мой алгоритм. Пусть думает о том, как представлены битовые карты изображений в памяти, если он решил разобраться, как я сохранил данные ключа.

Да, C++ был почти идеальным языком в этом плане. Но времена меняются, старые технологии уходят и на их место приходят новые более продуктивные и удобные. Так наша команда перешла на .NET. Но в обмен на простоту разработки и удобство отладки, мы в придачу получили в довесок и простоту декомпиляции нашего ПО. Теперь хакер мог не просто обойти лицензионные ограничения, но и получить почти полный исходник нашей программы просто скормив ее рефлектору.
Разумеется, в качестве решения этой проблемы на рынке было представлено множество различных обфускаторов. Но, как ни странно, большинство из них разочаровывали меня сразу с двух сторон: и ценовой политикой (даже минимальная лицензия некоторых превосходила стоимость нашего ПО в несколько раз), и «интеллектуальностью» алгоритма. Так, после некоторых обфускаторов, умудрялись падать даже простые WinForms приложения. Что же касалось WPF, то без долгого-долгого черного шаманства над эксклудами, запустить среднего размера программу не представлялось возможным в принципе.

Так сформировалось понимание проблемы и четкое желание создать свой продукт, сводящий озвученные выше проблемы к минимуму. И появился SaaS обфускатор и протектор .NET кода AppFuscator.com

Обфускатор и протектор .NET кода appfuscator




Алгоритмы защиты


Renaming

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

Assembly Merging

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

Decomposition

Декомпозиция структуры классов в процедурное представление — наша собственная оригинальная разработка, базирующаяся на идее перевода программы из объектно-ориентированной формы (простой для реверс-инжиниринга) к процедурному стилю, с максимальным уничтожением всей доступной информации хранимой в метаданных (но сохранением полной работоспособности сборщика мусора).
Расскажу чуть подробнее. Допустим, у нас есть сборка с такой структурой классов:

Обфускатор кода

После выполнения декомпозиции мы увидим следующее:
.NET обфускатор

Метод, который выделен на скриншоте — это и есть бывший MainForm_Load — обработчик загрузки формы. Только теперь он, как и весь остальной код, лежит за пределами класса, к которому относится, в глобальном пространстве имен (в отличие от C#, где любой метод должен относиться к классу, в IL допустимо объявление обычных глобальных функций).

В самой форме (теперь это класс b) остался только один метод: Dispose. Он является перегрузкой виртуальной функции, поэтому мы его не выносим.

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

External Method Call Hiding

Сокрытие вызова внешних методов — подмена явного вызова методов из внешних сборок (в том числе обращений к Common Language Runtime), на неявное обращение по неуправляемому указателю.
Небольшой пример. Было:

public void ShowMessage(string text)
{
    MessageBox.Show(text);
}


Стало:
// <Module>
public static void a(object obj, string text)
{
    object arg_0C_0 = calli(System.Int32(System.String), text, g.b);
}


String Encryption

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

Reflection Analyzing

Анализ обращений к механизму отражения — набор алгоритмов, отслеживающих обращение к Reflection.
Мне всегда категорически не нравилось, что после сборки очередной версии программы начинается следующий этап – танцы с бубном вокруг обфускатора. Поэтому в нашем продукте предусмотрен анализатор, который обучен автоматически отслеживать связи, и корректировать имена в коде программы. Он имеет довольно мощную логику разбора сложных конструкций и выбора конечного решения. Все возможные случаи закрыть автоматически он, конечно, не сможет, но жизнь разработчику облегчит существенно.
Пусть у нас есть два класса:

class ClassA
{
    string GetText() { return "A"; }
    public int smallField;
}

class ClassB
{
    string GetText() { return "B"; }
}


Рассмотрим следующий пример кода:

public void HabraTestFirst(int x)
{
    Type work = null;
    string method = "gettext";

    if (x == 0)
        work = typeof(ClassA); // Берем ClassA
    else
    {
        string name = "HabraCode.ClassB, HabraCode, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
        work = Type.GetType(name, true); // Берем ClassB
    }

    // И вызываем у него метод GetText
    HabraReflectionCall(work, method);
}

public void HabraReflectionCall(Type target, string method)
{
    var call = target.GetMethod(method, BindingFlags.IgnoreCase | BindingFlags.NonPublic | BindingFlags.Instance);
    var obj = Activator.CreateInstance(target);
    call.Invoke(obj, null);
    MessageBox.Show(string.Format("HabraReflection call: {0}.{1}", obj.ToString(), call.Name));
}


В этом примере, в зависимости от переданного на вход числового параметра, будет вызван метод либо в классе ClassA либо в ClassB. При этом никакой связи наследованием\интерфейсами эти классы между собой не имеют.

Компилируем простую программу:
var reflector = new Reflector();
reflector.HabraTestFirst(0);
reflector.HabraTestFirst(1);


Запускаем и вуаля:
Обфускация .NET Reflection

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

public void HabraTestSecond()
{
    FieldInfo result = null;
    var anyobj = new ClassA().GetType();

    foreach (var it in anyobj.GetFields())
    {
        if (it.Name == "smallField") // Ищем у класса поле smallField перебирая все по очереди
            result = it;
    }

    if (result == null) throw new InvalidOperationException();
    MessageBox.Show("HabraReflection field: " + result.ToString());
}


При запуске программа честно сообщит нам:
Reflection Alnalyzing module

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

Исходный код можно скачать здесь.

WPF Analyzing

Автоматическое исключение из обфускации типов, методов и полей, к которым имеются обращения из Windows Presentation Foundation. Программа производит декомпиляцию и анализ скомпилированных XAML ресурсов в сборках. Полностью поддерживает расширения синтаксиса WPF, включая сложные конструкции (например, PropertyPath и другие).
Благодаря этому, в программе, написанной на WPF, переименовано будет только то, что должно быть переименовано.

Типичный пример программы после обфускации:
Результат обфускации .NET приложения
(кликните для увеличения)

Почему SaaS?



Все просто — вместо того, чтобы покупать хороший коробочный обфускатор за несколько тысяч долларов, а затем постоянно платить за покупку его обновлений (ведь технологии платформы .NET развиваются очень быстро), Вы приобретаете доступ к сервису онлайн обфускации. И после этого платите только за тот объем работы, который нужен лично Вам. Выпускаете версию раз в месяц – платите только за это.
Ценовая политика сейчас в стадии формирования, но я с уверенностью могу сказать о том, что условия будут вкусными. Кроме того, будет доступна бесплатная версия, функционал которой превосходит все бесплатные обфускаторы, представленные на данный момент.

Это безопасно?


Мы очень серьезно подходим к вопросу защиты Ваших данных.

Первое и самое главное: Вам не нужно отправлять на сервер исходные коды программ. Вы присылаете только уже скомпилированные сборки, именно в таком виде, как их увидели бы Ваши пользователи (а возможно и не только пользователи), если бы Вы не занимались вопросом защиты и не обфусцировали их. Никакой другой информации не потребуется.
Весь процесс обфускации производится на выделенных серверах, доступ к которым имеет только несколько человек из нашей команды. Разумеется, мы используем последние версии ПО и производим регулярные апдейты. Кроме того, модуль обфускации и web-фронтенд для взаимодействия с пользователем, физически размещены на отдельных серверах, что повышает защищенность и масштабируемость системы.

Мы являемся юридическим лицом, и готовы предоставить все необходимые документы, такие как договор на использование нашего сервиса и NDA.

Сейчас сервис запущен в режиме бета версии, и Вы можете тестировать весь функционал абсолютно бесплатно.
Tags:
Hubs:
+28
Comments 37
Comments Comments 37

Articles