# How to Verify Protected Output

We recommend inspecting protected assemblies with a .NET decompiler such as ILSpy or dnSpy, then testing the protected application. Start with Obfuscation (renaming), check the other enabled features against familiar parts of your code, and verify the files that will actually be deployed.

In this guide, renaming means the type and member name changes controlled by the Obfuscation option. Other protection features, such as String Encryption, Control Flow Obfuscation, and NecroBit, are checked separately.

# 1. Always use -licensed with a licensed command-line installation

When using a licensed version of .NET Reactor, always include -licensed in command-line invocations, including local scripts and automated builds:

dotNET_Reactor.Console.exe -licensed -project "C:\Build\MyApp.nrproj"

This option requires the .NET Reactor instance executing the command to run as a licensed full version. If a demo version is detected, no files are protected and the process returns exit code 101. See the command-line reference.

Owning a license does not by itself ensure that a particular build machine or script invokes a licensed installation. Without this check, an incorrectly configured environment could produce demo-protected output. Such output can display a nag screen and/or stop functioning 14 days after it was generated, as described in the License Agreement.

Make the build stop on a nonzero exit code, and do not package files left over from an earlier run. -licensed checks the license of .NET Reactor itself. It does not activate the installation or create a license for your application.

# 2. Open the correct protected files

Inspect the protected output, not the original compiler output. Check the main assembly and each additional assembly you intended to protect. Keep an unprotected copy available for comparison, and confirm the file path loaded by the decompiler.

# Source display versus decompiler output

A decompiler can display available original source through debugging symbols instead of reconstructing code from the protected assembly. When using dotPeek, disable source lookup and reopen the code before assessing protection. Follow dotPeek Shows Original Source Code for the exact setting and checks.

# Folder deployments and installers

For a folder deployment, inspect the assemblies in the final deployment folder.

If .NET Reactor runs during the build and the build then creates a setup package, install that package into a clean test location before inspecting the installed assemblies. This checks that the installer contains the protected files rather than an earlier unprotected copy. Do not rely only on inspecting an intermediate build folder.

# Single-file applications

When processing a single-file application, .NET Reactor creates subfolders containing the individual protected assemblies. These files are provided so that you can inspect the assemblies separately without having to extract them from the bundle.

Use these per-assembly outputs for inspection and the newly created protected bundle for deployment and runtime tests. Do not add the inspection copies as separate dependencies beside the bundle.

With Anti ILDASM enabled, common decompilers may be unable to browse the bundle. The separate files make the assemblies directly accessible, but their protection settings still apply. They are not unprotected inspection copies. See Single-File Publishing and the inspection guidance below.

# Embedded assemblies

Dependencies configured with Embed Assemblies belong inside the main assembly. They should not appear as separate dependency files in the final output and must not be deployed separately. Check that packaging does not copy their original DLLs from the build folder into the release.

Embedding does not itself protect an assembly. If an embedded dependency needs protection, protect and inspect it before embedding it. Do not confuse embedded dependencies with the separate inspection outputs created when processing single-file bundles.

# 3. Check renaming first

Renamed classes can be difficult to find by their original names. To check which classes and members are renamed, create a separate inspection configuration, keep Obfuscation enabled, and set Naming Convention → Debug. Keep Obfuscate Public Types, exclusions, and other renaming rules unchanged. Protect a fresh copy of the unprotected assembly with this configuration.

The Debug naming convention adds the prefix Δ_ to the original names. For example, a class named OrderService becomes Δ_OrderService when it is renamed. This makes the original name recognizable while also showing whether .NET Reactor renamed that class or member.

Open this inspection output in the decompiler, locate a few familiar classes, and inspect their methods, properties, fields, and other members. Check for the Δ_ prefix, not merely whether the original name is still readable. Classes or members that retain their unchanged original names should be reviewed against the public-type settings and exclusions described below.

Use Debug only for inspection. Restore the intended production naming convention, generate fresh protected output, and verify that output before release. Do not distribute the Debug inspection build.

When Obfuscation is enabled, .NET Reactor renames nonpublic classes and members by default, subject to the configured exclusions. Public names are preserved when Obfuscate Public Types is disabled. See Obfuscation.

# A public class still has its original name

There are two ways to handle this, depending on whether the class needs to be public:

  • The class is only an implementation detail within its assembly. Change its declaration from public to internal in your source code, rebuild, and protect again. It can then be renamed by the default nonpublic-type settings. Only make this change when callers and frameworks do not require public accessibility.
  • Public types may also be renamed. Enable Obfuscate Public Types. No source-code accessibility change is necessary. If other assemblies reference those public types or members, protect the related assemblies together so that their references are updated. Keep public names when consumers outside that protection run require them.

See Troubleshooting → Public Type Obfuscation before enabling public renaming across assembly boundaries.

# A nonpublic class or member still has its original name

