← Home

Frequently Asked Questions

Common questions about Demeanor .NET obfuscator.

Compatibility

Does Demeanor work with .NET 10?

Yes. Demeanor v6 is built on .NET 10 and C# 14. It obfuscates assemblies targeting any version of .NET — from .NET Framework 1.0 through .NET 10. The ECMA-335 metadata format has been consistent since the beginning; Demeanor reads and writes it all.

Does it work with NativeAOT or trimming?

Yes. Obfuscation runs as an MSBuild post-build step, before AOT compilation. The obfuscated IL is what gets compiled to native code.

What about .NET MAUI or Blazor?

Demeanor works with any .NET assembly. For Blazor, Demeanor automatically detects [Parameter], [CascadingParameter], [Inject], and [SupplyParameterFromQuery] attributes on component properties and preserves them from renaming. Routing ([Route] attributes) also survives. Internal component logic, fields, and event handlers are fully obfuscated. For MAUI, exclude XAML-bound types using --xr ".*Page$" or [Obfuscation] attributes.

Does it work with source generators?

Yes. Source generators produce C# source code at compile time, which the compiler then compiles to IL normally. Demeanor obfuscates the final compiled assembly — it doesn't matter whether the IL came from hand-written or generated source.

Can I obfuscate C++/CLI mixed-mode assemblies?

Yes. Demeanor auto-detects mixed-mode assemblies and uses in-situ mode, patching metadata in place while preserving native code sections.

Common Issues

Will obfuscation break serialization?

Types serialized to JSON, XML, or binary depend on property and field names. Exclude them with [Obfuscation(Exclude = true, ApplyToMembers = true)] or --exclude "Namespace.TypeName". See the Exclusions Guide.

Will obfuscation break reflection?

typeof(MyClass) works perfectly — it compiles to a metadata token, not a string, so it tracks renames automatically. The danger is string-based reflection: Type.GetType("MyClass"), Activator.CreateInstance("MyClass"), and similar APIs that look up types or members by name. Exclude those types with [Obfuscation(Exclude = true)].

What about WinForms and data binding?

WinForms uses reflection extensively for data binding and designer support. Exclude form types: --xr ".*Form$" --xr ".*UserControl$".

Can I debug obfuscated code?

By default, Demeanor strips PDB files after obfuscation. Use --debug to preserve PDB data for debugging. In production, use the JSON report (--report) to map obfuscated names back to originals.

"Assembly has already been obfuscated by Demeanor"

Your build pipeline is running obfuscation twice. Ensure <Obfuscate> is only set for the final build step, not in a shared Directory.Build.props that applies to test projects too.

Performance & Quality

Does obfuscation slow down my application?

No measurable runtime overhead. Renamed symbols are resolved by metadata token, not by name. String decryption adds nanoseconds per call. Control flow restructuring produces structurally equivalent IL.

Does it increase assembly size?

Slightly. String encryption adds a small decryptor method. Reference proxy adds delegate relay methods. In practice the size increase is 5-15%, often offset by heap compaction from shorter names.

Is the obfuscated output deterministic?

Within the same version and configuration, yes. Name generation is deterministic. Use --report and --prior-report for incremental builds that preserve name mappings across versions.

Licensing

Do I need a license key for Community tier?

No. Community tier is free with no license key required. Install the NuGet package, enable obfuscation, and build. You get full symbol renaming.

What happens when my Enterprise subscription expires?

The build fails with a clear error telling you the license has expired and linking to renewal. This prevents you from unknowingly shipping builds with reduced protection. To build without Enterprise features, remove the license key — the build then runs in Community tier (renaming only).

Does the license phone home?

Never. License keys are self-validating using RSA-2048 signatures. No activation server, no internet connection required, no machine locking. Works fully offline and air-gapped.

How many developers can use one license?

Unlimited. Licenses are per-company, not per-seat or per-machine. Every developer and CI agent in your organization is covered.

Security

Is .NET obfuscation worth it?

