.NET Decompilers Explained: What Attackers Can See in Your Assembly
If you ship a .NET application and have never opened it in a decompiler, you do not know what you are distributing. This page shows you what an attacker sees when they point one of the standard decompilation tools at your compiled assembly - what is recoverable, how quickly, and what it means for the code you have written.
The tools described here are free, widely available, and actively maintained. Using them requires no special knowledge. The threat they represent to unprotected .NET assemblies is not theoretical.
Why .NET is different from native code
A C++ application compiled for release is native machine code - processor instructions without names, types, or structure beyond what the CPU requires. Reverse engineering it is skilled, time-consuming work. You see registers and memory addresses. Reconstructing the original source requires expertise and patience.
A .NET application compiled for release is something different. The compiler produces CIL - Common Intermediate Language - a structured bytecode that preserves the type names, method names, parameter names, and logical structure of the original source. The runtime needs this information for reflection, serialization, and type resolution. The side effect is that a .NET assembly is, in a very real sense, self-documenting.
The tools described on this page exploit that documentation. They are not cracking tools in the traditional sense. They are readers.
ILSpy: the standard, now built into Visual Studio
ILSpy is the most widely used .NET decompiler. It is open source, free, and actively maintained. Visual Studio 2022 and later use ILSpy's own engine for their built-in decompilation feature - when debugging without source code available, Visual Studio automatically decompiles the assembly using ILSpy to show reconstructed C#. The standalone ILSpy application goes further, letting you open any assembly directly and browse the full decompiled project tree without a debugging session.
As a standalone tool, ILSpy presents a decompiled assembly as a project tree that looks indistinguishable from a Visual Studio solution. Namespaces, classes, methods, properties, fields - all organized exactly as a developer would expect. String literals appear inline. The reconstructed C# is not identical to the original source, but it is close enough that understanding it requires no special skill.
What ILSpy recovers from an unprotected release build: every type and member name, every method body reconstructed as C#, every string literal, the complete call graph showing what calls what, and every external dependency referenced by name. What it does not recover without a PDB: the names of local variables and the original source file paths.
This last point matters for how you test. If a PDB file is present alongside the assembly, decompilers can read it and show significantly more - local variable names become readable, and tools like dotPeek can use the source file paths embedded in the PDB to locate and display the original source files directly, bypassing decompilation entirely. An attacker does not have your PDB. If you want to understand what someone looking at your shipped assembly actually sees, open it in ILSpy without the PDB present. That is the accurate threat model.
The difference between the original source and the ILSpy output is cosmetic. The logic is there.
ILSpy also supports full-text search across the entire decompiled assembly. Searching for license, serial, trial, key, activate, expire - or any other term related to protection logic - returns results in seconds. An attacker does not need to read the entire codebase. They navigate directly to the part they care about.

