Protegendo o código .Net da engenharia reversa com ConfuserEx 0.6.0

O artigo descreve a experiência do uso de combate do obfuscator ConfuserEx 0.6.0 para proteger o serviço .Net no Windows e no Mono. Foi em 2016, mas acho que o tema não perdeu a relevância agora.



ConfuserEx ( http://yck1509.github.io/ConfuserEx/ ) é um dos ofuscadores de código aberto gratuitos para .Net. Suporta Windows .NET Framework e Mono.

Contém um grande número de módulos que implementam vários métodos de proteção de código (renomeação, ofuscação do fluxo de execução, criptografia de recursos e constantes, proteção contra depuração e criação de perfil, wrappers). ConfuserEx permite que você estenda a funcionalidade escrevendo seus próprios módulos de segurança.



O código-fonte aberto permite que você modifique o sistema de proteção, altere a assinatura do ofuscador, dificultando assim os programas de desofuscador e a engenharia reversa manual.



Documentação



O projeto possui uma documentação bastante detalhada no formato WiKi .



Interface de usuário



ConfuserEx oferece suporte à interface do usuário e modo de linha de comando.



Modo de linha de comando



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.


Arquivo de projeto



A estrutura do arquivo do projeto está descrita na documentação . O arquivo de projeto contém uma descrição dos conjuntos que precisam ser protegidos, as configurações dos módulos de proteção e as regras pelas quais os módulos de proteção são aplicados para proteger os conjuntos.



As regras permitem que você aplique seletivamente (ou não) módulos de proteção a diferentes partes do código. Durante a ofuscação, a regra é verificada para aplicabilidade ao elemento de código protegido atual. Nesse caso, é avaliada a conformidade da chamada assinatura do elemento de código com a regra. As regras podem conter expressões lógicas complexas .



.



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




Para todos os elementos de código que começam com ShellCommand , usando as regras no arquivo de projeto, o módulo de Proteção de Nome foi desabilitado (os nomes são preservados). Você pode ver os resultados do módulo de Proteção de Constantes .



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