Obfuscation does not make reverse engineering impossible — it makes it expensive. Without obfuscation, tools like ILSpy and dnSpy produce near-original source code in seconds. With obfuscation, an attacker faces meaningless names, encrypted strings, flattened control flow, and proxy indirection. The goal is to make the cost of reverse engineering exceed the value of your IP.

Can someone deobfuscate my code with de4dot or similar tools?

de4dot can reverse simple string encryption and remove proxy delegates, but it cannot restore renamed symbols — the original names are permanently discarded. Demeanor's layered approach (renaming + string encryption + constants encryption + control flow flattening + reference proxy) means an attacker must defeat multiple independent protections. No single tool reverses all of them.

Does obfuscation protect against memory dumping?

Renaming and control flow obfuscation persist in memory because they modify the IL itself. String encryption decrypts at call sites, so strings exist in cleartext only briefly during execution. For maximum protection against memory analysis, combine obfuscation with NativeAOT compilation, which eliminates IL entirely from the deployed binary.

Features We Deliberately Don't Ship

Some obfuscation techniques sound impressive in a feature checklist but provide little real-world protection. Demeanor focuses on transformations that permanently alter the binary and survive execution, rather than runtime tricks an attacker can bypass by simply running the application. Here's why we skip certain features other vendors advertise.

Why doesn't Demeanor offer dead code injection?

Dead code injection adds fake methods and branches that never execute, hoping to confuse reverse engineers. In practice, it provides no meaningful protection:

  • Automated removal: Static analysis tools like de4dot and ILSpy strip unreachable code automatically. An attacker runs one command and the injected code is gone.
  • Targeted attacks ignore it: Real attackers use cross-references to find the specific method they care about — they don't read the entire assembly. Fake methods with no callers are invisible to this workflow.
  • NativeAOT eliminates it: If you use ahead-of-time compilation, the linker removes all unreferenced methods, so injected dead code never makes it into the deployed binary.
  • Binary size cost: Injecting realistic fake methods can double assembly size — a visible cost to customers with zero security return.

Demeanor's control flow flattening already inserts unreachable blocks inside real methods as a side effect of restructuring the control flow graph. This is strictly stronger than adding whole fake methods, because the unreachable blocks share the same method body and cannot be separated by call-graph analysis.

Why doesn't Demeanor offer method body encryption?

Method body encryption replaces IL with encrypted stubs and decrypts at runtime before JIT compilation. It sounds strong — a decompiler sees only stubs. But the fundamental weakness is structural:

  • "Run it once and dump": The decrypted IL must exist in memory for the JIT to compile it. An attacker hooks the JIT entry point (compileMethod), runs the application, and captures every method body as it's decrypted. Tools that automate this have existed for over a decade.
  • Single point of failure: The decryptor is itself a method in the assembly. Find it, extract the key, and decrypt every method offline without ever running the application.
  • Incompatible with NativeAOT: The ahead-of-time compiler reads IL at build time. If the IL is encrypted stubs, it compiles the stubs — producing a broken binary.
  • Runtime cost: Per-method decryption adds latency to cold paths. Decrypt-all-at-once adds a fixed startup penalty. Either way, customers notice.

Demeanor's existing protections — anti-tamper integrity verification, string encryption, constants encryption, and control flow flattening — are all permanent transformations that persist in the deployed binary and cannot be reversed by running the application. We chose to invest in protections that survive execution rather than ones defeated by it.

Why doesn't Demeanor offer anti-dump protection?

Anti-dump erases IL from memory after JIT compilation to defeat memory-dumping tools like MegaDumper and ExtremeDumper. We evaluated this and decided against it:

  • Requires method body encryption first: Without encrypted IL bodies, the IL is on disk in the assembly file. There's nothing to "dump" that the attacker doesn't already have. Anti-dump only matters as a companion to method body encryption, which we also chose not to implement (see above).
  • Windows-only: Erasing loaded PE pages requires VirtualProtect — a Windows kernel API that doesn't exist on Linux or macOS. Demeanor runs cross-platform.
  • Fragile on modern .NET: .NET 5+ changed how assemblies are mapped into memory. The "find PE base address and zero the RVA" technique that worked on .NET Framework is unreliable on modern runtimes.
  • Meaningless for NativeAOT: AOT binaries contain no IL. There is nothing to dump and nothing to erase.
  • Arms race: MegaDumper and ExtremeDumper authors counter each anti-dump technique within weeks. The reverser community treats anti-dump as a solved problem: "attach before the eraser runs" or "hook the JIT to capture methods before erasure."
