Защита .Net кода от реверс инженеринга с помощью ConfuserEx 0.6.0

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

В статье рассказывается об опыте боевого применения обфускатора ConfuserEx 0.6.0 для защиты сервиса .Net под Windows и Mono. Дело было в далеком 2016 году, но, я думаю, тема не потеряла актуальность и сейчас.


ConfuserEx (http://yck1509.github.io/ConfuserEx/) один из бесплатных обфускаторов для .Net с открытым исходным кодом. Поддерживает работу в среде Windows .NET Framework и Mono.
Содержит большое число модулей, реализующих различные методы защиты кода (переименование, запутывание потока выполнения, шифрование ресурсов и констант, защита от отладки и профилирования, упаковщики). ConfuserEx дает возможность расширения функциональности, путем написания собственных модулей защиты.


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


Документация


У проекта имеется достаточно подробная документация в формате WiKi.


Пользовательский интерфейс


ConfuserEx поддерживает работу в UI режиме, а так же режиме командной строки.


Режим командной строки


ConfuserEx\bin\Confuser.CLI.exe
ConfuserEx.CLI: No input files specified.
Usage:
Confuser.CLI -n|noPause <project configuration>
Confuser.CLI -n|noPause -o|out=<output directory> <modules>
    -n|noPause : no pause after finishing protection.
    -o|out     : specifies output directory.
    -probe     : specifies probe directory.
    -plugin    : specifies plugin path.
    -debug     : specifies debug symbol generation.</source>

ConfuserEx\bin\Confuser.CLI.exe -n LicenseManagerService.crproj

 [INFO] ConfuserEx v0.6.0-custom Copyright (C) Ki 2014
 [INFO] Running on Microsoft Windows NT 6.1.7601 Service Pack 1, .NET Framework v4.0.30319.0, 64 bits
[DEBUG] Discovering plugins...
 [INFO] Discovered 10 protections, 1 packers.
[DEBUG] Resolving component dependency...
 [INFO] Loading input modules...
 [INFO] Loading 'LicenseManagerService\bin\x86\Release\LicenseManagerService.exe'...
 [INFO] Initializing...
[DEBUG] Building pipeline...
 [INFO] Resolving dependencies...
[DEBUG] Checking Strong Name...
[DEBUG] Creating global .cctors...
[DEBUG] Executing 'Name analysis' phase...
[DEBUG] Building VTables & identifier list...
[DEBUG] Analyzing...
 [INFO] Processing module 'LicenseManagerService.exe'...
[DEBUG] Executing 'Invalid metadata addition' phase...
[DEBUG] Executing 'Renaming' phase...
[DEBUG] Renaming...
[DEBUG] Executing 'Anti-debug injection' phase...
[DEBUG] Executing 'Anti-dump injection' phase...
[DEBUG] Executing 'Anti-ILDasm marking' phase...
[DEBUG] Executing 'Encoding reference proxies' phase...
[DEBUG] Executing 'Constant encryption helpers injection' phase...
[DEBUG] Executing 'Resource encryption helpers injection' phase...
[DEBUG] Executing 'Constants encoding' phase...
[DEBUG] Executing 'Anti-tamper helpers injection' phase...
[DEBUG] Executing 'Control flow mangling' phase...
[DEBUG] Executing 'Post-renaming' phase...
[DEBUG] Executing 'Anti-tamper metadata preparation' phase...
[DEBUG] Executing 'Packer info extraction' phase...
 [INFO] Writing module 'LicenseManagerService.exe'...
[DEBUG] Encrypting resources...
 [INFO] Finalizing...
 [INFO] Packing...
[DEBUG] Encrypting modules...
 [INFO] Protecting packer stub...
[DEBUG] Discovering plugins...
 [INFO] Discovered 11 protections, 1 packers.
[DEBUG] Resolving component dependency...
 [INFO] Loading input modules...
 [INFO] Loading 'LicenseManagerService\bin\x86\Release\LicenseManagerService.exe'...
 [INFO] Initializing...
[DEBUG] Building pipeline...
 [INFO] Resolving dependencies...
[DEBUG] Checking Strong Name...
[DEBUG] Creating global .cctors...
[DEBUG] Executing 'Name analysis' phase...
[DEBUG] Building VTables & identifier list...
[DEBUG] Analyzing...
 [INFO] Processing module 'LicenseManagerService.exe'...
[DEBUG] Executing 'Packer info encoding' phase...
[DEBUG] Executing 'Invalid metadata addition' phase...
[DEBUG] Executing 'Renaming' phase...
[DEBUG] Renaming...
[DEBUG] Executing 'Anti-debug injection' phase...
[DEBUG] Executing 'Anti-dump injection' phase...
[DEBUG] Executing 'Anti-ILDasm marking' phase...
[DEBUG] Executing 'Encoding reference proxies' phase...
[DEBUG] Executing 'Constant encryption helpers injection' phase...
[DEBUG] Executing 'Resource encryption helpers injection' phase...
[DEBUG] Executing 'Constants encoding' phase...
[DEBUG] Executing 'Anti-tamper helpers injection' phase...
[DEBUG] Executing 'Control flow mangling' phase...
[DEBUG] Executing 'Post-renaming' phase...
[DEBUG] Executing 'Anti-tamper metadata preparation' phase...
[DEBUG] Executing 'Packer info extraction' phase...
 [INFO] Writing module 'LicenseManagerService.exe'...
[DEBUG] Encrypting resources...
 [INFO] Finalizing...
[DEBUG] Saving to 'C:\Users\pash76\AppData\Local\Temp\ehwkjzxt.brh\mqqtgvji.gxk\LicenseManagerService\bin\x86\Release\LicenseManagerService.exe'...
[DEBUG] Executing 'Export symbol map' phase...
 [INFO] Finish protecting packer stub.
[DEBUG] Saving to 'D:\pash76\Develop\License_manager\Confused\LicenseManagerService\bin\x86\Release\LicenseManagerService.exe'...
[DEBUG] Executing 'Export symbol map' phase...
 [INFO] Done.
Finished at 9:35, 0:03 elapsed.

Файл проекта


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


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


Альтернативой правил является декларативный способ пометкой кода атрибутами.


<project outputDir=".\Confused" baseDir=".\" xmlns="http://confuser.codeplex.com">
    <rule pattern="true" inherit="false">
        <protection id="anti ildasm" />
        <protection id="anti tamper" action="remove" />
        <protection id="constants">
            <argument name="mode" value="dynamic" />
            <argument name="decoderCount" value="13" />
            <argument name="elements" value="SIP" />
            <argument name="cfg" value="false" />
        </protection>
        <protection id="ctrl flow" />
        <protection id="anti dump" action="remove" />
        <protection id="anti debug" />
        <protection id="invalid metadata" action="remove" />
        <protection id="ref proxy" />
        <protection id="resources">
            <argument name="mode" value="dynamic" />
        </protection>
        <protection id="rename">
            <argument name="mode" value="sequential" />
        </protection>
    </rule>
    <packer id="compressor">
        <argument name="key" value="dynamic" />
        <argument name="compat" value="true" />
    </packer>
    <module path="LicenseManagerService\bin\x86\Release\LicenseManagerService.exe">
        <rule pattern="
            match('UAVLicenseManager\.CentOSSystemInfoProvider::ShellCommand.*')

            or match(' ?LicenseManagerService\.Program(::)?')
            or match(' ?LicenseManagerService\.UAVLicenseManagerService(::)?')

            or (
                match(' ?UAVLicenseManager\.License(::)?') 
                and is-public() 
                and (member-type('type') or member-type('field') or member-type('property'))
                )
            or match(' ?UAVLicenseManager\.LicenseKey(::)?')
            or match(' ?UAVLicenseManager\.LicenseOwner(::)?')
            or match(' ?UAVLicenseManager\.Location(::)?')
            or match(' ?UAVLicenseManager\.LicenseLimit(::)?')
            " inherit="true">
            <protection id="rename" action="remove" />
        </rule>
    </module>
</project>

Модули защиты


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


Защита Name Protection нарушает работу механизмов Reflection и сериализации. Типы, которые необходимо сериализовывать или обрабатывать с использованием рефлексии необходимо добавлять в исключения с помощью правил или с помощью атрибутов.


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


Модуль Windows Mono
Anti Debug Protection
Anti Dump Protection
Anti IL Dasm Protection
Anti Tamper Protection
Compressor (в режиме совместимости)
Constants Protection (режим cfg не поддерживается)
Control Flow Protection
Invalid Metadata Protection
Name Protection
Reference Proxy Protection
Resources Protection

Упаковщики


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



Кастомизация защиты


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


ConfuserEx\src\Confuser.Core\ConfuserEngine.cs


static void Inspection(ConfuserContext context) {
    context.Logger.Info("Resolving dependencies...");
    foreach (var dependency in context.Modules
                                      .SelectMany(module => module.GetAssemblyRefs().Select(asmRef => Tuple.Create(asmRef, module)))) {
        try {
            AssemblyDef assembly = context.Resolver.ResolveThrow(dependency.Item1, dependency.Item2);
        }
        catch (AssemblyResolveException ex) {
            context.Logger.ErrorException("Failed to resolve dependency of '" + dependency.Item2.Name + "'.", ex);
            throw new ConfuserException(ex);
        }
    }
    context.Logger.Debug("Checking Strong Name...");
    foreach (ModuleDefMD module in context.Modules) {
        var snKey = context.Annotations.Get<StrongNameKey>(module, Marker.SNKey);
        if (snKey == null && module.IsStrongNameSigned)
            context.Logger.WarnFormat("[{0}] SN Key is not provided for a signed module, the output may not be working.", module.Name);
        else if (snKey != null && !module.IsStrongNameSigned)
            context.Logger.WarnFormat("[{0}] SN Key is provided for an unsigned module, the output may not be working.", module.Name);
        else if (snKey != null && module.IsStrongNameSigned &&
                 !module.Assembly.PublicKey.Data.SequenceEqual(snKey.PublicKey))
            context.Logger.WarnFormat("[{0}] Provided SN Key and signed module's public key do not match, the output may not be working.", module.Name);
    }
    var marker = context.Registry.GetService<IMarkerService>();
    context.Logger.Debug("Creating global .cctors...");
    foreach (ModuleDefMD module in context.Modules) {
        TypeDef modType = module.GlobalType;
        if (modType == null) {
            modType = new TypeDefUser("", "<Module>", null);
            modType.Attributes = TypeAttributes.AnsiClass;
            module.Types.Add(modType);
            marker.Mark(modType, null);
        }
        MethodDef cctor = modType.FindOrCreateStaticConstructor();
        if (!marker.IsMarked(cctor))
            marker.Mark(cctor, null);
    }
    //context.Logger.Debug("Watermarking...");
    //foreach (ModuleDefMD module in context.Modules) {
    //    TypeRef attrRef = module.CorLibTypes.GetTypeRef("System", "Attribute");
    //    var attrType = new TypeDefUser("", "ConfusedByAttribute", attrRef);
    //    module.Types.Add(attrType);
    //    marker.Mark(attrType, null);
    //    var ctor = new MethodDefUser(
    //        ".ctor",
    //        MethodSig.CreateInstance(module.CorLibTypes.Void, module.CorLibTypes.String),
    //        MethodImplAttributes.Managed,
    //        MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
    //    ctor.Body = new CilBody();
    //    ctor.Body.MaxStack = 1;
    //    ctor.Body.Instructions.Add(OpCodes.Ldarg_0.ToInstruction());
    //    ctor.Body.Instructions.Add(OpCodes.Call.ToInstruction(new MemberRefUser(module, ".ctor", MethodSig.CreateInstance(module.CorLibTypes.Void), attrRef)));
    //    ctor.Body.Instructions.Add(OpCodes.Ret.ToInstruction());
    //    attrType.Methods.Add(ctor);
    //    marker.Mark(ctor, null);
    //    var attr = new CustomAttribute(ctor);
    //    attr.ConstructorArguments.Add(new CAArgument(module.CorLibTypes.String, Version));
    //    module.CustomAttributes.Add(attr);
    //}
}

Кроме того, рекомендуется изменить имя .net модуля ConfuserEx. По умолчанию используется имя koi.
Искать поиском по исходникам


ConfuserEx\src\Confuser.Protections\Compress\Compressor.cs
ConfuserEx\src\Confuser.Runtime\Compressor.cs
ConfuserEx\src\Confuser.Protections\Compress\ExtractPhase.cs
ConfuserEx\src\Confuser.Protections\Compress\StubProtection.cs

Примеры результатов


Сборка защищена без применения упаковщика (для возможности дизассемблера dotPeek). Файл проекта с настройками приведен выше.


Запутывание исполняемого кода


using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
[assembly: InternalsVisibleTo("LicenseManagerServiceTests")]
namespace LicenseManagerService
{
    static class Program
    {
        /// <summary>
        /// Главная точка входа для приложения.
        /// </summary>
        ///
        static void Main()
        {
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                new UAVLicenseManagerService()
            };
            ServiceBase.Run(ServicesToRun);
        }
    }
}

