Detecting Process Injection
How to detect process injection — CreateRemoteThread, RWX memory, and process hollowing signals in Sysmon, a Sigma rule, and the tuning that keeps it high-fidelity.
Process injection is how malware hides in plain sight — it runs its code inside a
legitimate process to inherit that process’s trust, evade process-based detection,
and slip past application allowlists. Because the malicious code lives inside
explorer.exe or a browser, you cannot detect it by looking for a bad process. You
detect the act of injection: a remote thread created in another process, a handle
opened with write-execute access, RWX memory allocated. This guide ships those
signals and the Sigma rule that catches them.
Process injection maps to MITRE ATT&CK T1055 — Process Injection and its many sub-techniques (DLL injection, process hollowing, thread hijacking). It is a core defense-evasion step, and it depends on the same Sysmon telemetry as the rest of the Windows detection cluster.
What is process injection?
Process injection places attacker code into the memory of another, legitimate process and executes it there. The malware might allocate memory in a target process, write its payload, and start a thread to run it (classic DLL/shellcode injection); or carve out a legitimate process and replace its image (process hollowing); or hijack an existing thread. In every case the goal is the same: run as a trusted process to evade detection and inherit its privileges and network reputation.
That is why hunting for a malicious executable fails — there isn’t one running as itself. The reliable signals are the Windows API behaviors injection requires, which Sysmon surfaces as discrete events.
What does process injection look like in telemetry?
| Technique | The injection step | Telemetry signal |
|---|---|---|
| Remote-thread injection | CreateRemoteThread into a target | Sysmon Event ID 8 |
| Handle-based injection | Open target with VM_WRITE/VM_OPERATION | Sysmon Event ID 10 (access mask) |
| Shellcode execution | Allocate/mark RWX memory | RWX allocation (EDR / EID 8 start address) |
| Process hollowing | Unmap and replace the process image | Sysmon Event ID 25 (process tampering) |
The unifying signal is one process reaching into another’s memory or threads. A source
process that is not a debugger or security tool creating a remote thread in
explorer.exe is injection — the discriminator is which source and which target.
How to detect process injection
The core rule flags a remote thread created in another process (Sysmon Event ID 8), allowlisting known-good source processes.
title: Remote Thread Created in Another Process
id: 6f2c9a41-darkpwn-illustrative
status: experimental
logsource:
product: windows
category: create_remote_thread
detection:
selection:
TargetImage|endswith: ['\explorer.exe','\svchost.exe','\lsass.exe','\winlogon.exe']
filter_known:
SourceImage|endswith: ['\MsMpEng.exe','\CSFalconService.exe']
condition: selection and not filter_known
falsepositives:
- Debuggers, security tools, and some installers (allowlist by SourceImage/signer)
level: high The chained detection in SPL and KQL
index=windows sourcetype=sysmon (EventCode=8 OR EventCode=3 OR EventCode=10)
| eval key = Computer . "|" . TargetProcessGuid
| bin _time span=5m
| stats values(EventCode) AS events, values(SourceImage) AS injectors,
values(DestinationIp) AS dests, min(_time) AS first by key, Computer, TargetImage
| eval injected = if(mvcount(mvfilter(match(events,"^8$"))) > 0, 1, 0)
| eval then_egress = if(mvcount(mvfilter(match(events,"^3$"))) > 0, 1, 0)
| eval then_lsass = if(mvcount(mvfilter(match(events,"^10$"))) > 0, 1, 0)
| where injected=1 AND (then_egress=1 OR then_lsass=1)
| eval severity = if(then_lsass=1, "critical", "high")
| table first, Computer, TargetImage, injectors, severity, dests DeviceEvents
| where ActionType in ("CreateRemoteThreadApiCall", "ProcessPrimaryTokenModified",
"WriteToProcessMemoryApiCall", "ReadProcessMemoryApiCall")
| extend Target = InitiatingProcessFileName, Injector = FileName
| summarize Actions = make_set(ActionType), Count = count()
by DeviceName, Target, Injector, bin(Timestamp, 5m)
| where array_length(Actions) >= 2 // write + execute, not a single benign API call
| order by Count desc The Splunk query encodes the point of the callout above: the remote thread is the setup and the
follow-on is the confirmation. Correlating on TargetProcessGuid is what makes it reliable —
PIDs get recycled, GUIDs do not.
Why Event ID 8 is not enough
Sysmon’s CreateRemoteThread event catches the classic injection pattern and misses several
common modern ones, which is worth knowing before you rely on it.
| Technique family | Creates a remote thread? | What detects it instead |
|---|---|---|
| Classic remote thread | Yes — Event ID 8 | The rule above |
| Process hollowing | Usually not | Sysmon Event ID 25 (process tampering); image/memory mismatch |
| Thread hijacking | No — reuses an existing thread | Unbacked memory execution; EDR API telemetry |
| APC injection | No | Queued APC to an alertable thread; EDR telemetry |
| DLL injection via load | No thread creation | Sysmon Event ID 7 — module loaded from an unusual path |
| Reflective / manual mapping | Sometimes | Unbacked memory — no file behind the executing region |
The unifying signal across the bottom half of that table is execution from memory that is not backed by a file on disk. Legitimate code executes from mapped images; injected code frequently does not. If your EDR exposes that property, it is a stronger primary detection than Event ID 8 because it is a property of the technique class rather than of one API.
This is the same discriminator as the CallTrace UNKNOWN frames used in
LSASS credential dumping detection — the call
originated from memory with no file behind it. One telemetry property, two detections.
Which T1055 sub-techniques should you actually prioritise?
MITRE tracks twelve sub-techniques under T1055, and treating them as a single detection problem is why coverage claims tend to be optimistic. Three are Linux-only, several are rare enough that a dedicated rule costs more to maintain than it returns, and the ones that carry real-world volume concentrate in a narrow band.
| Sub-technique | ID | Platform | Detection anchor | Priority |
|---|---|---|---|---|
| Dynamic-link Library Injection | T1055.001 | Windows | EID 8, plus EID 7 for a module from an unusual path | High |
| Portable Executable Injection | T1055.002 | Windows | Unbacked-memory execution | High |
| Thread Execution Hijacking | T1055.003 | Windows | EID 10 with suspend/resume rights; no EID 8 | High |
| Asynchronous Procedure Call | T1055.004 | Windows | EDR API telemetry only | Medium |
| Thread Local Storage | T1055.005 | Windows | Unbacked-memory execution | Low |
| Ptrace System Calls | T1055.008 | Linux | auditd rule on the ptrace syscall | Medium (Linux) |
| Proc Memory | T1055.009 | Linux | Writes to /proc/<pid>/mem | Medium (Linux) |
| Extra Window Memory Injection | T1055.011 | Windows | EDR only; very rare | Low |
| Process Hollowing | T1055.012 | Windows | EID 25 (process tampering) | High |
| Process Doppelgänging | T1055.013 | Windows | Transacted file operations; EDR | Low |
| VDSO Hijacking | T1055.014 | Linux | ptrace plus memory-map anomaly | Low (Linux) |
| ListPlanting | T1055.015 | Windows | EDR only | Low |
Four sub-techniques carry most of the volume you will actually meet: DLL injection, PE injection, thread hijacking, and hollowing. Two Sysmon events and one EDR property cover all four — Event ID 8, Event ID 25, and unbacked-memory execution. That is the whole high-priority programme.
Read the platform column before claiming T1055 coverage in a control review. If your estate runs
Linux workloads and your only injection telemetry is Sysmon on Windows, three sub-techniques are
structurally invisible and no amount of Sigma tuning changes it. The Linux equivalents are an
auditd rule on the ptrace syscall and integrity monitoring on /proc/<pid>/mem — a different
pipeline, usually a different owner, and almost always a different backlog.
What does the ProcessAccess mask actually tell you?
Sysmon Event ID 10 records the access rights one process requested when opening a handle to
another, in the GrantedAccess field. That hexadecimal value is the most useful discriminator in
the entire injection dataset, and most rules throw it away — they alert on the source-target pair
and inherit every false positive that opening a handle for any benign reason produces.
| Access right | Value | What it enables |
|---|---|---|
PROCESS_CREATE_THREAD | 0x0002 | Start a thread in the target |
PROCESS_VM_OPERATION | 0x0008 | Allocate memory or change its protection |
PROCESS_VM_READ | 0x0010 | Read the target’s memory |
PROCESS_VM_WRITE | 0x0020 | Write the target’s memory |
PROCESS_QUERY_INFORMATION | 0x0400 | Read process metadata |
PROCESS_SUSPEND_RESUME | 0x0800 | Suspend and resume threads |
PROCESS_QUERY_LIMITED_INFORMATION | 0x1000 | Read a metadata subset |
Injection has to write. Credential theft only has to read. That one distinction splits Event ID 10 into two very different alerts:
- Write-capable masks — anything containing
0x0002,0x0008, or0x0020. The classic injection triad is0x002A: create-thread, VM-operation, VM-write combined. This is injection setup. - Read-only masks —
0x1010and0x1410are the well-known LSASS-reading combinations. That is credential dumping, a different playbook with a different containment decision. 0x1FFFFF—PROCESS_ALL_ACCESS. Lazy tooling asks for everything, which makes it both a loud signal and a trivial one to catch.
Filtering Event ID 10 on write-capable masks removes a large share of benign volume before it ever reaches a rule, because most legitimate handle opens are queries. It also prevents the common misclassification where an LSASS read is triaged as injection and the responder spends the first hour of an incident hunting for a payload that was never written.
How do you hunt for injection when no rule fired?
Alerting covers the patterns you anticipated. Hunting covers the rest, and injection is a technique class where hunting is unusually productive, because the behaviour is structurally rare in normal operation. Three hypotheses are worth running on a schedule:
- Rarest source-target pairs. Aggregate Event ID 8 and write-capable Event ID 10 across the
fleet by
SourceImage→TargetImage, then sort ascending by host count. Your EDR injecting appears on every host; a one-off binary injecting appears on one. Frequency analysis finds the second without knowing in advance what it is. - Signer anomalies on injectors. Any process that injects and is unsigned, signed by a certificate seen on a handful of hosts, or running from a user-writable path is worth reviewing individually. The volume is low enough to review by hand, which is what makes this hypothesis practical rather than aspirational.
- Injectors that are themselves unbacked. A process injecting from memory with no file behind it is stage two of an intrusion. This is the highest-signal hunt of the three and returns almost nothing on a clean estate — which is the property you want. A hunt that always returns results is a tuning problem wearing a hunt’s clothing.
Run these against the same telemetry the alerts use. When a hunt repeatedly surfaces the same benign pattern, that is an allowlist input; when it surfaces something new, it becomes a rule. That feedback loop is the detection engineering workflow applied to a single technique.
Which false positives will you actually see?
| False positive | Why it happens | Resolution |
|---|---|---|
| EDR and AV agents | They inject to hook and inspect processes | Allowlist by signer, re-verify after every upgrade |
| Debuggers and profilers | Attaching is their entire function | Scope to developer workstations; alert elsewhere |
| APM and instrumentation agents | Attach to runtimes to trace | Allowlist the specific agent path and signer |
| Accessibility software | Legitimately injects into UI processes | Allowlist by signer |
| Installers and updaters | Patch running processes in place | Correlate with a software-deployment record |
| Anti-cheat and DRM | Inject by design | Rare in enterprise; allowlist explicitly if present |
| Legacy IME / input tools | Inject into every process | Inventory and plan removal; they are also a real risk |
Re-verify agent allowlists after upgrades. Security vendors change binary paths and add helper processes between versions, and the failure mode is silent in both directions — either a flood of new alerts, or an allowlist entry that matches nothing and quietly stopped suppressing what it was written for.
How do you triage a process-injection alert?
- Identify the injector and the target. An unsigned or unusual process injecting into
lsass.exe,explorer.exe, or a browser is high severity immediately. - Check whether the injector is backed by a file on disk and whether that file is signed. An injector running from unbacked memory is itself already-injected code — you are seeing stage two, not stage one.
- Look at what the target did next. Outbound connections, LSASS access, or a spawned shell confirm the injection was operational rather than incidental.
- Trace the injector’s parent chain. This usually reveals the initial access vector — a document handler, a scripting engine, or a service process.
- Capture memory before killing anything. Injected code exists only in memory; terminating the process destroys the evidence and you lose attribution.
- Isolate the host, then look for persistence established from the injected context.
- Rotate credentials that were resident on that host, particularly if
lsass.exewas the target. - Hunt for the same injector hash and parent pattern fleet-wide. Injection tooling is reused, so one detection is usually not one host.
Step 5 is the one most often skipped under pressure, and it is the difference between knowing what the intrusion was and only knowing that there was one.
How to test your process-injection detection
In an isolated VM you own:
- Use a benign injection test (an Atomic Red Team T1055 test, or a lab tool that injects a harmless payload) and confirm Sysmon Event ID 8/10 fire.
- Confirm your allowlisted security tools do not trip the rule.
- Run a process-hollowing test and confirm Event ID 25 (process tampering) appears.
- Verify the rule survives a renamed injector — it keys on the behavior, not the name.
How to reduce the process-injection attack surface
- Monitor EDR health — attackers inject after unhooking or disabling it via BYOVD.
- Collect the right Sysmon events (EID 8, 10, 25) — see Sysmon configuration.
- Baseline legitimate injectors so the allowlist is accurate.
Which surface-reduction control buys the most?
| Control | What it removes | Effort | Caveat |
|---|---|---|---|
| ASR: block Office child processes / injection | The most common initial-access path | Low | Audit mode first; a few macros break |
| EDR tamper protection | The unhook-then-inject sequence | Low | Vendor-dependent completeness |
| Credential Guard | The payoff of LSASS-targeted injection | Medium | UEFI/Secure Boot prerequisites |
| WDAC / application allowlisting | An injected loader running arbitrary binaries | High | Real programme; huge payoff |
| Microsoft vulnerable-driver blocklist | The BYOVD precursor to kernel-mode injection | Very low | Keep it updated |
| Removing legacy injecting software | Persistent noise and real risk | Varies | Old IME and accessibility tools |
Start with the ASR rules in audit mode. Blocking Office applications and script hosts from creating child processes or injecting removes the single most common route to a first injection, and audit mode tells you exactly what would break before anything does.
The last row is worth acting on rather than tuning around. Legacy software that injects into every process is simultaneously your loudest false-positive source and a genuine risk — anything with that capability is a target for hijacking. Removing it improves detection precision and security posture with one change.
Common process-injection detection mistakes
- Hunting for a bad process. There isn’t one — the code runs inside a good process.
- No CreateRemoteThread/ProcessAccess telemetry. Without EID 8/10, the act is invisible.
- No source-target allowlist. The rule pages on debuggers and security tools.
- Ignoring EDR tampering. Injection often follows the EDR being unhooked.
- Relying on Event ID 8 alone. Hollowing, thread hijacking, and APC injection create no remote thread and are invisible to it.
- Correlating on PID instead of ProcessGuid. PIDs are recycled; the chain breaks silently.
- Killing the process before capturing memory. Injected code exists only in memory.
- Never re-verifying agent allowlists. Vendor upgrades change paths, and the failure is silent in both directions.
Process injection detection checklist
- Collect Sysmon Event IDs 8 (remote thread), 10 (process access), and 25 (tampering).
- Alert on remote threads/handles into system and high-value processes.
- Allowlist legitimate injectors by SourceImage and code-signing certificate.
- Correlate injection with the target’s subsequent network/credential activity.
- Enable ASR rules blocking Office/script-host injection and child processes.
- Run Credential Guard and WDAC to limit the payoff and the payload.
- Monitor EDR health and driver loads (BYOVD precursor).
- Test with Atomic Red Team T1055 and a renamed injector.
- Collect Sysmon Event IDs 8, 10, and 25 — hollowing is invisible without 25.
- Correlate on ProcessGuid, never PID; PIDs are recycled and break the chain silently.
- Chain the injection event to what the target does next — egress, LSASS access, or a shell.
- Where your EDR exposes it, alert on unbacked-memory execution as a primary signal; it covers the technique families Event ID 8 cannot see.
- Allowlist injectors by signer and path, and re-verify after every agent upgrade.
- Capture memory before terminating anything — injected code exists nowhere else.
- Enable ASR rules blocking Office and script hosts from injecting, in audit mode first.
- Inventory and remove legacy software that injects into every process; it is both your noisiest false positive and a real hijacking target.
Items 10 and 12 are the two that most often separate a rule that looks correct from one that works. PID recycling silently breaks correlation on busy hosts, and unbacked-memory execution is the only signal that generalizes across the injection families a single API event misses.
The takeaway
Detecting process injection means watching the act — CreateRemoteThread, write/execute handles, RWX memory, process tampering — from unexpected source processes, allowlisted to your real injectors, and chained to the payoff. You cannot find a bad process; find the injection. Continue with LSASS credential dumping detection and BYOVD detection, or browse the full Detection Engineering pillar.
Training & tools referenced
Disclosure: Some links below are affiliate links. If you buy through them, darkpwn may earn a commission at no extra cost to you. We only recommend training and tools we actually use in our own lab, and affiliate links never influence editorial coverage.
- TryHackMeAuthorized labs to practice detecting process injection on WindowsSecurity TrainingStart training
Frequently asked questions
How do you detect process injection?
Watch the Windows API behaviors injection needs: a remote thread created in another process (Sysmon Event ID 8, CreateRemoteThread), a process opening another with write/execute access (Event ID 10), allocation of read-write-execute (RWX) memory, and process image/memory tampering (Event ID 25). Alert on these from unexpected source processes, allowlisting legitimate injectors like security tools.
What is process injection?
Process injection is running malicious code inside the address space of a legitimate process to evade defenses and inherit that process's trust and privileges. Techniques include DLL injection, process hollowing, and thread execution hijacking. It maps to MITRE ATT&CK T1055 and its sub-techniques.
Why do attackers use process injection?
Injecting into a trusted process (like explorer.exe or a browser) lets malware hide from process-based detection, bypass application allowlists, and run under the identity and network reputation of the host process. It is a core defense- evasion technique used by most modern malware and C2 frameworks.
Does Sysmon Event ID 8 catch all process injection?
No. CreateRemoteThread catches the classic pattern but misses process hollowing, thread hijacking, APC injection, and DLL injection via a normal module load, none of which create a remote thread. The unifying signal across those techniques is execution from memory that is not backed by a file on disk, which is a stronger primary detection where your EDR exposes it because it is a property of the technique class rather than of one API.
Can EDR stop process injection?
Modern EDR detects many injection techniques via userland hooks and kernel telemetry, but attackers evade with direct/indirect syscalls, BYOVD to unhook the EDR, and novel injection variants. Behavioral detection on the underlying API sequence, plus EDR-health monitoring, remains necessary.