Nebula.NET
Code virtualization
Compile your most sensitive .NET methods to a custom bytecode run by an embedded VM — so their logic no longer exists as IL and can’t be decompiled. Nebula.NET Enterprise.
Code virtualization is Nebula.NET’s strongest protection — an Enterprise feature. (For the why and how in depth, see the illustrated deep dive: Code that ships with nothing to read.) It takes a chosen method, compiles it to a custom bytecode, and replaces the method body with a stub that runs that bytecode on a small embedded virtual machine. The original instructions are gone: a decompiler no longer shows your logic, only a call into the VM. To recover the method, an attacker must first reverse-engineer the VM itself — a dramatically higher bar than reading flattened IL.
What it looks like
A method like this:
public static int Score(int a, int b)
{
int r = 17;
for (int i = 0; i < 8; i++) r = r * 31 + ((a >> i) & 1) ^ (b + i);
return r & 0x7FFFFFFF;
}
decompiles, after virtualization, to essentially nothing but a VM call:
public static int Score(int a, int b)
{
return VirtualMachine.Run(typeof(<owner>).TypeHandle, 100663302, new int[] { a, b });
}
The loop, the arithmetic, the constants — none of it is in the shipped IL anymore. It lives as opaque bytecode in an embedded resource, executed at runtime.
Enabling it
{
"virtualizeMethods": true,
"virtualizeInclude": [
"Contoso.Licensing.LicenseCheck.Validate",
"Contoso.Crypto.KeyDerivation.Derive"
]
}
virtualizeMethods— turn virtualization on (Enterprise license required).virtualizeInclude— an allowlist of methods to virtualize, byNamespace.Type.Method. Leave it empty to virtualize every eligible method. Virtualization is heavier than other transforms, so an allowlist targeting your genuinely sensitive methods (licensing, key derivation, algorithms) is the usual choice. Discover names withnebula inspect --methods.
Virtualization runs before control-flow obfuscation, so a virtualized method is protected by the VM (and any leftover stub is still hardened by the other passes) — you never lose virtualization by also enabling control-flow.
What can be virtualized (v1 scope)
Virtualization is behaviour-exact: it only transforms methods it can compile with identical semantics, and silently leaves everything else untouched — it is never applied incorrectly. A method is eligible when all of these hold:
- it is static and non-generic, on a non-generic type;
- it has no exception handlers (
try/catch/finally); - its parameters, locals and return type are all 32-bit-integer family (
bool,char,byte,sbyte,short,ushort,int,uint); - its body uses only integer arithmetic, bitwise ops, comparisons, branches and conversions.
This maps exactly to the CLR’s evaluation-stack model, so the VM’s arithmetic — including 32-bit overflow wraparound — matches the original method precisely. It’s an ideal fit for the integer-heavy code that protection matters most for: license validators, auth-code/checksum logic, key math, and algorithmic kernels. Methods outside the model (strings, object/reference types, long/floating-point, calls, field access) are left for the other passes to protect and are candidates for a future virtualization release.
Runtime
A virtualized assembly ships a tiny Nebula.Runtime.dll alongside it (the VM). Nebula copies it to your output automatically and injects a loader so it resolves at startup — no project reference or deps.json entry needed. Nebula.Runtime targets .NET Standard 2.0, so virtualized assemblies run on every .NET target: .NET Framework 4.6.1+, .NET 6–10, and beyond.
When to use it
Reach for virtualization on the handful of methods an attacker would attack first — the license check, the “is-pro” gate, a proprietary algorithm. Combined with string encryption and anti-tamper, it makes those methods impractical to reverse. See how it holds up against automated tools in Resisting automated deobfuscation.