Упаковка, Сжатие И Защита Сборок

Последний выпуск статьи доступен на сайте makeloft.xyz. В материалах данной статьи описаны механизмы раскладки, сжатия, динамического нагружения, а также способы элементарной защиты.

.

СЕТЬ -сборки с использованием стандартных инструментов среды разработки Визуальная Студия .

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

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

Иногда после установки приложения вообще отказываются запускаться, и причину выявить довольно сложно.

Но, обладая определенными знаниями, вы можете выполнить весь процесс самостоятельно с полным контролем на каждом этапе и возможностью отладки уже упакованных сборок.



Упаковка, сжатие и защита сборок

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

Поэт .

Внешне программа представляет собой единый Exe -файл небольшого размера, хотя на самом деле состоит из нескольких библиотек.

Стоит сразу отметить, что упаковка сборок имеет множество преимуществ при отсутствии явных недостатков: • множество сборок можно объединить в одну и даже включить в исполняемый файл.

Приложение становится монолитным и стабильным, ведь ни одна его часть никуда не потеряется.

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

• Скорость запуска приложений заметно увеличится! Субъективно, Поэт стал запускаться в 2-4 раза быстрее на разных машинах, после связки всех файлов в один.

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

• появляются удобные возможности для более серьезной защиты.

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

• возможность отладки сборок сохраняется! 1. Динамическая загрузка сборок в домен приложения.

Если файлы сборки добавляются в отдельный файл ресурсов, например, Сборки.

resx , то они будут просто массивом байтов.

Загрузить их в домен приложения очень легко с помощью небольшого класса.

Загрузчик сборок представлено чуть ниже.

Достаточно в нужный момент, обычно при запуске приложения, вызвать

  
  
  
  
  
   

AssemblyLoader.ResolveAssemblies<Assemblies>(AppDomain.CurrentDomain);



using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace Loader { public static class AssemblyLoader { public static void ResolveAssembly(this AppDomain domain, string assemblyFullName) { try { domain.CreateInstance(assemblyFullName, "AnyType"); } catch (Exception exception) { Console.WriteLine(exception.Message); } } public static void ResolveAssemblies<TResource>(this AppDomain domain, Func<byte[], byte[]> converter = null) { var assemblies = ResolveAssembliesFromStaticResource<TResource>(converter); ResolveAssemblies(domain, assemblies); } public static void ResolveAssemblies(this AppDomain domain, List<Assembly> assemblies) { ResolveEventHandler handler = (sender, args) => assemblies.Find(a => a.FullName == args.Name); domain.AssemblyResolve += handler; assemblies.ForEach(a => ResolveAssembly(domain, a.FullName)); domain.AssemblyResolve -= handler; } public static void ResolveAssembly(this AppDomain domain, Assembly assembly) { ResolveEventHandler handler = (sender, args) => assembly; domain.AssemblyResolve += handler; ResolveAssembly(domain, assembly.FullName); domain.AssemblyResolve -= handler; } public static List<Assembly> ResolveAssembliesFromStaticResource<TResource>(Func<byte[], byte[]> converter = null) { var assemblyDatyType = typeof (byte[]); var assemblyDataItems = typeof (TResource) .

GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) .

Where(p => p.PropertyType == assemblyDatyType) .

Select(p => p.GetValue(null, null)) .

Cast<byte[]>() .

ToList(); var assemblies = new List<Assembly>(); foreach (var assemblyData in assemblyDataItems) { try { var rawAssembly = converter == null ? assemblyData : converter(assemblyData); var assembly = Assembly.Load(rawAssembly); assemblies.Add(assembly); } catch (Exception exception) { Console.WriteLine(exception.Message); } } return assemblies; } } }

Автор статьи долго не мог понять, как инициировать процесс загрузки сборки в домен.

При попытке создать неизвестный тип вместо вызова события домен.

AssemblyResolve += обработчик; исключения просто падали, а само событие не вызывалось.

А главное волшебство, как оказалось, в одной строчке домен.

CreateInstance(assemblyFullName, «AnyType»); , в свое время потребовалось несколько дней, проведенных в обнимку с MSDN , чтобы найти на заднем плане пример, раскрывающий эту тайну.

2. При этом никто не запрещает сжимать или шифровать массивы байтов Очень простой класс может быть полезен для сжатия.

Компрессор .

Только теперь нужно инициировать загрузку сборок немного по-другому

AssemblyLoader.ResolveAssemblies<Resources>( AppDomain.CurrentDomain, bytes => Foundation.Compressor.ConvertBytes(bytes));