What about code virtualization?

Code virtualization converts .NET IL into custom virtual machine bytecode that runs on an embedded interpreter. It is the strongest protection against both static and dynamic analysis — and the most complex. A few competitors offer it (Eazfuscator.NET, .NET Reactor, Babel, ArmDot).

Demeanor does not currently offer code virtualization. Here's why:

  • Incompatible with NativeAOT: The ahead-of-time compiler cannot compile custom VM bytecode to native code. Virtualized methods must be interpreted at runtime, negating the performance benefits of AOT.
  • Significant runtime overhead: Interpreted bytecode runs 10–100x slower than JIT-compiled IL. Vendors recommend virtualizing only "hot" methods (license checks, key algorithms), but selective application limits the protection surface.
  • Debugging and compatibility challenges: Stack traces become meaningless. Profilers can't instrument virtualized methods. Exception handling behaves differently inside the VM.

We are monitoring customer demand. If your threat model requires this level of protection, consider pairing Demeanor's obfuscation with NativeAOT compilation — AOT eliminates IL from the deployed binary entirely, achieving a similar "no readable IL" outcome without the runtime cost of an embedded interpreter.

Framework Compatibility

Does Demeanor work with Entity Framework / EF Core?

Yes — it just works with zero configuration. Demeanor's static analysis detects property references in LINQ expression trees and EF Core's Fluent API lambdas, and automatically preserves entity types and their properties. All CRUD operations, navigation properties, queries, and change tracking work after obfuscation. Tested with EF Core + SQLite.

Does it work with ASP.NET Core / Minimal APIs?

Yes. Routes, middleware, and dependency injection all survive obfuscation — they use delegates and types, not string names. The one area that needs attention is JSON-serialized response types: if you return a DTO from a Minimal API endpoint, its property names appear in the JSON output. Use [JsonPropertyName] attributes to fix property names explicitly (a best practice regardless of obfuscation), or exclude DTO types. Demeanor also auto-detects explicit JsonSerializer.Serialize<T>() calls and preserves T's properties.

Will obfuscation break WPF / XAML data binding?

Demeanor automatically detects INotifyPropertyChanged patterns and preserves data-bound properties — in testing, 0 of 4 ViewModel properties were renamed while all fields and methods were fully obfuscated. However, Window and UserControl type names must be excluded (--xr ".*Window$" --xr ".*UserControl$") because compiled XAML (BAML) references them by fully-qualified string name.

Will obfuscation break dependency injection?

No, in most cases. .NET DI resolves services by interface type, not by name. services.AddScoped<IService, MyService>() works because the runtime uses metadata tokens, not strings. Only exclude types registered by name string (e.g., keyed services).

Can I obfuscate a NuGet package / class library?

Yes, but only obfuscate internal implementation, not your public API surface. Use the default mode (without --include-publics) to protect internals while preserving the public contract. If you ship a library with no public API (e.g., a plugin), use --include-publics.

Does Demeanor run on Linux and macOS CI servers?

Yes. Demeanor ships as a single cross-platform NuGet package with self-contained native binaries for Windows, Linux, and macOS (x64 and ARM64). No runtime installation required. It works on GitHub Actions, Azure DevOps, GitLab CI, Jenkins, and any CI system that supports dotnet build.

Build Integration

How do I read obfuscated stack traces in production?

Use --report to generate a JSON name-mapping file during obfuscation. Look up mangled names in the report to find original types, methods, and parameters. Keep report files versioned alongside each release.

