Configuring Obfuscation
With the package installed, every Babel Obfuscator feature is configured with MSBuild properties and items in the project file. This page shows the settings you will use most, feature by feature.
How Settings Reach Babel
The package defines one MSBuild property or item for each attribute of the Babel task, and passes them to the task when the CoreObfuscate target runs. Most properties have the same name as the task attribute and accept the same values as the corresponding command line switch. The ones that differ, such as BabelLicense, MergeInternalize or BabelRules, are listed in the Property Mapping table.
Settings go in a PropertyGroup, usually conditioned on the build configuration so that Debug builds stay readable:
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<StringEncryption>stream</StringEncryption>
<ControlFlowObfuscation>if=on;switch=on;call=on</ControlFlowObfuscation>
<ControlFlowIterations>3</ControlFlowIterations>
<ValueEncryption>true</ValueEncryption>
<ResourceEncryption>true</ResourceEncryption>
<DebuggingProtection>true</DebuggingProtection>
<VerboseLevel>2</VerboseLevel>
</PropertyGroup>Lists of files are expressed as items in an ItemGroup: BabelRules, MergeAssembly, EmbedAssembly, MapInFile and BabelPlugin.
Properties are evaluated before any target runs, so a value set in the project body applies to the whole build. To compute a setting from something that is only known while building, such as the resolved path of a package reference, set it inside the BeforeObfuscate target instead. See Overriding the Task Configuration.
Defaults Applied by the Package
Without any setting, a project that references the package is obfuscated as follows:
- Symbol renaming is on for types, methods, properties, fields, events and parameters, virtual members included, and namespaces are flattened. The
SymbolsRenamingproperty switches all of these at once; the individualObfuscateTypes,ObfuscateMethods,ObfuscateProperties,ObfuscateFields,ObfuscateEvents,ObfuscateParameters,VirtualFunctionsandFlattenNamespacesproperties override it one by one. - Every other feature is off until you enable it.
- Debug symbols are regenerated when the compiler produced a PDB next to the assembly (
GenerateDebugis set automatically), so obfuscated builds stay debuggable and stack traces can be decoded. - Assembly search directories are built from the project references, so Babel resolves every dependency the compiler saw.
- The Babel configuration file is ignored (
NoConfigurationistrue), so the build depends only on what the project declares. - A
babelRules.xmlfile in the project folder is picked up automatically. - The strong-name key resolved by MSBuild for the project (
KeyOriginatorFile, orKeyContainerName) is passed to Babel, so the obfuscated assembly is signed like the original.
To turn renaming off, for a test project or for a string-encryption-only pass:
<PropertyGroup>
<SymbolsRenaming>false</SymbolsRenaming>
</PropertyGroup>Obfuscation Rules
Rules exclude symbols from a feature or apply a feature to a set of symbols. There are three ways to provide them:
- Add a
babelRules.xmlfile to the project folder: it is used automatically. - Reference any number of rules files with
BabelRulesitems:
<ItemGroup>
<BabelRules Include="rules\renaming.xml" />
<BabelRules Include="rules\encryption.xml" />
</ItemGroup>- Write the rules inline in the
XmlRulesproperty, which is convenient for a small set of rules that belongs to the project:
<PropertyGroup>
<XmlRules>
<Rules>
<Rule name="rename public types" feature="renaming" exclude="false">
<Access>Public</Access>
<Pattern>*</Pattern>
</Rule>
</Rules>
</XmlRules>
</PropertyGroup>The Android Application example uses an inline rule to rename public types, and the Blazor Web App example uses a rules file to obfuscate the classes behind Razor pages. Rules can also be attached to the code with custom attributes.
String Encryption
StringEncryption enables string encryption and selects the algorithm, exactly like the --stringencryption switch: true uses the default algorithm, while xor, hash, stream or custom selects one. See Standard Algorithms for a comparison.
<PropertyGroup>
<StringEncryption>stream</StringEncryption>
</PropertyGroup>STREAM Ultimate is the algorithm to prefer for modern targets: it is fully managed, so it works with trimming, NativeAOT and on FIPS-constrained hosts, and it is verified from .NET Framework through .NET 10, Android, iOS and .NET MAUI included. On Android, iOS and MAUI projects the package already runs Babel after compilation and before packaging, which is where string encryption has to happen for the packaged app to carry the encrypted strings. Strings declared const cannot be encrypted; see Encryption of Const Strings.
Control Flow Obfuscation
ControlFlowObfuscation takes the same key-value list as --controlflow, and ControlFlowIterations the number of passes (ILIterations is accepted as an alias):
<PropertyGroup>
<ControlFlowObfuscation>goto=on;if=on;switch=on;case=on;call=on</ControlFlowObfuscation>
<ControlFlowIterations>3</ControlFlowIterations>
</PropertyGroup>Version 12 adds the Chained State algorithm Ultimate as a flattening transform hardened against automated deobfuscators. Enable it with chain=on, alone or together with the other algorithms:
<PropertyGroup>
<ControlFlowObfuscation>switch=on;case=on;chain=on</ControlFlowObfuscation>
</PropertyGroup>Flattening has a runtime cost. Use rules to keep it off hot methods, as explained in Performance & Tuning.
Code Encryption
MsilEncryption encrypts method bodies. true encrypts every eligible method, while a regular expression restricts encryption to the methods whose full name matches it:
<PropertyGroup>
<MsilEncryption>MyApp\.Licensing\..*</MsilEncryption>
</PropertyGroup>See Code Encryption for the runtime behaviour, password-protected code and the methods that cannot be encrypted.
Value, Resource and Call Protection
<PropertyGroup>
<ValueEncryption>array=true;true</ValueEncryption>
<ResourceEncryption>true</ResourceEncryption>
<DynamicProxy>all</DynamicProxy>
<SuppressIldasm>true</SuppressIldasm>
<SuppressReflection>true</SuppressReflection>
</PropertyGroup>Each property accepts the values of the corresponding switch: see Value and Array Encryption, Resource Encryption and Dynamic Proxy.
Tampering Detection and Anti-Debugging
<PropertyGroup>
<TamperingDetection>true</TamperingDetection>
<DebuggingProtection>true</DebuggingProtection>
</PropertyGroup>On desktop targets the tampering check hashes the loaded image in memory, so it is inert in single-file, trimmed or AOT-published applications. The package forwards the project’s PublishSingleFile setting to Babel, which warns when tampering detection is requested for such a publish. See Tampering Detection and Anti-Debugging.
Android and iOS package integrity Ultimate
Starting with version 12, on .NET for Android and iOS targets (.NET MAUI included) tampering detection verifies the integrity of the package instead of the image: the APK signing certificate on Android, the bundle identifier and the Apple Team ID on iOS. Pin the expected values with the properties added in this version:
<!-- Android: SHA-256 fingerprint of the release signing certificate -->
<PropertyGroup Condition="$(TargetFramework.Contains('-android'))">
<TamperingDetection>true</TamperingDetection>
<TrustedSigner>2924C53EE9C511E9F26E0720FD8151064F7621681667D7AB1899E551CDB25104</TrustedSigner>
</PropertyGroup>
<!-- iOS: bundle identifier and Apple Team ID -->
<PropertyGroup Condition="$(TargetFramework.Contains('-ios'))">
<TamperingDetection>true</TamperingDetection>
<TrustedBundle>com.mycompany.myapp</TrustedBundle>
<TrustedTeam>ABCDE12345</TrustedTeam>
</PropertyGroup>TrustedSigner accepts several fingerprints separated by semicolons, for example an upload key and the Google Play app-signing key. How to obtain each value, and what happens when a pin is missing, is explained in Android (MAUI) Package Integrity and iOS (MAUI) Package Integrity. The Android Application example shows a complete Android project file.
Managed AES for FIPS Hosts
The runtime decryptors injected for code, string (XOR and HASH), value and resource encryption use the platform cryptographic provider, and a host with a broken OpenSSL FIPS configuration cannot start such an assembly. Since version 11.8, encryption=aesmanaged selects a self-contained managed decryptor instead:
<PropertyGroup>
<MsilEncryption>true</MsilEncryption>
<Use>encryption=aesmanaged</Use>
</PropertyGroup>Use accepts any key-value pair accepted by the --use switch, separated by semicolons, for example tagassembly=on. See FIPS Compliance for the trade-offs of the managed decryptor.
Merging and Embedding Assemblies
MergeAssembly and EmbedAssembly items list the assemblies to merge into, or embed in, the target assembly. MergeInternalize makes the merged public types internal, and MergeCopyAttributes controls whether their assembly-level attributes are copied:
<ItemGroup>
<MergeAssembly Include="$(TargetDir)Acme.View.dll" />
<MergeAssembly Include="$(TargetDir)Acme.ViewModel.dll" />
<EmbedAssembly Include="$(TargetDir)Framework.Mvvm.dll" />
</ItemGroup>
<PropertyGroup>
<MergeInternalize>true</MergeInternalize>
</PropertyGroup>Babel runs right after compilation, before the build copies dependencies to the output folder, so on a clean build a path under $(TargetDir) may not exist yet. The reliable source is the list of references resolved for the compiler, which you can filter inside the BeforeObfuscate target:
<Target Name="BeforeObfuscate">
<ItemGroup>
<MergeAssembly Include="@(ReferencePathWithRefAssemblies)"
Condition="'%(Filename)' == 'Acme.View' Or '%(Filename)' == 'Acme.ViewModel'" />
</ItemGroup>
</Target>The package also removes merged and embedded assemblies from the .deps.json file and from the publish set, so dotnet publish does not ship them twice. See Merge and Embed for the feature and Publishing for the build-side details.
Map Files and Cross-Assembly Renaming
GenerateMapOutFile writes the XML map file of the obfuscated assembly, used to decode stack traces and to rename the public interface of a library consistently across assemblies. MapInFile items feed the map files of already obfuscated assemblies back to Babel:
<PropertyGroup>
<GenerateMapOutFile>true</GenerateMapOutFile>
<BabelMapOutFile>$(SolutionDir)MapOut\$(TargetFileName).map.xml</BabelMapOutFile>
</PropertyGroup>
<ItemGroup>
<MapInFile Include="$(SolutionDir)MapOut\Acme.Core.dll.map.xml" />
</ItemGroup>See XML Map Files and Cross Assembly Renaming. The Unit Tests example uses a map file to run tests against an obfuscated library, and the Publish .NET App example obfuscates the public interface of a NuGet dependency and feeds its map file to the application.
Optimizations
<PropertyGroup>
<DeadCodeElimination>true</DeadCodeElimination>
<SealClasses>true</SealClasses>
<EnumRemoval>true</EnumRemoval>
<ConstRemoval>true</ConstRemoval>
<DisgregateRemoval>true</DisgregateRemoval>
<InlineExpansion>true</InlineExpansion>
<CleanAttributes>true</CleanAttributes>
</PropertyGroup>Each optimization is described in Optimizations.
Plugins
BabelPlugin items reference the plugin assemblies to load, and PluginsArguments passes key-value arguments to them. The BabelEncrypt plugin ships inside the package, in the same folder as the build tools:
<ItemGroup>
<BabelPlugin Include="$(BabelTaskDir)BabelEncrypt.dll" />
</ItemGroup>
<PropertyGroup>
<PluginsArguments>dictionary=exclusionlist.txt</PluginsArguments>
</PropertyGroup>See Babel Obfuscator Plugins and the Encrypt Plugin.
Logging and Diagnostics
<PropertyGroup>
<VerboseLevel>3</VerboseLevel>
<GenerateLogFile>true</GenerateLogFile>
<BabelLogFile>$(IntermediateOutputPath)babel.log</BabelLogFile>
<BabelProvideCommandLineArgs>true</BabelProvideCommandLineArgs>
<ShowStatistics>true</ShowStatistics>
<Trace>MyApp\.Services\..*</Trace>
<BabelWarningsToIgnore>W00008</BabelWarningsToIgnore>
<BabelWarningsAsErrors>W00000</BabelWarningsAsErrors>
</PropertyGroup>VerboseLevelcontrols how much of the Babel log reaches the build output;GenerateLogFileandBabelLogFilesave the complete log to a file.BabelProvideCommandLineArgsprints the command line the package passes to Babel and stores it in the@(BabelCommandLineArgs)item.Tracetakes a regular expression and explains, symbol by symbol, why a match was or was not obfuscated: the fastest way to debug rules.BabelWarningsToIgnore,BabelWarningsAsErrorsandBabelWarningsAsInfostake lists of warning codes and change how they are reported.MakeBabelProjectFilewrites an MSBuild project equivalent to the current configuration, which is useful to reproduce a build with the Babel task or the command line tool.
Every property accepted by the package is listed in the Package Reference.