Дизассемблированый код в dotPeek



Для типа LicenseManagerService.Program, с помощью правил в файле проектов, был отключен модуль Name Protection (имена сохранились). Видны результаты работы модуля Control Flow Protection.


// Decompiled with JetBrains decompiler
// Type: LicenseManagerService.Program
// Assembly: LicenseManagerService, Version=1.0.5980.24716, Culture=neutral, PublicKeyToken=null
// MVID: A6EB17CC-65EE-4E2D-B66C-24E166429A4A
// Assembly location: D:\pash\Develop\License_manager\Confused\LicenseManagerService\bin\x86\Release\LicenseManagerService.exe
using System.Runtime.InteropServices;
using System.ServiceProcess;
namespace LicenseManagerService
{
  internal static class Program
  {
    private static void Main()
    {
      ServiceBase[] serviceBaseArray1 = new ServiceBase[1]
      {
        (ServiceBase) new UAVLicenseManagerService()
      };
label_1:
      int num1 = 1005209177;
      ServiceBase[] serviceBaseArray2;
      while (true)
      {
        int num2 = 1280737639;
        uint num3;
        switch ((num3 = (uint) (num1 ^ num2)) % 3U)
        {
          case 0U:
            goto label_1;
          case 1U:
            serviceBaseArray2 = serviceBaseArray1;
            num1 = (int) num3 * 1248105312 ^ 483770479;
            continue;
          default:
            goto label_4;
        }
      }
label_4:
      Program.\u200E‮‪‌‌‪‎‪‌‫‪‬‌‭‪‎‬‌‪‍‍‌‭‮(serviceBaseArray2);
    }
    static void \u200E‮‪‌‌‪‎‪‌‫‪‬‌‭‪‎‬‌‪‍‍‌‭‮([In] ServiceBase[] obj0)
    {
      ServiceBase.Run(obj0);
    }
  }
}

