Skater .NET Obfuscator
4.6 out of 5 (71 reviews on Visual Studio Marketplace)

What a Decompiler Sees in Your .NET App, and How to Take the Secrets Back

By the Skater Development Team. Published 6 October 2026. About 8 minutes to read.

A .NET build keeps every string literal in readable form, so a free decompiler can lift your endpoints, tokens and connection strings in minutes. Skater can encrypt those literals inside the assembly or move them into a separate native DLL. Use the DLL for anything that would hurt if read, accept that nothing shipped to a user's machine is unbreakable, and keep credentials with real power on a server.

A ten-minute teardown

Here is how a curious stranger, or a competitor, treats your released build. No access to your repository is needed.

  1. 0:00Take the EXE or DLL from your installer or the customer's disk.
  2. 0:02Open it in ILSpy or dnSpy. C# comes back almost line for line.
  3. 0:04Search for http, Server=, token, license. Every literal is searchable.
  4. 0:07Follow "Used by" from a hit. A string is a landmark that leads straight to the license check, the sync client or the login code.
  5. 0:10Patch the check, point the endpoint at a server of their own, or reuse your credentials.

Suppose your field-service app contains this class:

internal static class Backend
{
    static readonly string Telemetry = "https://telemetry.fieldops.example/ingest";
    static readonly string ApiToken  = "tok_demo_7f3a91c2e5b84d10";
    static readonly string ReportDb  =
        "Server=sql-reports.fieldops.internal;Database=Reports;User Id=reporter;Password=Sparrow-42;";
    const string LicenseMsg = "License check failed (HTTP 403)";
}

Search the compiled build for fieldops and the decompiler returns every line above, character for character. The error text even tells the reader which function to patch.

What string protection can and cannot do

Be clear about the goal before touching a setting. Code that runs on someone else's computer can always be studied by a determined person, and Skater's documentation says as much. The realistic aim is to remove the easy wins: the search that finds a token in seconds, the landmark string that leads to your license logic.

That shapes a rule. If one leaked value would compromise every customer, such as a broad-rights database password or a signing key, do not rely on obfuscation to guard it. Put a service in front of it. String protection is for what has to ship: endpoints, validation messages, salts, client tokens with narrow scope.

Two ways Skater hides a string

Inside the assembly

Set under Strings Store Location as Keep Strings Inside The Output Assembly. Skater encrypts each literal and adds a decryption routine that restores the value at runtime. Nothing extra to deploy. The catch: the routine ships with the strings, and Skater's notes say some newer decompilers can hook it and print the originals.

In a native DLL

Choose Strings Stored in Separate DLL (recommended). Skater writes the strings into C++ source, compiles a machine-code library (default name appExtension), keeps the strings encrypted inside it, and rewrites your assembly to fetch them through P/Invoke. .NET assembly browsers cannot open that file.

The DLL costs you one extra file to deploy, and the app will not start without it. Skater notes that the DLL algorithm was redesigned in version 9.7 to be more secure, so stay on a current release. A third option, Cloud Vault, stores protected strings in a hosted vault (added in 9.9). It has its own article, and this guide sticks to the two local modes.

Walkthrough in the Skater interface

  1. Load the right file. Open the assembly that actually contains the literals. If your .NET app publishes an EXE launcher plus a DLL, protect the DLL as well.
  2. Open the Strings tab and mark what to protect. Start narrow: tokens, endpoints, connection strings, and the messages that expose your logic.
  3. Choose the storage mode under Strings Store Location (above).
  4. Name the DLL if you chose the separate-DLL mode. A custom name is mildly less obvious than appExtension but is not protection by itself.
  5. Obfuscate and save. The native DLL lands in the same folder as the output assembly.
  6. Save the settings. Assign them in the GUI once, and the command-line version reuses them in batch runs.

What the decompiler shows afterwards

These are simplified sketches. Skater's generated names differ from build to build.

// Inside the assembly: a blob and a call, not a readable literal
static readonly string ApiToken = Decrypt("q9T2x...");

// Native DLL: no literal and no ciphertext in the managed code
[DllImport("appExtension")] static extern string GetString(int id);
static readonly string ApiToken = GetString(1);

In both cases the search from the teardown now comes back empty. In the first, a patient reader can still study the Decrypt routine. In the second, the trail leaves managed code and continues into native machine code.

Sorting your strings

Tier 1: use DLL mode

Tokens, license-server URLs, salts, and anything harmful if read or redirected.

Tier 2: either mode

Error and validation messages, SQL text, route names. These map your code more than they leak power.

Tier 3: leave alone

UI labels, log templates, strings in hot loops, and literals the language requires as constants, such as attribute arguments and switch case labels.

Tier 4: do not ship

Broad-rights passwords and signing keys. Move them behind a service.

The storage mode is chosen per obfuscation run. If an assembly holds even one Tier 1 string, use DLL mode for it, or move the secrets into a small assembly of their own if your design allows.

Proving it worked

Remember that a protected string is plain again in memory when the program uses it. Verification proves the build is hard to read, not that runtime inspection is impossible.

When it breaks

DllNotFoundException at launch
The DLL is not beside the executable. Packaging that gathers files from the project's own output folder will not know the DLL exists, so add it explicitly.
BadImageFormatException
A 32-bit/64-bit mismatch between the DLL and the running process. Check which platform your protected output targets and ship the matching build.
Plaintext still in the build
The string was not selected, the same literal lives in another assembly, or it is a required constant. Rescan after each change.
Slow screens
Decryption or P/Invoke on every call inside a loop. Read the value once into a field at startup and reuse it.

Make it part of every build

Protection that depends on someone remembering is protection that lapses. Build in Release, run the Skater command line with the settings saved from the GUI, scan the output, package the DLL, then smoke-test on a clean machine. Skater's wiki has a CI/CD article for this, and the 26.1 release reworked CLI logging so a headless failure is easier to diagnose. Keep the settings file in source control.

Two boundaries to know. Skater has a separate option for strings stored in resources, but files outside the assembly, such as appsettings.json, are untouched, and they should not hold secrets in a shipped client anyway. And strings are only the landmarks: add member-name obfuscation and, from the Professional Edition up, control flow obfuscation so the code around them is hard to follow too.

Questions people ask

If I can only pick one string-storage mode, which one?

Pick Strings Stored in Separate DLL for anything that would hurt if read. Keeping strings inside the assembly is easier to ship, but the decryption routine travels with the assembly, and Skater's own notes say some newer decompilers can intercept it.

Can someone still read the strings while the app runs?

Possibly. A string has to be plain text at the moment your code uses it, so memory inspection or hooking can catch it. The DLL mode makes static analysis far harder, and Skater's documentation is frank that native code can still be reverse engineered.

Is it safe to ship a production database password if I use the DLL?

It is better not to. Anything on a user's machine can eventually be extracted by a determined person. Put a service in front of the database, or issue short-lived per-user tokens, and treat string protection as a second line of defense.

What happens if appExtension.dll is not deployed?

The protected assembly cannot fetch its strings and will not run. That is by design, because it keeps the strings away from .NET assembly browsers. Confirm the DLL is in your installer, and test on a clean machine.

Which Skater edition includes string encryption?

According to the edition table on the prices page, string values encryption starts at the Standard Edition. String encryption with cryptography and Control Flow obfuscation start at the Professional Edition. Check the current table before you buy.

Does this work with .NET 10?

Skater supports .NET 10 projects where the publish output is a DLL (framework-dependent deployment), plus .NET Framework 1.0 through 4.8, .NET Core and .NET Standard. If an EXE launcher calls your DLL, obfuscate the DLL too, since that is where the literals live.