Does obfuscation affect code signing / strong names?

Obfuscation modifies the assembly binary, invalidating existing signatures. Demeanor automatically re-signs strong-named assemblies if you provide the key via --keyfile. For Authenticode (signtool.exe), run code signing as a post-obfuscation step in your build pipeline.

Does obfuscation work with dotnet publish and self-contained deployments?

Yes. Obfuscation runs during the build phase, before packaging. Whether you use dotnet publish, dotnet pack, or self-contained deployment, the published output is already obfuscated.

How It Works

What does aggressive mode do and why is it the default?

Aggressive mode sets renamed methods and fields to compiler-controlled (privatescope) accessibility. This strengthens obfuscation in three ways:

  • Lossy transformation: The original accessibility is permanently discarded. A decompiler must perform global data flow analysis to recover it.
  • No high-level equivalent: Compiler-controlled accessibility doesn't exist in C# or VB.NET. A decompiler must synthesize a new accessibility for every member.
  • Maximum name overloading: The runtime never uses names for privatescope members, so Demeanor assigns all of them the same name. More overloading means smaller metadata and harder reverse engineering.

Aggressive mode also strips property and event metadata, removes readonly, and clears compiler attributes like [CompilerGenerated] and [Nullable]. Use --no-aggressive to disable.

How does Demeanor handle ResourceManager and satellite assemblies?

The .NET ResourceManager infers resource names from type names at runtime. For example, a ResourceManager(typeof(MainForm)) looks for a resource named MainForm.resources. When Demeanor renames MainForm to a, it also renames the resource to a.resources so the lookup continues to work.

Culture-specific satellite assemblies (e.g., fr/MyApp.resources.dll) can also be updated using --satellite-assemblies. Demeanor finds installed satellite assemblies and renames their resources to match. You can restrict processing to specific cultures with --sa-cultures fr,de.

How does Demeanor handle enumerations?

By default, Demeanor renames enumeration members to meaningless names. For example:

// Before                    // After
enum Color {                 enum a {
  Red = 1,                     a = 1,
  Green = 3,                   b = 3,
  Blue = 5,                    c = 5,
}

If your code uses Enum.GetName(), Enum.Parse(), or Enum.IsDefined(), exclude the enum with [Obfuscation(Exclude = true)] or use --no-enumerations to skip all enums.

How does serialization interact with obfuscation?

Binary serialization writes type and field names to the output stream. When you obfuscate a serializable type, the serialized data contains obfuscated names. This means:

  • An obfuscated build can serialize and deserialize its own data — the names are consistent.
  • An unobfuscated build cannot deserialize data from an obfuscated build (different names).
  • Use --no-serializable to preserve the names of all [Serializable] types and their fields while still obfuscating their methods.

For JSON serialization with System.Text.Json, use [JsonPropertyName] attributes to decouple property names from member names, or exclude DTO types from obfuscation.

Why use a namespace prefix (--prefix)?

By default, obfuscated types are named a, b, c with no namespace. If your application references two obfuscated assemblies from different vendors, they may have identically named types — the C# compiler cannot distinguish between type a in assembly X and type a in assembly Y. Use --prefix WiseOwl to produce names like WiseOwl.a, WiseOwl.b, avoiding collisions.

What is in-situ mode and when is it used?

Normally Demeanor rebuilds the entire PE file from scratch, producing an optimized assembly that is 8-12% smaller than the original. However, assemblies containing unmanaged code (e.g., C++/CLI mixed-mode) require their native code sections to remain at their original memory locations. Demeanor auto-detects mixed-mode assemblies and switches to in-situ mode, patching only the metadata while preserving the native code layout. You should never need to request this manually.

Reports & Incremental Obfuscation (Enterprise)

What is the obfuscation report?

The JSON report (--report) is an Enterprise feature that provides a complete record of every symbol in the obfuscated assembly. For each type, method, field, property, and event, it shows the original name, the obfuscated name (if renamed), or the reason it was excluded (if not renamed). It serves two purposes: incremental obfuscation and exclusion diagnostics.

What is incremental obfuscation?

Incremental obfuscation preserves name mappings across versions of your application. When you release v2, you feed v1's report via --prior-report. Symbols that existed in v1 keep their same obfuscated names; only new symbols get new names. This ensures:

