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>
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>
