The article describes the experience of combat use of the ConfuserEx 0.6.0 obfuscator to protect the .Net service under Windows and Mono. It was back in 2016, but I think the topic has not lost its relevance now.
ConfuserEx ( http://yck1509.github.io/ConfuserEx/ ) is one of the free open source obfuscators for .Net. Supports Windows .NET Framework and Mono.
Contains a large number of modules that implement various methods of code protection (renaming, obfuscation of the execution flow, encryption of resources and constants, protection against debugging and profiling, wrappers). ConfuserEx allows you to extend functionality by writing your own security modules.
Open source code allows you to modify the protection system, change the signature of the obfuscator, thereby making it difficult for de-obfuscator programs and manual reverse engineering.
Documentation
The project has a fairly detailed documentation in the WiKi format .
User interface
ConfuserEx supports UI and command line mode.
Command line mode
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 file
The structure of the project file is described in the documentation . The project file contains a description of the assemblies that need to be protected, the settings of the protection modules, and the rules by which the protection modules are applied to protect the assemblies.
Rules allow you to selectively apply (or not apply) protection modules to different parts of the code. During obfuscation, the rule is checked for applicability to the current protected code element. In this case, the compliance of the so-called signature of the code element with the rule is evaluated . The rules can contain complex logical expressions .
<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:]'";
For all code elements starting with ShellCommand , using the rules in the project file, the Name Protection module has been disabled (names are preserved). You can see the results of the Constants Protection module .
internal sealed class _ob : _qA
{
private readonly string ShellCommandNetworkAdapterMACAddress = \u003CModule\u003E.\u206Eā¬āāāāÆā¬āāāā®ā¬ā¬ā¬ā¬ā«ā«āŖā®ā®ā«āāā®āā¬ā¬āŖāāāŖāāāŖā«ā®<string>(3331371713U);
private readonly string ShellCommandNetworkAdapterCaption = \u003CModule\u003E.\u206Fā«āā¬āÆā®āāā®ā®ā«ā¬ā«āÆā¬āāāŖā¬āāā¬āÆāÆāŖā¬āŖā«ā®āā¬āā«āā¬ā¬āā®āÆā®<string>(4243712535U);</source>