dnSpy: decompiler plus debugger plus binary editor
dnSpy goes further than ILSpy. It combines decompilation with a full .NET debugger and a binary editor, making it the primary tool for active reverse engineering rather than just reading.
With dnSpy, an attacker can set breakpoints in a running .NET application - without access to source code - and step through the execution at runtime. They can inspect the values of local variables, fields, and parameters at any point during execution. They can observe the runtime behavior of methods that are difficult to understand from static analysis alone.
The binary editing capability is what makes dnSpy particularly relevant to license protection specifically. After locating a license validation routine through decompilation, an attacker can identify the conditional branch that determines whether the validation passes or fails. In dnSpy's IL editor they can change that instruction directly in the running process - or save a patched version of the assembly to disk. The patch required to bypass a simple license check is often a single instruction change: flipping a conditional branch so that validation always returns true, regardless of what the key actually is.
This is not a theoretical capability. It is a documented, widely practiced technique applied routinely to commercial .NET software.
JetBrains dotPeek: free, with IDE integration
dotPeek is JetBrains' free .NET decompiler. For developers who use IntelliJ-based tools, it integrates with ReSharper and Rider, allowing decompilation of external assemblies directly within the IDE workflow.
Its decompilation quality is comparable to ILSpy. The primary audience is developers who want to understand library internals or debug issues in third-party code - but the same capability is available to anyone who wants to examine an assembly they did not write.
dotPeek can also generate navigable source code from an assembly and serve it as a symbol server, allowing step-through debugging of decompiled code in Visual Studio. This makes it easy to observe not just the structure of an assembly but its runtime behavior under controlled conditions.
.NET Reflector: the original commercial decompiler
.NET Reflector, now maintained by Redgate, was the original .NET decompiler - available since the early days of the framework. It is commercial software, in contrast to the free tools above, but it established the baseline that the others followed.
For the purposes of understanding the threat, Reflector is not meaningfully different from ILSpy or dotPeek in terms of what it can recover. Its continued relevance is primarily in enterprise environments where it is already part of the toolchain.
ILDasm: the tool that ships with the .NET SDK
ILDasm - the IL Disassembler - ships with the .NET SDK itself. It is not a decompiler in the sense of reconstructing C#; it shows raw CIL instructions. But it is worth understanding because it is always available - every developer machine with the .NET SDK installed has it - and it reveals the assembly's structure clearly enough to locate methods, read string literals, and understand the flow of a program.
I was somewhat surprised to find that ILDasm, a tool with roots in the earliest days of .NET, works perfectly well on modern .NET Core and .NET 5+ assemblies. The CIL format has not changed in any way that makes older tools obsolete. Simple tools remain effective tools.
de4dot: automated deobfuscation
de4dot is a different category of tool. Rather than decompiling source from a protected assembly, it attempts to remove the obfuscation itself - restoring meaningful names where possible, decrypting strings, and reconstructing control flow. It supports a range of common obfuscators and has specific modules for several commercial tools.
What de4dot can and cannot do is directly relevant to evaluating protection choices. It can reverse string encryption schemes that use simple algorithms. It can reconstruct control flow obfuscation to make the decompiled output more readable. It cannot restore renamed symbols - the original names are gone permanently - and it struggles significantly with heavily flattened control flow. Against code virtualization, it produces no useful output.
When attackers encounter a protected assembly, de4dot is typically the first tool they run - a quick scan to see what the protection is and whether an automated reversal is possible.
What a decompiler cannot see
Understanding what decompilers recover is not complete without understanding what they cannot.
When ArmDot's code virtualization is applied to a method, a decompiler looking at that method sees the VM interpreter stub - a dispatch loop reading bytes from a buffer. The original method logic is not present in CIL form anywhere in the assembly. I was genuinely interested when I first saw what ILSpy produces for a virtualized method - it attempts to show the interpreter loop as C#, producing something that is technically valid output but entirely disconnected from the original code. The decompiler is not confused in the sense of producing errors. It faithfully reconstructs what is there. What is there just happens to be an interpreter rather than the original logic.
That output is what a sufficiently protected method looks like to an attacker with the best free decompilation tools available. It does not tell them what the method does. Understanding the method requires reversing the VM itself - a significantly more expensive undertaking.
The practical picture
An attacker with ILSpy and five minutes can recover a near-complete picture of any unprotected .NET assembly: every class name, every method, every string literal, the complete structure and call graph. With dnSpy they can set breakpoints and observe runtime behavior. With de4dot they can attempt to reverse basic obfuscation.
None of this requires expertise. The tools are polished, well-documented, and designed to be used by ordinary developers for ordinary purposes. The same capability that helps a developer understand a library they are using is available to anyone who wants to understand a commercial application they did not write.
The question for any .NET developer shipping commercial software is not whether these tools exist. It is whether their assembly gives those tools anything useful to work with.
See also: Best .NET Obfuscators: A Side-by-Side Comparison →
Back to: .NET Obfuscation Tools →
