How to obfuscate a .NET DLL or EXE

A step-by-step walkthrough of obfuscating a compiled .NET assembly, what changes in the output, and how to verify the result still runs.

Obfuscating a .NET assembly takes about thirty seconds. The harder part is knowing what changed, what to test afterwards, and which switches matter. This walks through the whole loop on a real assembly.

Before you start

You need a compiled, managed .NET assembly — a .dll or .exe produced by the C#, VB.NET or F# compiler. Obfuscation works on the metadata inside that file, so no source code is required or uploaded.

Two things will not work, and it is worth ruling them out now:

  • Native binaries. A C or C++ executable, or a .NET app published with NativeAOT, has no managed metadata to rewrite.
  • Already-protected assemblies. If a file has been through another obfuscator or a packer, its metadata may no longer be in a form that can be safely rewritten a second time.

Always keep the original build output. Obfuscation is a one-way transformation — you cannot recover the original names from the protected copy, and neither can anyone else.

Step 1 — Build in Release

Obfuscate the same binary you intend to ship. A Debug build carries extra metadata and is usually paired with a .pdb symbol file that maps IL back to source lines, which undoes much of the benefit.

dotnet build -c Release
# output: bin/Release/net8.0/MyApp.dll

Ship without the .pdb, or publish symbols to a private symbol server rather than alongside the binary.

Step 2 — Upload the assembly

Drop the .dll or .exe onto the obfuscator. The file is processed in memory and streamed straight back — it is never written to disk on the server or retained after the response.

The free tier handles files up to 1 MB, one at a time, which covers most single libraries. Larger assemblies and batches are a Pro feature.

Step 3 — Decide about public names

The Obfuscate public names option is the one setting worth understanding, and it only applies to .exe files.

Private and internal members are always renamed — nothing outside the assembly can reference them, so renaming is safe. Public types and members are different: they are the surface other code binds to. For an application that is the final consumer of its own types, renaming them is safe and worthwhile. For a library that others compile against, renaming the public surface would break every caller, so it is left alone.

Why public names are only renamed for EXEs covers the reasoning and the exceptions in more depth.

Step 4 — Verify the output runs

This is the step people skip, and it is the one that matters. Obfuscation rewrites metadata without touching your IL, so the output is functionally identical in the vast majority of cases — but anything that resolves a type or member by name at runtime can break.

Run through, at minimum:

  • Application start-up and your main user journey.
  • Anything that serializes or deserializes (JSON, XML, binary settings files).
  • Dependency injection resolution, especially convention-based registration.
  • For WPF or WinForms: every window and view, since XAML binds by name.
  • Plugin loading or any Type.GetType(string) call.

If something misbehaves, obfuscation and reflection walks through the specific failure modes and how to work around each.

Step 5 — Confirm it actually worked

Open the obfuscated file in a decompiler such as ILSpy and compare it to the original. In the protected copy you should see method and field names replaced with short, meaningless identifiers, and the readable structure of your logic largely gone.

What you will still see is the overall shape of the code: control flow, framework calls, and any string literals. That is expected — what obfuscation protects against sets realistic expectations about how far this goes.

Making it automatic

Doing this by hand for every release does not scale, and a manual step is a step that eventually gets forgotten. The API takes the same upload as a single HTTP request, so your build pipeline can protect artifacts on every tagged release without anyone remembering to do it.

curl -X POST https://freeobfuscator.com/api/v1/obfuscate \
  -H "X-Api-Key: $env:FO_API_KEY" \
  -F "[email protected]" \
  -F "renamePublic=true" \
  -o MyApp.protected.exe

Obfuscating .NET builds in CI/CD has complete, working examples for GitHub Actions and Azure Pipelines.

A realistic summary

Obfuscation makes casual decompilation unproductive. Someone who opens your assembly hoping to read your licensing logic or lift an algorithm will find a wall of meaningless identifiers instead of documentation. A determined, skilled attacker with enough time can still work it out — that is true of every obfuscator, and anyone claiming otherwise is overselling. What you are buying is cost and time, and for most products that is enough to matter.

Try it on your own assembly

Upload a DLL or EXE and download the obfuscated result. Free, no signup, nothing stored.