Detecting Process Injection (T1055): A SOC Analyst's KQL & SPL Guide
Process injection is the technique your EDR vendor’s marketing slides promise to catch and your SOC queue rarely surfaces cleanly. It is the load-bearing wall of modern intrusions: Cobalt Strike, Brute Ratel, every commodity loader, and nearly every ransomware affiliate uses it to run malicious code inside a trusted process so that the malicious activity inherits a legitimate process’s identity, network reputation, and parent lineage.
MITRE ATT&CK tracks this as T1055, and it is not one technique — it is a family of at least a dozen sub-techniques that share a goal but differ wildly in the telemetry they produce. Writing one detection rule for “process injection” is how teams end up with either 4,000 alerts a day or zero true positives. This guide walks through how to detect the four sub-techniques that actually matter, with production KQL for Microsoft Sentinel / Defender and SPL for Splunk pulled directly from the df00tech detection library.
1. Why Process Injection Resists Signature Detection
There is no single API call that means “injection.” The canonical chain is OpenProcess → VirtualAllocEx → WriteProcessMemory → CreateRemoteThread, but every step has a legitimate use, and attackers rotate through alternatives — NtMapViewOfSection instead of WriteProcessMemory, QueueUserAPC or SetThreadContext instead of CreateRemoteThread. Allocation and write primitives are largely invisible to standard Windows logging.
The practical consequence: you do not detect injection by looking for one event. You detect it by correlating a suspicious source (an injection-capable process doing something unusual) with a suspicious target (a trusted process behaving abnormally). Each sub-technique gives you a different correlation to anchor on.
2. DLL Injection — T1055.001
The most common variant. The attacker writes a DLL path into the target’s address space and triggers LoadLibrary via a remote thread. The detectable artifact is a CreateRemoteThread API call followed by the target process loading a DLL from a non-standard path. This KQL from T1055.001 joins the injection event to the resulting image load:
// Detect DLL injection via CreateRemoteThread targeting LoadLibrary
let SuspiciousInjectors = dynamic(["rundll32.exe", "regsvr32.exe", "mshta.exe", "wscript.exe", "cscript.exe", "powershell.exe", "cmd.exe"]);
DeviceEvents
| where Timestamp > ago(24h)
| where ActionType == "CreateRemoteThreadApiCall"
| project Timestamp, DeviceName, AccountName,
InitiatingProcessFileName, InitiatingProcessId,
FileName, ProcessId
| join kind=leftouter (
DeviceImageLoadEvents
| where Timestamp > ago(24h)
| where FileName !startswith "C:\\Windows\\System32\\"
| where FileName !startswith "C:\\Windows\\SysWOW64\\"
| where FileName !startswith "C:\\Program Files"
| project DeviceName, ProcessId=InitiatingProcessId, LoadedDLL=FileName, SHA256
) on DeviceName, ProcessId
| where isnotempty(LoadedDLL)
| extend SuspiciousSource = InitiatingProcessFileName in~ (SuspiciousInjectors)
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, LoadedDLL, SHA256, SuspiciousSource
| sort by Timestamp desc
The SuspiciousSource flag is your triage accelerator: a CreateRemoteThread from rundll32.exe or powershell.exe is far more interesting than one from a signed installer. These are the same LOLBins you should already be watching for living-off-the-land abuse.
3. Process Hollowing — T1055.012
Hollowing creates a legitimate process suspended, unmaps its memory, and replaces it with malicious code before resuming. The tell is structural, not API-based: a trusted binary like svchost.exe running with the wrong parent and an empty command line, because the real command line is never set when a process is created with CREATE_SUSPENDED. The T1055.012 detection encodes exactly that heuristic:
let HollowingTargets = dynamic(["svchost.exe", "explorer.exe", "rundll32.exe", "notepad.exe", "cmd.exe", "dllhost.exe", "werfault.exe", "MSBuild.exe", "RegAsm.exe", "InstallUtil.exe", "vbc.exe"]);
let LegitParents = dynamic(["services.exe", "svchost.exe", "explorer.exe", "winlogon.exe", "System", "smss.exe", "csrss.exe", "wininit.exe"]);
DeviceProcessEvents
| where Timestamp > ago(24h)
| where FileName in~ (HollowingTargets)
| where InitiatingProcessFileName !in~ (LegitParents)
| where ProcessCommandLine == "" or ProcessCommandLine == FileName or strlen(ProcessCommandLine) < 5
| extend HollowingIndicator = case(
FileName =~ "svchost.exe" and InitiatingProcessFileName !=~ "services.exe", "Critical - svchost not from services.exe",
FileName in~ ("MSBuild.exe", "RegAsm.exe", "InstallUtil.exe") and strlen(ProcessCommandLine) < 5, "Critical - .NET LOLBin, empty cmdline",
strlen(ProcessCommandLine) < 5, "High - Empty command line",
true, "Medium - Unusual parent-child")
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName, FileName, ProcessCommandLine, HollowingIndicator
| sort by Timestamp desc
svchost.exe not spawned by services.exe is one of the highest-fidelity host signals in Windows. Treat the “Critical” tier as page-the-analyst.
4. Thread Execution Hijacking — T1055.003
Instead of creating a thread, the attacker suspends an existing thread, points its instruction pointer at injected code with SetThreadContext, and resumes it. In Splunk, Sysmon’s ProcessAccess (EventCode 10) gives you the cross-process handle with the access mask. This SPL from T1055.003 filters to the masks used by injection tooling:
index=wineventlog sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=10
| where GrantedAccess IN ("0x1FFFFF", "0x001F0FFF", "0x1F3FFF", "0x1F1FFF", "0x143A", "0x1478")
| rename SourceImage as Hijacker, TargetImage as Victim
| search NOT Hijacker IN ("*\\MsMpEng.exe", "*\\csrss.exe", "*\\services.exe", "*\\WerFault.exe", "*\\taskmgr.exe", "*\\WmiPrvSE.exe")
| eval ThreadHijackSuspect=if(match(GrantedAccess, "(0x1FFFFF|0x001F0FFF|0x1F3FFF)"), "High - ALL_ACCESS handle", "Medium - Suspicious access mask")
| table _time, host, User, Hijacker, Victim, GrantedAccess, ThreadHijackSuspect
| sort - _time
The GrantedAccess allowlist is the entire game here — PROCESS_ALL_ACCESS (0x1FFFFF) to a process you didn’t spawn is rarely benign.
5. PE Injection — T1055.002 — and Early Bird APC — T1055.004
PE injection writes a full executable into the target and runs it without loading a DLL. The clever inversion in the T1055.002 rule: alert on CreateRemoteThread where the target process does not subsequently load any new module — direct code execution with no DLL artifact.
The Early Bird APC pattern queues an APC onto a suspended process’s main thread so the payload runs before EDR userland hooks initialize. Detect it as a suspicious parent (powershell.exe, wscript.exe, mshta.exe) spawning a hollow-able binary with an empty command line, then correlating a remote-thread call against it. Both rules, plus the parent T1055 overview and remaining sub-techniques, are in the library with full Sentinel, Splunk, Elastic, Chronicle, and CrowdStrike variants.
6. Tuning: Surviving Contact With Production
These rules are deliberately broad so they catch technique variants, which means baselining is mandatory before you alert:
- Profile your injectors. Security agents, telemetry tools, and some installers legitimately call
CreateRemoteThread. Build a per-environment allowlist of source images rather than trusting a generic one. - Anchor on the target, not just the source. A weird source into
notepad.exeis noise; the same source intosvchost.exeorlsass.exeis an incident — and the latter overlaps your credential dumping detections. - Stack-rank, don’t fire-on-first. Run the broad query as a scheduled hunt for two weeks, count outcomes per source/target pair, then promote only the rare, high-severity tiers to real-time alerts.
- Chain with parent context. Injection from a process whose own parent is PowerShell or Office is a complete kill-chain narrative, not an isolated event.
Process injection will never be a single-rule detection. But with the source/target correlation model above and the production queries from the df00tech detection library, a SOC can move it from “EDR says trust me” to a tier you actually own and can defend in an audit.