using System.IO; using System.IO.Compression; namespace Foundation { public static class Compressor { public static void ConvertFile(string inputFileName, CompressionMode compressionMode, string outputFileName = null) { outputFileName = outputFileName ?? (compressionMode == CompressionMode.Compress ? inputFileName + ".

compressed" : inputFileName.Replace(".

compressed", string.Empty)); var inputFileBytes = File.ReadAllBytes(inputFileName); ConvertBytesToFile(inputFileBytes, outputFileName, compressionMode); } public static void ConvertBytesToFile(byte[] inputFileBytes, string outputFileName, CompressionMode compressionMode = CompressionMode.Decompress) { var bytes = ConvertBytes(inputFileBytes, compressionMode); File.WriteAllBytes(outputFileName, bytes); } public static byte[] ConvertBytes(byte[] inputFileBytes, CompressionMode mode = CompressionMode.Decompress) { using (var inputStream = new MemoryStream(inputFileBytes)) using (var outputStream = new MemoryStream()) { if (mode == CompressionMode.Compress) using (var convertStream = new GZipStream(outputStream, CompressionMode.Compress)) inputStream.CopyTo(convertStream); if (mode == CompressionMode.Decompress) using (var convertStream = new GZipStream(inputStream, CompressionMode.Decompress)) convertStream.CopyTo(outputStream); var bytes = outputStream.ToArray(); return bytes; } } } }

Также нам понадобится базовая консольная утилита для архивирования.



using System; using System.IO.Compression; using System.Linq; using Foundation; namespace FileCompressor { class Program { private static void Main(string[] args) { try { args.Where(n => !n.ToLower().

Contains(".

compressed")) .

ToList() .

ForEach(n => Compressor.ConvertFile(n, CompressionMode.Compress)); args.Where(n => n.ToLower().

Contains(".

compressed")) .

ToList() .

ForEach(n => Compressor.ConvertFile(n, CompressionMode.Decompress)); } catch (Exception exception) { Console.WriteLine(exception.Message); Console.ReadKey(); } } } }

Визуальная Студия позволяет выполнить произвольную командную строку до и после компиляции проекта.

Это настраивается в свойствах проекта.

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

При правильной настройке все будет отлично отлажено, так как залитая в домен версия соответствует скомпилированной.

Это важно, поскольку если приложение перестает работать после сборки или возникают другие проблемы, вы можете легко отладить причину и устранить ее.

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

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

Сжатие нескольких файлов вместе более эффективно, чем сжатие каждого файла по отдельности.

Все ключи даны в статье, вам остается только разобраться и применить знания в своих проектах.

Но если возникнут трудности, есть возможность купить исходные коды редактора Поэт .

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

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

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

А если максимально усложнить алгоритм расшифровки, то постороннему человеку будет не так просто вытащить расшифрованные сборки.

Сама расшифровка может выполняться в собственном коде C++, а для веб-приложений вы можете получать ключи расшифровки с сервера.

Остаются две проблемы — процессы операционной системы можно отладить или снять с них дамп памяти.

Защититься от отладчика не сложно

static class AntiDebugger { private static int Fails { get; set; } private static DateTime TimeStamp { get; set; } public static void Run() { TimeStamp = DateTime.Now; new Thread(ProtectThread).

Start(); //new Thread(ProtectThread).

Start(); } private static void ProtectThread() { while (true) { var now = DateTime.Now; if (TimeStamp.AddSeconds(5) < now) Fails++; else { TimeStamp = now; Fails = 0; } // One fail may attend when pc wake up from the sleep mode if (Fails >= 2) InitProtection(); Thread.Sleep(TimeSpan.FromSeconds(3)); } } private static void InitProtection() { Fails = 0; Console.WriteLine("Debugger detected!"); //Process.GetCurrentProcess().

Kill(); //Process.GetCurrentProcess().

PriorityClass = ProcessPriorityClass.High; //for (var i = 0; i < 100; i++) //{ // new Thread(() => { while (true) ; }) {Priority = ThreadPriority.Highest}.

Start(); //} } }

Один или пара потоков проверяют и обновляют переменную TimeStamp через указанный интервал.

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

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

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

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

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

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

Полученные результаты Надеюсь, что статья будет полезна разработчикам.

Еще раз повторю, что в нем хорошо описаны все ключевые моменты, остается только применить их на практике.

Если вдруг возникнут трудности, вы всегда можете приобрести полные исходники редактора.

Поэт с сайта makeloft.by .

P.S. Информация за денежные пожертвования и благодарности.

П.

П.

С.

Предварительная версия бесплатной библиотеки Основа Фонда .

Теги: #.

NET #сборки #C++ #программирование #.

NET #C++

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.