← back to theory
Red Team

In-Memory Post-Exploitation Tradecraft

A modern operator almost never drops a tool to disk and runs it. The whole toolkit — credential dumpers, .NET enumerators, service-abuse helpers — is executed in the memory of an existing process, so antivirus never scans a file and the PowerShell logging layers never see a script. This page is how that is done: injection, the fork-and-run vs inline choice, execute-assembly and BOFs, and the OPSEC of process lineage. It is the mechanism behind a large share of the commands in the Toolkit.

The core primitive: process injection

Injection means placing your code into a process's address space and getting a thread to run it. Classically: allocate executable memory in a target process, write your shellcode there, and start execution (a remote thread, a queued APC, a hijacked thread). Whether the target is a brand-new process you spawn or one already running, the point is the same — your code runs under another process's identity and image, so on the surface it is svchost.exe or explorer.exe doing the work, not an unknown binary.

Which process you pick is an OPSEC decision, not a detail. Some choices signal intent (lsass.exe for credential theft), some are chosen purely to blend in (svchost.exe, dllhost.exe, RuntimeBroker.exe, taskhostw.exe), and some help defeat application-control (regsvr32.exe, backgroundtaskhost.exe). Injecting into a process that runs often and lives briefly is a way to disappear into normal activity.

Fork-and-run vs inline

When an implant runs an external tool, it has two strategies, and the difference is central to both stealth and stability.

TechniqueHow it worksTrade-off
Fork-and-runSpawn a temporary sacrificial process, inject the tool's code into it, run it, capture output, then kill the processSafe — a crash takes the sacrificial process, not the implant. But spawning a process (and the parent/child relationship it creates) is a detection opportunity
Inline / self-injectRun the tool inside the implant's own process, no new process spawnedQuieter — no new process, no telltale lineage. But a crash or a detected action takes the implant with it, so it is riskier
This is the same stealth-versus-safety dial as beacon-versus-session, one level down. Fork-and-run is the resilient default; inline execution is what you reach for when the extra process itself would be the thing that gets you caught — and it is where the implant's built-in AMSI/ETW bypass applies, since the code runs in a process the implant already controls.

execute-assembly: running .NET in memory

The bulk of the offensive C# toolset — Rubeus, SharpUp, Seatbelt, Certify, SharpHound and the rest — are .NET assemblies. execute-assembly loads the .NET runtime (CLR) into a process and runs the assembly's bytes straight from memory, passing arguments as if it were launched normally. Nothing is written to disk, and because it never goes through the PowerShell engine, script-block and transcription logging are blind to it. It works in both modes: fork-and-run into a chosen (optionally PPID-spoofed) process, or inline into the implant with AMSI/ETW patched first.

Not every assembly is compatible, and a tool that shells out or spawns children reintroduces the very process events inline execution was meant to avoid. The tradecraft is choosing the right execution method for each tool, not applying one blindly.

BOFs — the lighter alternative

A Beacon Object File (BOF/COFF) is a small compiled object loaded and run inside the implant's own process, using the implant as a mini-linker. Because there is no new process, no CLR to host, and a tiny footprint, BOFs are the quiet way to do focused post-exploitation tasks. Frameworks distribute them as extensions (Sliver's armory does exactly this), which is why an operator can run many “native” commands that are really in-process modules.

PPID spoofing and process migration

Endpoint detection leans heavily on process lineage — who spawned whom. An illegitimate parent/child pair (WINWORD.exe spawning cmd.exe, or rundll32.exe with no parent reason) is a classic indicator. Two techniques bend this to the operator's advantage.

TechniqueWhat it does
PPID spoofingStart a process with an arbitrary parent, so the new process appears to have been launched by a benign, expected one. Mimic real relationships (e.g. svchost.exe → RuntimeBroker.exe / taskhostw.exe) so a fork-and-run doesn't stand out in a process tree
Process migrationMove the running implant from its current host process into a different, longer-lived or more plausible one — leaving the original process (and its risk) behind

Getting a tool into shellcode

Injection needs position-independent shellcode, but most tools are PE executables or .NET assemblies. Packers bridge the gap: Donut turns a .NET/PE payload into shellcode that reflectively loads and runs it, and wrappers like PEzor add evasion (unhooking, anti-debug, sleep obfuscation, output as .NET or shellcode) so the result both runs in memory and resists inspection. This is how a stock mimikatz.exe or a PowerShell-derived executable becomes something an implant can inject and execute-assembly.

Unhooking

EDR products insert user-mode hooks into common API functions to watch calls from inside a process. Loaders counter this by unhooking — restoring the original, clean copies of those functions (typically by reloading a pristine ntdll from disk) — or by calling the underlying syscalls directly, so the sensitive operations happen out of the EDR's view. It is the endpoint-level equivalent of the AMSI/ETW patch: quietly remove the observation point before doing the noisy thing.

The takeaway

In-memory tradecraft is a stack of choices around one idea — run your code inside a process that is already trusted, and touch disk as little as possible. Injection provides the primitive; fork-and-run versus inline sets the stealth/safety balance; execute-assembly and BOFs run the standard toolset without dropping files; PPID spoofing and migration launder process lineage; and packers, unhooking and AMSI/ETW bypasses smooth over what defenders watch. Every one is a deliberate trade of noise for control. The tools that implement these — and the Sliver commands that drive them — are in the Toolkit.

Related reading