  • Serialization compatibility: Data serialized with v1's obfuscated names deserializes correctly in v2.
  • Plugin stability: External code referencing obfuscated names (from config files, databases, or DI containers) continues to work.
  • Smaller patches: Unchanged symbols produce identical bytes, so binary diffs stay small.
When should I use incremental obfuscation?

Use it whenever you ship updates to an application whose obfuscated type or member names might be persisted externally — in serialized data, configuration files, database records, or by third-party plugins. If your application is self-contained with no persisted obfuscated names, incremental mode is optional but still produces smaller update patches.

What happens when I add new types or members in a new version?

New symbols that don't exist in the prior report get fresh obfuscated names. Existing symbols keep their prior names. Even if a new type is inserted before existing types (shifting metadata indices), the prior report ensures all existing symbols keep their original obfuscated names.

What happens when I remove types or members?

Removed symbols are simply absent from the new report. Their prior names are not reused for other symbols — the naming scope skips reserved names. This prevents accidental name collisions between versions.

What does the exclusion diagnostics section show?

Every symbol that was NOT renamed includes an excludedReason field explaining why: public visibility, [Obfuscation] attribute, string-based reflection usage, virtual override of an external method, runtime special name, COM interop, and more. This helps you understand why certain symbols weren't obfuscated and whether you can adjust your configuration to rename them.

AI-Assisted Obfuscation

What is AI-assisted obfuscation?

Demeanor v6 includes an MCP server (demeanor --mcp) with 20 tools that let AI assistants like Claude Code directly audit, obfuscate, and debug your .NET assemblies. Type /obfuscate in Claude Code and Claude acts as a senior .NET security consultant — auditing every assembly, identifying framework risks, configuring protection, and writing your CI/CD build config. The setup that used to take days of trial-and-error takes one conversation. Learn more.

Do I need Claude / AI to use Demeanor?

No. The AI workflow is an accelerator, not a requirement. Every capability the AI uses is available as a CLI command: demeanor audit, demeanor report, demeanor deobfuscate, demeanor check, demeanor validate-exclusions, and demeanor init. The standalone user has every capability; the Claude user has judgment, explanation, and automation on top.

What is the pre-obfuscation audit?

The demeanor audit command scans your assemblies for 20 known framework patterns that break after obfuscation: JSON serialization, SignalR hubs, MVC controllers, EF Core entities, IOptions binding, MEF plugins, gRPC services, WPF XAML bindings, and more. Each finding is classified as auto-detected (no action needed), needs-exclusion (add an exclusion rule), or advisory (informational). No other obfuscator has this.

What is the pattern learning loop?

When Claude discovers a new failure pattern during an obfuscation session (a type that broke due to an unrecognized framework pattern), it saves the pattern to .demeanor/discovered-patterns.json in your project. On subsequent sessions, the audit tool reads this file and includes the learned patterns. Your obfuscation configuration gets smarter with every session.

Migrating from Another Obfuscator

I'm migrating from Dotfuscator. How does Demeanor compare?

Demeanor supports the same [Obfuscation] attributes that Dotfuscator uses, so your existing code-level exclusions work without changes. Key differences: Demeanor installs as a NuGet package (no separate installer), uses per-company licensing (no per-seat costs), works offline (no activation server), and runs on Linux/macOS (not Windows-only). See the full comparison.

I'm migrating from SmartAssembly. What changes?

SmartAssembly uses a standalone GUI and per-machine licensing. Demeanor integrates directly into MSBuild via NuGet — no installer, no machine locking. If you used SmartAssembly's [DoNotObfuscate] attribute, replace it with the standard [Obfuscation(Exclude = true)]. Your protection level is equivalent or stronger at a lower cost with unlimited seats. See the full comparison.