~/wiseowlsoftware/demeanor $
Photo of Brent Rector, author of Demeanor for .NET
Brent Rector — author of Demeanor, the original .NET obfuscator.
Five decades in software: mainframes, minis, PCs, cloud, and AI.
Based in San Diego.
EST. 1999 · THE ORIGINAL .NET OBFUSCATOR

Know what breaks before you obfuscate.

Run demeanor audit against a compiled assembly. Demeanor tells you in plain language what’s safe, what breaks, and what to change — before a single byte is rewritten.

Demeanor knot logo
Since 1999 No phone-home Per-company licensing Win / Linux / Mac

Obfuscation tuned to your app — not a generic pass.

Most .NET obfuscators run a transformation pass, write a new DLL, and hand it back. If it crashes at runtime, that becomes a debugging problem you own — for days. Demeanor works the other way around: it reads your assemblies first, tells you which constructs in your code would break, which Demeanor already handles, and what to change for tighter protection. You start the obfuscation run knowing what’s going to happen.

MOST .NET OBFUSCATORS
  • Run a fixed transformation pass on your assembly.
  • Treat every codebase the same — no awareness of your reflection, serialization, or framework patterns.
  • Ship the output. Good luck.
  • If it breaks at runtime, the debugging is on you — often through obfuscated stack traces.
  • Every edge case means trial-and-error exclusions, another build, another crash.
DEMEANOR
  • Analyzes your app first. demeanor audit scans every assembly and surfaces the constructs that are problematic for obfuscation.
  • Tells you what’s safe, what isn’t, and why. Classifies each finding as auto-handled, needs an exclusion, or needs a code change — with the exact file and line.
  • Recommends fixes. Shows the specific [Obfuscation] attribute, CLI flag, or code alternative that would make the construct obfuscation-compatible.
  • You approve the work upfront. No surprise crashes after the fact.
  • Then obfuscates — correctly, the first time.

What the decompiler sees.

Your code in, three outputs back — the original, Community-tier renaming, and Enterprise-tier full protection. Each panel is real ILSpy output.

YOUR CODE
public class PricingEngine
{
    private readonly decimal _baseDiscount;
    private decimal _lastTotal;

    public decimal DiscountRate => _baseDiscount;
    public string EngineName => "Standard";
    public event EventHandler<decimal>? PriceChanged;

    public decimal CalculateTotal(int quantity, decimal unitPrice)
    {
        var subtotal = ComputeSubtotal(quantity, unitPrice);
        var discount = ApplyDiscount(subtotal);
        var total = subtotal - discount;
        _lastTotal = total;
        NotifyPriceChanged(total);
        return total;
    }

    public decimal GetLastTotal() => _lastTotal;

    private decimal ComputeSubtotal(int quantity, decimal unitPrice)
        => quantity * unitPrice;
    private decimal ApplyDiscount(decimal subtotal)
        => subtotal * _baseDiscount;
    private void NotifyPriceChanged(decimal total)
        => PriceChanged?.Invoke(this, total);

    public override string ToString()
        => $"PricingEngine(discount={_baseDiscount})";
    public override bool Equals(object? obj)
        => obj is PricingEngine p && _baseDiscount == p._baseDiscount;
    public override int GetHashCode() => _baseDiscount.GetHashCode();
}
COMMUNITY
public class PricingEngine
{
    private readonly decimal m_a;
    private decimal b;
    private EventHandler<decimal>? c;

    public decimal DiscountRate => m_a;
    public string EngineName => "Standard";
    public event EventHandler<decimal>? PriceChanged;

    decimal a(int a, decimal a) => (decimal)a * a;
    decimal a(decimal a) => a * m_a;
    void a(decimal a) => c?.Invoke(this, a);

    public decimal CalculateTotal(int quantity, decimal unitPrice)
    {
        decimal num = a(quantity, unitPrice);
        decimal num2 = a(num);
        decimal num3 = (b = num - num2);
        a(num3);
        return num3;
    }

    public decimal GetLastTotal() => b;

    public override string ToString()
        => $"PricingEngine(discount={m_a})";
    public override bool Equals(object? obj)
        => obj is PricingEngine p && m_a == p.m_a;
    public override int GetHashCode() => m_a.GetHashCode();
}

Public API preserved for compatibility. Internal helpers all collapse to a — three methods, three parameters per method, all named the same. Real ILSpy output.

ENTERPRISE
public class a
{
    private decimal m_a;
    private decimal m_b;
    private EventHandler<decimal> m_c;

