A1ForceMauiObfuscate

Documentation

Install it, then point it at a build.

Two packages on NuGet. The tool rewrites an assembly that is already built. The same guide ships as the package readme.

Install

A project cannot reference a DotnetTool package, so the command and the MSBuild import are separate. Install 1.0.2 or later. Version 1.0.0 is not a .NET tool, and 0.0.1 does not check a licence.

dotnet tool install --global A1Force.MauiObfuscate

The command is maui-obfuscate. It is not dotnet maui-obfuscate unless you installed a local tool. Do not add a PackageReference to A1Force.MauiObfuscate.

Licence key

Obfuscation requires a key. init, --help, and --version do not. A key lasts one year. The price is USD $999 per year. Subscribe and the key is emailed, with these setup steps, after Stripe confirms the payment. A renewal emails a new key.

export A1FORCE_LICENCE_KEY="<key>"
maui-obfuscate --project path/to/App.csproj

On Windows PowerShell, set $env:A1FORCE_LICENCE_KEY. A one-off run can pass --licence-key. Leave the key out of the project file so it does not land in the build log.

Settings file

init writes maui-obfuscation.json next to the project. It does not overwrite an existing file unless you pass --force.

maui-obfuscate init --project path/to/App.csproj

ViewModels, models, and services are renamed. Views, XAML, platforms, resources, generated files, and startup files are not. An exclude always wins. defaultInclude is false, so a file that matches nothing stays.

Protections

init turns all three on. Set a flag to false to skip that pass. They run after renaming, including on dependencies you included.

"protections": {
  "controlFlowFlattening": true,
  "packing": true,
  "antiDebug": true
}

Control-flow flattening

Each basic block sets a state and jumps back to a switch. The method still returns the same values. Methods with an exception handler, a pinned local, or a branch that leaves a value on the stack are left unchanged. The result is ordinary IL, so MAUI AOT, including iOS, can still compile it.

String packing

Each ldstr becomes a call that inflates one compressed table and returns the original string. The plaintext is not stored in the assembly. XAML, resx, images, and fonts are not packed.

Anti-debug

A module initializer calls Debugger.IsAttached and, when that is true, Environment.FailFast. The same assembly runs on Windows, Mac, Linux, Android, and iOS. Close the debugger before launching a protected build.

Dependencies

dependencies names assemblies that sit beside the app output. The tool renames them with the same file groups and updates callers. An entry with "include": false wins over a broader include. A glob does not select strong-named framework assemblies.

"dependencies": [
  { "name": "MyCompany.Core", "include": true },
  { "name": "MyCompany.*", "include": true },
  { "name": "ThirdParty.LeaveAlone", "include": false }
]

Each selected assembly needs its portable PDB beside it. The tool parks MyCompany.Core.dll as MyCompany.Core.dll.original, then writes the obfuscated bytes back to MyCompany.Core.dll. Callers still load that file name. Close the running app before building so those DLLs are not locked.

What the pass writes

  • App.dll — obfuscated bytes, original file name
  • App.dll.original and App.pdb.original — the build, before this pass
  • App.obfuscated.rename-map.json — original names to short names, for stack traces

The same trio is written for each dependency that was renamed. A later build replaces App.dll; the next pass parks that new build as .original again. The output has no PDB.

MSBuild

Reference the Build package from the application project. NuGet imports the targets, so the project does not need an Import. Install the tool as well. The target runs after Build, and only for Release.

<PackageReference Include="A1Force.MauiObfuscate.Build" Version="1.0.2" />

Set A1FORCE_LICENCE_KEY in that build's environment. MauiObfuscateAfterBuild defaults to true. The global shim is %USERPROFILE%\.dotnet\tools\maui-obfuscate.exe on Windows and $HOME/.dotnet/tools/maui-obfuscate on macOS and Linux.

Names that stay

XAML and resources are not edited, so names the loader looks up have to remain.

  • CreateMauiApp, Main, and the entry-point type
  • The public surface of an x:Class type, and its x:Name fields
  • Virtual methods, overrides, interface methods, constructors, and enum fields
  • Anything marked [Obfuscation(Exclude = true)]

A public property on an included view model is renamed. If a binding looks that name up, turn the group off or exclude the member. Build with a portable PDB beside the DLL. The SDK already does this for current .NET, including Release.