Malware Tradecraft: Persistence, Injection & C2
Static analysis tells you what a sample could do. Dynamic analysis and unpacking, covered in the last two chapters, tell you what it actually does once it runs. That behavior almost always sorts into three categories: the sample arranges to survive a reboot, it hides its running code inside a process that looks legitimate, and it talks to an operator somewhere on the internet. This chapter walks through the real mechanisms behind all three, and for every mechanism, the artifact a defender uses to catch it.
From Behavior to Tradecraft
By the time you reach this chapter you have hashed a sample, pulled its strings and imports, run it in a sandbox, watched its process tree and network calls, and if it was packed, gotten past the packer to see the real code underneath. What you are looking at now is behavior: API calls, registry writes, new processes, outbound connections. The question this chapter answers is what that behavior is actually accomplishing, and where each piece of it turns into something a defender can alert on.
Almost everything a piece of malware does after initial execution serves one of three goals. It needs to survive a reboot or a user logoff, or the access it just gained disappears the moment the machine restarts, that is persistence. It often wants to run its code inside a process that is not obviously malicious, whether to blend in, to inherit that process's permissions, or to sidestep a security product that only inspects certain binaries closely, that is injection. And unless it is fully autonomous, it needs a way to receive instructions and exfiltrate data from whoever is running it, that is command and control, usually shortened to C2.
These three categories are not independent stages that happen once each. A single intrusion typically establishes persistence more than once as a fallback, injects into more than one process across the lifetime of the compromise, and maintains a beacon that survives process injection being killed by an EDR. The rest of this chapter treats each category as its own section, and every mechanism gets the same treatment: what it does, and what it leaves behind.
Persistence Mechanisms
Persistence answers one question: how does the malware's code run again after the current process ends, whether that is a reboot, a user logoff, or the malware's own process being killed. Windows offers a wide surface for this, and most of it existed for entirely legitimate administrative reasons long before malware authors started using it. The mechanisms below are ordered roughly from most common in commodity malware to stealthier options used when the intrusion needs to survive closer scrutiny.
Registry Run and RunOnce keys
A value written under HKCU\Software\Microsoft\Windows\CurrentVersion\Run, its RunOnce counterpart, or the equivalent HKLM keys for machine-wide persistence, runs automatically at logon. This is the single most common persistence mechanism in commodity malware because it requires one registry write and no elevated privileges when placed under HKCU.
Detection: Sysmon Event ID 13 (RegistryEvent, value set) on the Run/RunOnce key paths. Any write where the value data points at a scripting engine (powershell, wscript, mshta, rundll32) rather than a normal signed application path deserves review, as does a value written by a process other than the expected installer for that key.
Scheduled tasks
A scheduled task created with schtasks.exe or the Task Scheduler COM API runs on a trigger: daily, at logon, at idle, or on a custom event. Scheduled tasks are attractive because the task can be disguised with a name resembling a legitimate Windows component, and the trigger can be tuned to avoid running during working hours when an analyst is likely watching.
Detection: Sysmon Event ID 1 (process creation) for schtasks.exe /create, and separately, Windows Security Event ID 4698 (a scheduled task was created) which fires regardless of which tool created the task. The task's action field, visible in the task XML under C:\Windows\System32\Tasks\, is the payload; an action pointing at a scripting engine with a URL or an encoded argument is a strong signal.
Windows services
A malicious service, either a new service pointing at an attacker binary or an existing service hijacked by changing its ImagePath registry value, runs at boot under the SYSTEM account by default, which makes it attractive for both persistence and privilege escalation in one step.
Detection: Windows Security Event ID 4697 (a service was installed) for new services, and Sysmon Event ID 13 for registry writes to a service's ImagePath value under HKLM\SYSTEM\CurrentControlSet\Services\. A service binary path that resolves outside System32 or Program Files, or that is missing a valid code signature, is worth flagging.
Startup folder
Dropping a shortcut or executable into the per-user or all-users Startup folder is the oldest persistence mechanism on Windows and still works. It is crude, easy to find on a filesystem review, and correspondingly rare in anything beyond low-effort commodity malware.
Detection: Sysmon Event ID 11 (FileCreate) scoped to the Startup folder paths. Because legitimate software rarely writes here outside of an installer, any file creation event in this location from a non-installer process is high-signal.
WMI event subscriptions
A permanent WMI event subscription is built from three objects stored inside the WMI repository rather than on the filesystem: an event filter that defines the trigger condition, an event consumer that defines the action, and a filter-to-consumer binding that connects them. Because none of the three objects are ordinary files, a persistence mechanism built this way survives a filesystem-only forensic sweep untouched, which is exactly why it is the stealthier option on this list.
Detection: Sysmon Event IDs 19, 20, and 21 log WmiEvent filter, consumer, and binding creation respectively. Any WMI consumer whose command line invokes powershell, wscript, or cscript is worth immediate investigation; legitimate WMI consumers calling a scripting engine directly are uncommon.
| Mechanism | Where It Lives | Primary Detection Signal |
|---|---|---|
| Registry Run/RunOnce key | HKCU/HKLM CurrentVersion\Run | Sysmon EID 13, value data pointing at a scripting engine |
| Scheduled task | Task Scheduler, C:\Windows\System32\Tasks | Sysmon EID 1 (schtasks /create), Security EID 4698 |
| Windows service | HKLM\SYSTEM\CurrentControlSet\Services | Security EID 4697, Sysmon EID 13 on ImagePath |
| Startup folder | User or All Users Startup directory | Sysmon EID 11 (FileCreate) scoped to Startup paths |
| WMI event subscription | WMI repository (no filesystem artifact) | Sysmon EID 19/20/21, consumer calling a scripting engine |
Classic DLL and Process Injection
Process injection means getting your code to execute inside the address space of a process you did not start with your own malicious code as the entry point. The reasons attackers do this vary: to run inside a process with useful permissions or network trust, to blend malicious activity into the process list under a legitimate-looking name, or to survive the termination of the process that originally launched the payload.
The textbook technique, taught in essentially every introductory malware analysis course and Windows internals reference, is a three-call sequence. VirtualAllocEx allocates a region of executable memory inside the target process. WriteProcessMemory writes the malicious code or DLL path into that region. CreateRemoteThread (or the lower-level NtCreateThreadEx) starts a new thread inside the target process pointed at the written code, or in the DLL injection variant, pointed at LoadLibrary with the malicious DLL's path as the argument.
Event ID 8: CreateRemoteThread
UtcTime: 2026-09-10 14:22:07.481
SourceImage: C:\Users\victim\AppData\Local\Temp\update.exe
TargetImage: C:\Windows\System32\svchost.exe
NewThreadId: 4412
StartAddress: 0x000001F3A2C40000
StartModule: (none, address not backed by a mapped module)
That log line is the reason this exact API sequence is one of the most heavily signatured behavioral patterns in modern EDR. A non-Microsoft process creating a remote thread inside svchost.exe, with a start address that does not correspond to any module actually mapped into that process, is close to a textbook indicator of malicious code execution. Sysmon Event ID 8 (CreateRemoteThread) and Event ID 10 (ProcessAccess, logging the OpenProcess/WriteProcessMemory handle sequence that precedes it) together give a defender both halves of the pattern.
Because this pattern is so well covered, it is also the reason more evasive injection variants exist. If CreateRemoteThread into a foreign process reliably trips an alert, an attacker who wants their injection to survive has to either avoid that API, avoid the classic allocate-write-execute sequence entirely, or avoid the remote thread creation step. The next two sections cover exactly that progression, and chapter 8 returns to this same theme from the other direction: how modern evasive malware tries to defeat the detections paired against each mechanism in this chapter.
Process Hollowing
Process hollowing solves a specific problem with classic injection: the target process's own code is still running, and the injected thread is an obvious addition sitting alongside it. Hollowing instead replaces the process's own code before it ever runs.
What hollowing cannot fake is the relationship between what is on disk and what is actually mapped in memory. The file on disk at C:\Windows\System32\svchost.exe is untouched and legitimately signed. The in-memory image inside that running process is not what that file contains. That mismatch is the detection angle: a memory-forensics or EDR capability that compares the PE header and section characteristics of a process's on-disk image against what is actually mapped at its base address will catch the substitution immediately.
Detection: Tools built for exactly this check (Volatility's hollowfind/malfind plugins in an offline memory dump, or an EDR's live equivalent) look for a mismatch between the on-disk file hash and the mapped image, a base address whose section permissions or entry point do not match the legitimate binary's known-good PE structure, or a VAD (Virtual Address Descriptor) region marked executable that does not correspond to any file-backed mapping. Unusual parent-child process relationships are a second, cheaper signal: a hollowed svchost.exe whose parent is not services.exe, or an explorer.exe whose parent is not userinit.exe, is already anomalous before any memory inspection happens.
APC Injection and Reflective DLL Loading
Two further variants push injection technique further away from the signatures built around the classic pattern, each by removing a different piece of what defenders learned to watch for.
APC injection
Asynchronous Procedure Calls are a normal Windows mechanism for queuing a function to run inside a specific thread, but only when that thread enters what is called an alertable wait state, a point where it is idle and explicitly willing to run queued callbacks. APC injection abuses this by using QueueUserAPC to queue malicious code onto a thread inside a target process. The code does not run immediately, it waits until the target thread's own execution naturally reaches an alertable wait, at which point Windows runs the queued function on the attacker's behalf. Because the injected code rides on a thread the target process already owns, there is no new remote thread creation event to log, which is precisely the artifact that made classic injection so detectable.
Reflective DLL loading
Reflective loading solves a different problem: even DLL injection via LoadLibrary still calls the normal Windows loader, which means the DLL gets written to disk (or memory-mapped from disk) and shows up in a process's module list, generating standard module-load telemetry. A reflectively loaded DLL is instead mapped and initialized entirely by hand: the attacker's own loader code resolves the DLL's imports, applies its base relocations, and calls its entry point manually, all from a memory buffer that was never a file on disk and never passed through LoadLibrary. The DLL never appears in the target process's module list because, from the loader's perspective, it was never loaded at all.
Both techniques share the same underlying detection theme: a region of memory marked executable that does not correspond to a normal, file-backed module mapping. Standard module-load telemetry (Sysmon Event ID 7, ImageLoad) is blind to reflectively loaded code by design, since that event only fires for the normal loader path. What remains visible is the memory layout itself: an executable VAD region with no backing file, often allocated with RWX permissions rather than the more restrictive combination a legitimate module would use.
| Injection Technique | Core Mechanism | Primary Detection Signal |
|---|---|---|
| Classic DLL/process injection | VirtualAllocEx + WriteProcessMemory + CreateRemoteThread | Sysmon EID 8 (CreateRemoteThread) + EID 10 (ProcessAccess) from a non-standard source process |
| Process hollowing | Suspended process, NtUnmapViewOfSection, remap payload, resume thread | On-disk image vs. in-memory PE header mismatch, anomalous parent-child relationship |
| APC injection | QueueUserAPC onto a thread's alertable wait state | Executable memory region with no backing module, no CreateRemoteThread event present |
| Reflective DLL loading | Manual PE mapping and entry point call, bypassing LoadLibrary | Executable VAD with no file-backed mapping, absence of expected ImageLoad (EID 7) event |
Notice the pattern across all four rows: as each technique removes one specific artifact that a defender used to catch the previous one, it always leaves behind the same underlying tell, code running from memory that has no legitimate reason to be executable. Memory-scanning EDR capability that inspects VAD permissions and file backing, rather than relying only on API call hooking, is what closes this gap across all four variants at once.
C2 Communication and Beaconing Patterns
Once code is running and persistent, it usually needs to reach an operator. The dominant architecture for this across commodity and targeted malware alike is the beacon. This is deliberately unlike an interactive reverse shell, which holds an open connection; a beacon connects briefly, on a schedule, and disconnects, which is both more resilient to network interruption and harder to spot in a stream of ordinary traffic.
Beacons most commonly ride over HTTP or HTTPS, because that traffic blends into the overwhelming majority of normal outbound enterprise traffic and passes through most firewalls without special rules. DNS is the other common channel: a beacon that encodes tasking and results into DNS query and response fields can reach a controller through networks that block essentially all other outbound traffic but still permit basic name resolution.
A beacon that checks in at exactly sixty second intervals, forever, is trivial to spot once you know to look for perfectly periodic traffic in a connection log. To avoid this, beacons apply jitter: instead of a fixed interval, the actual wait before the next check-in is randomized within a range around a base value, for example a sixty second base interval with plus or minus twenty percent jitter. This breaks naive interval-matching detection while still keeping the beacon's average check-in frequency predictable enough for the operator to rely on.
14:00:03 POST /api/v2/update 198.51.100.44:443 312 bytes out / 88 bytes in
14:01:11 POST /api/v2/update 198.51.100.44:443 308 bytes out / 91 bytes in
14:00:51 POST /api/v2/update 198.51.100.44:443 310 bytes out / 84 bytes in
14:01:47 POST /api/v2/update 198.51.100.44:443 309 bytes out / 90 bytes in
# base interval ~60s, jitter roughly +/-20%, near-constant payload size
Two further techniques let C2 traffic blend past network-layer detection entirely. Domain fronting routes the encrypted connection through a large, widely trusted CDN or cloud front-end, so that the TLS handshake and any network-visible metadata point at a benign, high-reputation hostname while the actual HTTP Host header inside the encrypted session directs the request to the attacker's real backend, letting the traffic ride the reputation of infrastructure a firewall would never block. Abuse of legitimate cloud collaboration platforms and CDNs works on a similar principle without needing the fronting trick at all: an implant that uses a popular file-sharing or messaging platform's own API as its transport channel produces traffic that is, at the network layer, indistinguishable from a legitimate user of that platform, because it genuinely is talking to that platform's real infrastructure.
Detection: None of this defeats detection that focuses on statistical behavior rather than raw destination or protocol. Beacon interval and jitter analysis flags a host making outbound connections at a suspiciously narrow, repeating range of intervals, even when the destination itself looks clean. JA3 and JA3S TLS fingerprinting identifies the specific TLS client library and configuration an implant uses, which frequently differs from the fingerprint of the legitimate browser or application the traffic is trying to imitate, regardless of what domain the connection is aimed at. DNS query pattern anomalies, unusually long subdomains, high query volume to a single domain, or query entropy consistent with encoded data rather than real hostnames, catch DNS-based beacons even when the destination domain has no prior bad reputation.
Bringing It Together
Every artifact named across this chapter, a Run key value pointing at a scripting engine, a memory region marked executable with no backing file, a beacon's interval and jitter profile, is raw material. On its own, one registry write or one HTTP POST proves very little. Turned into a written detection rule with defined thresholds and exclusions, the same artifact becomes something a SOC can act on at scale. Chapter 6 picks up exactly there, converting the mechanisms and signals from this chapter into actual YARA rules for static and memory-resident indicators and Sigma rules for the behavioral and log-based signals, both mapped back to the ATT&CK techniques they cover.
One honest caveat belongs here. Every detection signal described in this chapter, from CreateRemoteThread monitoring to beacon jitter analysis, has a matching evasion technique that sophisticated malware uses to try to defeat it. Chapter 8 covers that side directly: anti-VM and anti-debug tricks, AMSI and EDR evasion, and fileless techniques that specifically target the assumptions behind the detections paired against each mechanism in this chapter. Reading this chapter without that one is only half the picture, but this chapter is the half that has to come first, because you cannot reason about evading a detection you do not yet understand.
Key Takeaways
- Malware behavior after execution almost always serves one of three goals: persistence, process injection, or C2 communication. Each has its own dominant mechanisms and its own detection surface.
- Persistence mechanisms range from Run keys and scheduled tasks, both leaving clear registry and event-log artifacts, to WMI event subscriptions, which live entirely in the WMI repository and evade filesystem-only forensics.
- Classic process injection (VirtualAlloc, WriteProcessMemory, CreateRemoteThread) is one of the most heavily signatured behavioral patterns in modern EDR, which is exactly why more evasive variants exist.
- Process hollowing makes a process's on-disk image and in-memory contents diverge; that mismatch, plus anomalous parent-child relationships, is what catches it.
- APC injection and reflective DLL loading each remove one specific artifact defenders rely on (remote thread creation, or normal module-load telemetry), but both still leave behind executable memory with no legitimate file backing.
- C2 beacons use jitter, domain fronting, and legitimate cloud service abuse to blend into normal traffic; statistical beacon analysis, TLS fingerprinting, and DNS pattern analysis catch what naive domain or protocol filtering misses.
Knowledge Check
Click an answer to reveal the explanation.
Which persistence mechanism is specifically designed to survive a filesystem-only forensic review, because none of its components exist as files on disk?
A process named svchost.exe is running on a host. Its file on disk is a legitimately signed, unmodified Windows binary. What detection approach would still catch this process if it had been created through process hollowing?
A beacon checks in over HTTPS at intervals that vary between 48 and 72 seconds around a 60-second base, and its destination domain has no history of abuse. Which detection approach is most likely to flag it regardless of the clean domain reputation?