Anti-Dump for .NET: Stopping Attackers From Lifting Your Assembly Out of Memory
String encryption, control-flow flattening, renaming — they all protect the assembly on disk. But your program does not run on disk; it runs in memory, and by the time it is running the CLR has loaded the image, the JIT
String encryption, control-flow flattening, renaming — they all protect the assembly on disk. But your program does not run on disk; it runs in memory, and by the time it is running the CLR has loaded the image, the JIT has compiled methods, and your encrypted strings have very likely been decrypted into RAM so the code could use them. An attacker who dumps the process memory captures that live image, and with it much of what your on-disk obfuscation was hiding. Anti-dump protection is the layer that addresses this specific gap, and understanding it clarifies why it is a necessary complement to — not a replacement for — the rest.
The dump attack
A memory-dumping tool (the genre that MegaDumper and similar represent) does something conceptually simple: attach to or scan your running process, locate the loaded .NET module in its address space, and write those bytes out as a .dll. Because it copies the loaded image, it gets:
- strings that string-encryption decrypted at runtime, now sitting in the clear,
- the real IL for methods the JIT needed (for non-virtualized code),
- a module that can often be loaded straight into a decompiler.
The attacker never had to defeat your on-disk file. They let your own process do the decryption and then photographed the result. This is why "I encrypted my strings" is not, by itself, enough.
.b{fill:none;stroke:#64748b;stroke-width:1.5}.t{fill:currentColor;font-size:12.5px}.s{fill:#64748b;font-size:11px}.ob{stroke:#6366f1;stroke-width:2}.bad{stroke:#ef4444;stroke-width:1.5}.ar{stroke:#64748b;stroke-width:1.5;marker-end:url(#d)}
Module loadedheaders + IL in RAM
CLR readheaders
Anti-dump: wipeDOS/NT headers, sections
attackerdumps
Dump = headerless blobnot a valid/loadable PE
The running code already resolved what it needs; the dumper gets an image missing the structure a loader requires.
How anti-dump defends the loaded image
The key insight behind anti-dump is that a PE's headers are read once, by the loader, at load time. After the CLR has parsed the DOS header, NT headers, section table, and the CLI/metadata directory, the already-running code does not consult them again — the JIT works from structures the runtime has already built. So those header regions are, from the running program's perspective, dead weight after startup, while from a dumper's perspective they are essential: without valid headers, the dumped bytes are not a recognizable assembly.
Anti-dump exploits that asymmetry. A small routine, injected and run early, locates the module's base address in memory (for example via the module handle / Marshal.GetHINSTANCE) and zeroes or scrambles the header regions — the DOS and NT headers, the section table, and the metadata directory pointers. Your program keeps running without noticing. A tool that then dumps the module copies a blob whose front matter is destroyed, so it does not load and does not open in a decompiler until someone manually reconstructs the headers from scratch — a far cry from one-click dumping.
Honest limits, and layering
Anti-dump is a speed bump, not a wall, and it is important to say so plainly. A determined attacker can rebuild PE headers by hand, or dump at a moment before the wipe runs, or reconstruct metadata from the live runtime. Its value is defeating the easy path — the automated dumpers that make lifting an assembly a thirty-second job — and raising the cost of the hard path.
That is exactly why it belongs in a stack, not alone:
- String encryption keeps secrets off disk; anti-dump keeps the decrypted form harder to capture.
- Anti-debugging and anti-tamper make the process hostile to the tools a dumper is usually paired with.
- Code virtualization ensures that even a successful dump yields virtualized methods an attacker still cannot read.
Layered, a dump becomes expensive to take and worth little once taken.
Doing this with Nebula
Nebula applies anti-dump conservatively — corrupting only the header regions the runtime has finished with — so the protection is transparent to your app while a casual memory dump yields an unusable blob. It composes with Nebula's string encryption, anti-debugging, anti-tamper, and (on Pro) code virtualization, so the in-memory image is defended at the same depth as the on-disk file. Where an assembly legitimately reloads its own image at runtime, you can exclude it.
Start with the free edition, or see the plans for the full in-memory protection stack.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.