Protéger le code .Net de l'ingénierie inverse avec ConfuserEx 0.6.0

L'article décrit l'expérience de l'utilisation au combat de l'obfuscateur ConfuserEx 0.6.0 pour protéger le service .Net sous Windows et Mono. C'était en 2016, mais je pense que le sujet n'a pas perdu de sa pertinence maintenant.



ConfuserEx ( http://yck1509.github.io/ConfuserEx/ ) est l'un des obfuscateurs open source gratuits pour .Net. Prend en charge Windows .NET Framework et Mono.

Contient un grand nombre de modules qui implémentent diverses méthodes de protection du code (changement de nom, obfuscation du flux d'exécution, chiffrement des ressources et des constantes, protection contre le débogage et le profilage, wrappers). ConfuserEx vous permet d'étendre les fonctionnalités en écrivant vos propres modules de sécurité.



Le code open source vous permet de modifier le système de protection, de changer la signature de l'obfuscateur, rendant ainsi difficile les programmes de désobscurcissement et la rétro-ingénierie manuelle.



Documentation



Le projet dispose d'une documentation assez détaillée au format WiKi .



Interface utilisateur



ConfuserEx prend en charge le travail en mode UI ainsi qu'en mode ligne de commande.



Mode ligne de commande



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.


Dossier de projet



La structure du fichier projet est décrite dans la documentation . Le fichier de projet contient une description des assemblys qui doivent être protégés, les paramètres des modules de protection et les règles selon lesquelles les modules de protection sont appliqués pour protéger les assemblys.



Les règles vous permettent d'appliquer de manière sélective (ou non) des modules de protection à différentes parties du code. Pendant l'obscurcissement, l'applicabilité de la règle à l'élément de code protégé actuel est vérifiée. Dans ce cas, la conformité de la soi-disant signature de l'élément de code avec la règle est évaluée . Les règles peuvent contenir des expressions logiques complexes .



.



<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:]'";




Pour tous les éléments de code commençant par ShellCommand , en utilisant les règles du fichier projet, le module Protection de nom a été désactivé (les noms sont conservés). Les résultats du module de protection des constantes sont visibles .



internal sealed class _ob : _qA
{
  private readonly string ShellCommandNetworkAdapterMACAddress = \u003CModule\u003E.\u206E‬‌‭‭‬‏‮‬‬‪‮‫‭‌‌‬‭‪‎‮<string>(3331371713U);
  private readonly string ShellCommandNetworkAdapterCaption = \u003CModule\u003E.\u206F‌‬‍‫‭‍‌‏‪‬‫‎‭‎‮‮<string>(4243712535U);</source>



All Articles