One Obfuscated File: Merging Dependencies
Shipping an application together with its dependency DLLs gives an attacker more surface, and those libraries are usually not obfuscated at all. This example uses the Babel Obfuscator NuGet package to merge a referenced assembly into the host, internalize its public types and obfuscate the result, so you deliver a single self-contained file with no loose, readable libraries.
To use this example you need a site license for Babel Obfuscator (Ultimate, Server or Data Center editions). The source code is available on GitHub:
git clone https://github.com/babelfornet/single-file-merge-nuget-example.gitPaymentCore is an ordinary class library with a public API (src/PaymentCore/PaymentProcessor.cs)
that Checkout uses like any dependency:
// PaymentCore (the dependency)
public sealed class PaymentProcessor
{
public PaymentProcessor(decimal feeRate) => _feeRate = feeRate;
public decimal Charge(decimal amount) => amount + Math.Round(amount * _feeRate, 2);
}
// Checkout (the host)
var processor = new PaymentProcessor(feeRate: 0.029m);
Console.WriteLine(processor.Charge(100.00m)); // 102.90After the build, PaymentProcessor is an internal, renamed type inside Checkout.dll — there is no
PaymentCore.dll to ship. The Checkout console project references the library and folds it in with
two MSBuild properties in Release:
<ItemGroup>
<ProjectReference Include="..\PaymentCore\PaymentCore.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="Babel.Obfuscator" Version="12.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<MergeAssemblies>true</MergeAssemblies>
<Internalize>true</Internalize>
<StringEncryption>hash</StringEncryption>
<BabelWarningsAsErrors>W00000</BabelWarningsAsErrors>
</PropertyGroup>MergeAssemblies folds the referenced assemblies into the output, and Internalize changes the
merged public types to internal so they can be renamed and are no longer part of any public
surface. The Babel package runs the merge before the SDK’s later
steps (dependency resolution, trimming, single-file bundling) and keeps dotnet publish consistent by
updating the .deps.json file and the set of published files.
Building the sample in Release produces a single Checkout.dll — PaymentCore.dll is no longer
present because it was merged in — and the application runs from that one file with identical output.
If you would rather keep a dependency as an opaque encrypted blob than merge its types, use
EmbedAssemblies instead.