.NET MAUI lets you ship one C# codebase to Windows, Android, iOS and macOS. That's great for productivity — and it means your proprietary logic now travels to a lot of devices you don't control. If any of that code is worth protecting (a licensing check, a pricing rule, an algorithm), you should obfuscate a MAUI app the same way you would any client you distribute. Here's how, and the MAUI-specific traps to avoid.
Why a MAUI app is exposed
A MAUI app isn't magic native code — it's built from ordinary .NET assemblies, and how exposed they are depends on the platform you ship to:
-
Windows — your managed
.dllfiles ship in the package. Extract and decompile, and you get your C# back, exactly as covered in can .NET code be decompiled?. -
Android — the
.apk/.aabcontains your .NET assemblies (individually or bundled). They're readable once unpacked; a decompiler does the rest. - iOS / macOS (AOT) — these are ahead-of-time compiled to native code, which is harder to decompile than IL. That raises the bar, but it isn't a reason to leave the other platforms' assemblies wide open.
Bottom line: on the platforms that ship IL (Windows, Android), your MAUI app is as decompilable as any .NET assembly. Obfuscation is what closes that.
How to obfuscate a MAUI app with Nebula
The job is to obfuscate your app's managed assemblies as part of the build, before they're packaged. Point Nebula at your published output with a config tuned for MAUI:
dotnet publish -c Release -f net8.0-windows10.0.19041.0
nebula --config nebula.config.json --input publish/YourApp.dll
Wire it into MSBuild/CI so it runs on every release build. Full details — including per-platform notes — are in Protecting .NET MAUI & Xamarin apps.
The MAUI-specific gotchas
MAUI leans hard on reflection and naming at runtime, so a careless rename will break the app rather than protect it. Keep these safe:
- XAML data-binding targets. Bindings resolve view-model properties by name. If you rename a bound property, the binding silently fails. Preserve your view-models' public bound members (or the whole public API of view-model types).
- Dependency injection. Types you register and resolve via the DI container are constructed by type — keep those types' identities where the container needs them.
-
Reflection / serialization. Anything loaded by name — converters,
[ObservableProperty]-style generated members, JSON models — must be preserved, same as in WPF. - iOS is AOT. iOS does no runtime code generation, so method (IL) encryption won't work there — it needs the JIT. Use renaming, control-flow flattening, string encryption and code virtualization on iOS instead; they don't depend on the JIT.
The golden rule with any UI framework: obfuscate, then run the app on a real device and click through it. MAUI's binding failures are silent, so a quick manual pass (plus your UI tests) is how you confirm the protected build behaves identically.
What to protect
You don't need to virtualize your whole app — protect the code that is the product. Rename and flatten broadly, encrypt the strings (API endpoints, keys, messages love to hide in MAUI apps), and reserve heavier protection like code virtualization for the handful of methods an attacker would target first — a license validator, a key-derivation step, a proprietary calculation. And as always, anything that must be truly secret belongs on a server, not shipped to a phone.
Try it
If you ship a MAUI app, unpack your own Android .aab or open a Windows build's .dll in the free Glass.NET decompiler and read a screen you consider proprietary — it's a fast wake-up call. Then run it through Nebula and look again.
Download Nebula.NET free to try it on your own MAUI build, or read the MAUI protection guide.
Top comments (0)