Call Hiding Enterprise

Decompilers trace call targets to reconstruct program flow. Demeanor replaces direct call and callvirt instructions with calls to static relay methods that dispatch through delegates initialized at runtime. Static analysis cannot determine what a relay method calls without executing the code.

Usage

CLIMSBuildDefault
(enabled by default at Enterprise)(enabled by default)On
--no-call-hiding<ObfuscateNoCallHiding>true</ObfuscateNoCallHiding>Disable
--proxy-threshold <N><ObfuscateProxyThreshold>16</ObfuscateProxyThreshold>Min IL body size

Before & After

YOUR CODE
public string FormatReceipt(int quantity, TaxRegion region)
{
    decimal total = CalculateTotal(quantity, region);
    return $"Receipt: ...";
}
AFTER OBFUSCATION
string a(int a, h a)
{
    decimal value = k.a(this, a, a);
    // k.a is a static relay — not a direct call
    ...
}

Real ILSpy output. The direct call to CalculateTotal is replaced by k.a(this, a, a) — a static relay method in an injected helper type. The relay dispatches through a delegate initialized at first call. Static analysis of the call graph is broken.

How It Works

For each eligible call site, Demeanor creates a static relay method in an injected type. The relay holds a delegate field that is lazily initialized to point to the real target. The --proxy-threshold option controls the minimum IL body size (in bytes) for same-assembly targets — methods below the threshold are left as direct calls to preserve JIT inlining. Cross-assembly calls are always proxied.

When to Disable

  • Stack trace readability — relay methods add an extra frame to call stacks. This can make crash reports harder to interpret.
  • Profiling — performance profilers may attribute time to the relay type instead of the actual callee.

Ready to protect your .NET code?

View Pricing All Options