    decimal  () => this.m_a;
    string  () => b.a("\u0003\r\u00cd\u00c60\u001c\u00c2\u00c8");

    decimal  (int a, decimal a)
    {
        decimal num =  (a, a);
        decimal num2 =  (num);
        decimal result = (this.m_b = num - num2);
         (result);
        return result;
    }

    decimal  () => this.m_b;

    decimal  (int a, decimal a) => (decimal)a * a;
    decimal  (decimal a) { d.a(); return a * this.m_a; }
    void  (decimal a) => this.m_c?.Invoke(this, a);

    public virtual string a() { /* control-flow obfuscated */
        return b.a("\u00df\u0094\u001f\u00df..."); }
    public virtual bool b(object a) { d.a(); /* control-flow obfuscated */ }
    public virtual int c() { d.a(); return this.m_a.GetHashCode(); }
}

Type renamed. Method names use Unicode glyphs the C# parser cannot read. Strings encrypted. Anti-tamper and anti-debug active. Control flow obfuscated. Real ILSpy output.

$ dotnet build
Build succeeded.
0 errors.
$ dotnet build
CS0100: The parameter name 'a' is a duplicate
CS0111: Type 'PricingEngine' already defines a member called 'a'
Build FAILED. 2 errors.
$ dotnet build
CS1001: Identifier expected
CS1525: Invalid expression term 'decimal'
CS1003: Syntax error, ',' expected
CS1002: ; expected
CS0106: The modifier 'public' is not valid for this item
Build FAILED. 47 errors.

Shipped since 1999. Rebuilt for .NET 10.

The first .NET obfuscator — written against internal Microsoft “Lightning” builds before .NET was publicly announced. Demeanor v6 is a complete ground-up rewrite in modern C# 14, designed around how developers actually work today.

Pre-obfuscation audit.

demeanor audit analyzes your compiled assemblies and flags every construct that would be problematic for obfuscation — serialization, reflection, data binding, dependency injection, plugin contracts, and more. Each finding is classified and tied to the exact file and line. You know what work is required before any obfuscation runs. No other .NET obfuscator does this.

Smarter automatic handling.

For the common framework patterns — JSON/XML/WCF serialization, EF Core, ASP.NET Core routing, WPF data binding, SignalR, DI — Demeanor detects and preserves what’s needed automatically. The audit tells you it found them so you aren’t surprised.

Already use an AI assistant? Optional.

The audit is a plain CLI command with readable output. If you already use an MCP-capable assistant, Demeanor’s opt-in MCP server lets you drive the same audit as a chat. No pricing tier depends on it; runs locally over stdio. How the conversational workflow works →

No more installers.

Prior versions of Demeanor required an MSI installer and a Visual Studio extension. Demeanor v6 is a .NET global tool: dotnet tool install -g WiseOwl.Demeanor puts demeanor on PATH (and auto-installs the companion WiseOwl.Inspector tool so inspect is on PATH too). Run demeanor init in any project — Release builds obfuscate themselves.

One price. Whole team. No seat count.

Per-company licensing. Every developer, every CI server, every build machine — one license. No seat counting, no machine locking, no activation server, no phone-home. Builds must succeed with no internet — non-negotiable.

Runs wherever .NET runs.

Cross-platform .NET global tool. Windows and Linux on x64 and ARM64; macOS on Apple Silicon. One dotnet tool install command.

Buy Enterprise Compare → All Protection Options →

Three commands. Sixty seconds.

Install the .NET global tool, audit your assembly, obfuscate it. No configuration, no ceremony — read the output of the audit, apply the fixes it recommends, ship protected code.

1

Install Demeanor

dotnet tool install -g WiseOwl.Demeanor

Cross-platform .NET global tool. Puts demeanor and inspect on your PATH.

2

Audit your compiled assembly

demeanor audit MyApp.dll --include-deps

Plain CLI command, readable report. Tells you what obfuscates cleanly, what is problematic, and what changes would fix the problematic cases.

3

Obfuscate

demeanor MyApp.dll --include-deps --report

Applies renaming, string and constants encryption, control-flow obfuscation, anti-tamper, and anti-debug. Writes a report you can use to decode production stack traces.

OPTIONAL · ALREADY USE AN AI ASSISTANT?

If you already use an MCP-capable assistant — Claude Code, Claude Desktop, Cursor, Windsurf, Continue.dev, or a VS Code MCP extension — Demeanor’s opt-in MCP server lets you drive the same audit as a chat. Requires your own Claude subscription — not included with Demeanor. How the conversational workflow works →