Check that Obfuscation is enabled and review the project's exclusions, rules, and any System.Reflection.ObfuscationAttribute declarations in the source. Verify that the assembly was included in the protection run. A name intentionally preserved for reflection or compatibility is different from an item that was unexpectedly skipped.

# 4. Inspect the other protection features

# Make familiar methods easy to find

Create a separate inspection configuration and either disable Obfuscation (renaming) or set Naming Convention → Debug. Leave the feature you want to inspect enabled.

As in the renaming check above, Debug keeps original names recognizable while marking renamed classes and members with Δ_. It still changes names, so it is not equivalent to disabling renaming when investigating a reflection problem. Keep renaming enabled when checking whether names are changed. Disable it only when you need to isolate other features or diagnose a possible renaming-related failure. See Naming Convention.

For example, to check String Encryption, find a method where you know a particular string is used:

internal static string GetStatusMessage()
{
    return "License validation completed.";
}

Compare that method in the protected assembly with the original. When the string is covered by String Encryption, the original literal should no longer appear in clear text at that location. The protected code obtains the string through the injected decryption logic instead. Searching all files for arbitrary readable text is less useful than checking a known string in a known method.

# What to look for

The following checks apply to code and resources included in the corresponding feature. Check exclusions and feature-specific selection rules when an expected transformation is missing.

Feature What to inspect and expect
Obfuscation (renaming) Eligible type and member names differ from their original names according to the selected naming convention. Unexpected unchanged names should be checked against public-type settings and exclusions.
String Encryption A known string literal is no longer visible in clear text in the protected method. Instead, the code uses the string-decryption logic. This check concerns the selected code strings, not every readable name or resource in the application.
Control Flow Obfuscation A familiar method has a changed control-flow structure, for example additional branches, jumps, or a less direct arrangement of the original logic. Compare the decompiled code and, where useful, the IL view. The exact display depends on the method and decompiler.
NecroBit Your original application code should no longer be visible in method bodies covered by NecroBit. Static constructors (.cctor) are the exception and may still contain original initialization code. Visible type names or method signatures alone do not indicate that NecroBit was not applied.
Code Virtualization A method selected for virtualization no longer shows its original implementation as ordinary managed instructions. The remaining stub invokes the virtualization runtime. Check a method actually selected for virtualization, not an arbitrary method.
Hide Method Calls Calls covered by the selected internal/external call settings are redirected through delegates instead of appearing as the original direct calls. Compare a method with known calls before and after protection.
Compress & Encrypt Resources The original managed-resource contents are stored in compressed and encrypted form rather than remaining directly readable in their original form. Inspect resource contents, not only resource names.
Anti ILDASM A decompiler may refuse to open the protected assembly or browse a protected single-file bundle. Check this behavior separately from the transformations inside individual methods.

# When one feature hides another

NecroBit or Code Virtualization can hide the method body you would otherwise inspect for string encryption or control-flow changes. In a separate inspection configuration, temporarily disable the feature that prevents that check while leaving the feature being examined enabled.

Similarly, if Anti ILDASM prevents your decompiler from opening the file, temporarily disable it in the inspection configuration. Opening a per-assembly file from a single-file output folder does not disable Anti ILDASM.

# 5. Verify that the protected software works

Run the actual protected deployment: the installed application, the final deployment folder, or the newly generated single-file bundle. Avoid a launch or test command that rebuilds the project and silently replaces the protected files with unprotected ones.

Test more than startup. Exercise the application's important workflows, including dependency loading, reflection, serialization, UI bindings, resources, plugins, and licensing behavior where applicable. For a library, test a consumer of the protected library.

Anti Debug and Anti Tampering also require behavior checks rather than just a visual inspection of method bodies. Test their intended behavior separately in a controlled environment. For example, debugger attachment with Anti Debug enabled is expected to terminate the process.

# Isolate a failing protection setting

If the protected application behaves differently from the unprotected one:

  1. Confirm the baseline. Verify that the unprotected application works in the same test environment and deployment format.
  2. Start with protection features disabled. Use a separate diagnostic configuration while keeping the required packaging settings consistent.
  3. Enable features one at a time. For each run, protect fresh unprotected inputs and repeat the failing scenario. Do not apply successive test configurations to an already protected assembly.
  4. Check the first failing configuration. Identify whether the newly enabled feature causes the failure by itself or only in combination with another enabled feature. Reduce the test to the relevant settings and code path.

A common cause is renaming combined with reflection: code or a framework looks up a type or member by its original name after that name has changed. See Troubleshooting → Reflection Problems.

Where original names must be preserved, use targeted exclusions through Declarative Protection or the Advanced Rules Editor, rather than leaving all protection disabled. After correcting the issue, restore the intended release settings and repeat both inspection and functional tests on the final package.

# Related topics

Choosing Protection Settings · Public Type Obfuscation · Reflection Problems · Single-File Publishing · Debugging Stack Traces

Publishing · Debugging · dotPeek Shows Original Source Code