Защита констант в коде


private readonly string ShellCommandNetworkAdapterMACAddress =
    @"ip -o link show | grep -m 1 'UP.*LOWER_UP.*ether\|LOWER_UP.*UP.*ether' | sed -n 's/.*ether \(.*\) brd.*/\1/p' | tr -d '\n[:blank:]'";


Для всех элементов кода, начинающихся с ShellCommand, с помощью правил в файле проектов, был отключен модуль Name Protection (имена сохранились). Видны результаты работы модуля Constants Protection.


internal sealed class _ob : _qA
{
  private readonly string ShellCommandNetworkAdapterMACAddress = \u003CModule\u003E.\u206E‬‌‭‭‬‏‮‬‬‪‮‫‭‌‌‬‭‪‎‮<string>(3331371713U);
  private readonly string ShellCommandNetworkAdapterCaption = \u003CModule\u003E.\u206F‌‬‍‫‭‍‌‏‪‬‫‎‭‎‮‮<string>(4243712535U);</source>
Источник: https://habr.com/ru/post/524788/

Интересные статьи

Интересные статьи

Привет, Хабр! В Монолите весь код должен быть в едином стиле, a в разных микросервисах можно использовать разные подходы, языки программирования и фреймворки. Для простых микросервисов с 1 — 2 ...
Генераторы псевдослучайных чисел выдают числа детерминировано, но обычно такие числа выглядят непериодическими (случайными). По крайней мере, в большинстве случаев применения подобных чисел так о...
Устраивать конкурсы в инстаграме сейчас модно. И удобно. Инстаграм предоставляет достаточно обширный API, который позволяет делать практически всё, что может сделать обычный пользователь ручками.
Привет! Меня зовут Никита Учителев. Я представляю отдел Research & Development компании Lamoda. Нас 20+ человек, и мы работаем над различными рекомендациями на сайте и в приложениях, разрабат...
Это пошаговая инструкция по классификации мультиспектральных снимков со спутника Landsat 5. Сегодня в ряде сфер глубокое обучение доминирует как инструмент для решения сложных проблем, в том ...