Пожалуй, защита ПО всегда была для меня одной из самых любимых тем. Я обожал придумывать сложные хитроумные проверки лицензионности программы, и с упоением реализовывал их. Я всегда держался принципа, что хакер, чтобы взломать защиту, должен изучить максимум технологий использующихся в программе. Пусть думает о синхронизации потоков, если ему захотелось поставить бряк в алгоритме проверки ключа. Пусть изучает вопросы подсчета COM ссылок, если он хочет вмешаться в мой алгоритм. Пусть думает о том, как представлены битовые карты изображений в памяти, если он решил разобраться, как я сохранил данные ключа.
Да, C++ был почти идеальным языком в этом плане. Но времена меняются, старые технологии уходят и на их место приходят новые более продуктивные и удобные. Так наша команда перешла на .NET. Но в обмен на простоту разработки и удобство отладки, мы в придачу получили в довесок и простоту декомпиляции нашего ПО. Теперь хакер мог не просто обойти лицензионные ограничения, но и получить почти полный исходник нашей программы просто скормив ее рефлектору.
Разумеется, в качестве решения этой проблемы на рынке было представлено множество различных обфускаторов. Но, как ни странно, большинство из них разочаровывали меня сразу с двух сторон: и ценовой политикой (даже минимальная лицензия некоторых превосходила стоимость нашего ПО в несколько раз), и «интеллектуальностью» алгоритма. Так, после некоторых обфускаторов, умудрялись падать даже простые WinForms приложения. Что же касалось WPF, то без долгого-долгого черного шаманства над эксклудами, запустить среднего размера программу не представлялось возможным в принципе.
Так сформировалось понимание проблемы и четкое желание создать свой продукт, сводящий озвученные выше проблемы к минимуму. И появился SaaS обфускатор и протектор .NET кода AppFuscator.com
Алгоритмы защиты
Renaming
Обфускация классов и их членов, с полной поддержкой Generics, наследования, перегрузки виртуальных методов, стандартных атрибутов обфускации.
Доступны различные варианты именования: английскими буквами, английскими буквами с перегрузкой по типу параметров, непечатаемыми символами и ряд других.
Assembly Merging
Объединение нескольких исходных защищаемых сборок в одну конечную, с целью усложнения анализа и декомпиляции.
В текущей версии в качестве встраиваемых сборок должны выступать сборки, не содержащие WPF ресурсов.
Decomposition
Декомпозиция структуры классов в процедурное представление — наша собственная оригинальная разработка, базирующаяся на идее перевода программы из объектно-ориентированной формы (простой для реверс-инжиниринга) к процедурному стилю, с максимальным уничтожением всей доступной информации хранимой в метаданных (но сохранением полной работоспособности сборщика мусора).
Расскажу чуть подробнее. Допустим, у нас есть сборка с такой структурой классов:
После выполнения декомпозиции мы увидим следующее:
Метод, который выделен на скриншоте — это и есть бывший 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);
Запускаем и вуаля:
Как видите, оба класса и оба метода в ходе обфускации были переименованы, но программа смогла выявить между ними зависимость, и обновила ссылку на имя в 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, анализатор смог адаптироваться и обновить запрашиваемое имя в IL коде.
Разумеется, такой подход будет работать не всегда, если конструкция будет более многошаговой или имя искомого метода будет браться, к примеру, из внешнего файла, то наш интеллигентный анализатор честно признает свое поражение.
Исходный код можно скачать здесь.
WPF Analyzing
Автоматическое исключение из обфускации типов, методов и полей, к которым имеются обращения из Windows Presentation Foundation. Программа производит декомпиляцию и анализ скомпилированных XAML ресурсов в сборках. Полностью поддерживает расширения синтаксиса WPF, включая сложные конструкции (например, PropertyPath и другие).
Благодаря этому, в программе, написанной на WPF, переименовано будет только то, что должно быть переименовано.
Типичный пример программы после обфускации:
(кликните для увеличения)
Почему SaaS?
Все просто — вместо того, чтобы покупать хороший коробочный обфускатор за несколько тысяч долларов, а затем постоянно платить за покупку его обновлений (ведь технологии платформы .NET развиваются очень быстро), Вы приобретаете доступ к сервису онлайн обфускации. И после этого платите только за тот объем работы, который нужен лично Вам. Выпускаете версию раз в месяц – платите только за это.
Ценовая политика сейчас в стадии формирования, но я с уверенностью могу сказать о том, что условия будут вкусными. Кроме того, будет доступна бесплатная версия, функционал которой превосходит все бесплатные обфускаторы, представленные на данный момент.
Это безопасно?
Мы очень серьезно подходим к вопросу защиты Ваших данных.
Первое и самое главное: Вам не нужно отправлять на сервер исходные коды программ. Вы присылаете только уже скомпилированные сборки, именно в таком виде, как их увидели бы Ваши пользователи (а возможно и не только пользователи), если бы Вы не занимались вопросом защиты и не обфусцировали их. Никакой другой информации не потребуется.
Весь процесс обфускации производится на выделенных серверах, доступ к которым имеет только несколько человек из нашей команды. Разумеется, мы используем последние версии ПО и производим регулярные апдейты. Кроме того, модуль обфускации и web-фронтенд для взаимодействия с пользователем, физически размещены на отдельных серверах, что повышает защищенность и масштабируемость системы.
Мы являемся юридическим лицом, и готовы предоставить все необходимые документы, такие как договор на использование нашего сервиса и NDA.
Сейчас сервис запущен в режиме бета версии, и Вы можете тестировать весь функционал абсолютно бесплатно.
Автор: Anakonda