Checking for Injected Code
Injected code runs inside a genuine process, so its name, path and signature all look right. What gives it away is memory, whether executable memory that no file on disk backs, a thread that starts there, or an image in memory that differs from its file. On a clean machine code runs from images mapped from files, though .NET and other just-in-time compilers and security products make executable memory of their own. So a hit is a lead to dump and examine, never a verdict on its own. Start with the threads, where each one starts, and which file is mapped there.
What It Shows
Code normally runs from images mapped from files, so executable memory that no file backs, a thread that starts in it, or an image in memory that differs from its file all point to code that did not arrive the normal way. These checks read memory, live or in an image, and the thread records Sysmon and Defender keep. They also flag .NET and other just-in-time code and security products, so dump what they find and check it before calling it injection.
Inside Parts It Reads
This check reads these parts, and each page shows what normal looks like.
Records of One Process Reaching Into Another
When one process reaches into another, these records catch it, each from a different sensor. Sysmon writes events 8 and 25 to Microsoft-Windows-Sysmon/Operational on machines running Sysmon, and Defender writes the DeviceEvents rows on devices onboarded to Defender for Endpoint.
| Record | Fields to Read | What It Tells You |
|---|---|---|
| Sysmon event 8 | Source | A thread started in another process. Sysmon infers StartModule and StartFunction and can leave them empty when the start lies outside every loaded module, a lead to examine in memory |
| Sysmon event 25 | Image, Type | Sysmon saw a process image change in memory, the trace hollowing and similar tampering leave |
| Create | Initiating | Defender's records of one process starting a thread in, queuing work to, or allocating memory in another. Microsoft names these action types in the portal's schema reference and in the data catalogue it published with its NL2KQL research, so confirm the one you need on your own tenant |
Live and in Memory
Each question has the tools that answer it on a running machine, and in a memory image, a copy of its memory saved to a file.
Is there executable memory that no file backs?
On a Running Machine
Windows.(documentation, opens in a new tab)Live Machine A Velociraptor artifact. It is a ready-made question Velociraptor asks of running machines, named from general to specific. VAD means virtual address descriptors, the records of memory regions. The memory regions of matching processes, filtered to executable ones with no mapped file, and it can upload them.System.VAD - PE-sieve (documentation, opens in a new tab)Live Machine A free scanner that compares a process's code in memory with its file on disk. It is known to raise false alarms. Scans one process for injected or replaced code, hooks and changes made in memory, and dumps what it finds.
- HollowsHunter (documentation, opens in a new tab)Live Machine A free scanner that compares a process's code in memory with its file on disk. It is known to raise false alarms. Runs the same scan over every running process.
- Defender for Endpoint live response (documentation, opens in a new tab)Live Machine Part of Defender for Endpoint. It opens a command console on one machine from the Defender portal, so you can list its processes from your desk.
processesLists the process. No built-in command reads its memory regions or threads, so live it reaches them only through a read-only library script you write and start with the run command.
In a Memory Image
windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin from its malware group. It checks a memory image for code that hides, or code put into a process. malfind means find malware. Private regions, and mapped ones not created as Execute, write copy, that were created executable and writable, or executable with a written page. It shows the first 64 bytes of each.malware. malfind windows.vadinfo(documentation, opens in a new tab)Memory Image A Volatility 3 plugin. It is one check run on a saved copy of a Windows machine's memory. vadinfo means virtual address descriptor information. Every region with its protection and mapped file, to read a flagged region in its process's context.windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin from its malware group. It checks a memory image for code that hides, or code put into a process. ldrmodules means loader modules. Every region that starts with an MZ header, private memory included, with whether each loader list holds it and the file behind it. A region in no list and with no file is a lead, while the process's own executable and resource files have ordinary gaps.malware. ldrmodules
Does a thread start in memory no file backs?
On a Running Machine
- Get-InjectedThreadEx (documentation, opens in a new tab)Live Machine A free PowerShell script that looks for threads starting in memory no file backs. It is known to raise false alarms. Scans every running thread on one machine for a suspicious start address, such as one in memory no file backs.
Create(documentation, opens in a new tab)Logs and Records A Defender XDR action type, which is one kind of event Defender for Endpoint records in its DeviceEvents table. Defender's record of one process starting a thread in another, with both processes named.Remote Thread Api Call
In a Memory Image
windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin from its malware group. It checks a memory image for code that hides, or code put into a process. Flags threads that start in memory no file backs, in a region not created as Execute, write copy as mapped images are, or in a second executable. It checks both start addresses and sees only threads still running.malware. suspicious_ threads windows.threads(documentation, opens in a new tab)Memory Image A Volatility 3 plugin. It is one check run on a saved copy of a Windows machine's memory. Each thread's two start addresses, the kernel's StartAddress and the Win32 start address, with the file mapped at each, so compare the Win32 one, which Sysmon event 8, Process Explorer and Windows.System.Threads show.
Is an image in memory different from its file on disk, or have the Windows functions it calls been changed in memory?
On a Running Machine
Windows.(documentation, opens in a new tab)Live Machine A Velociraptor artifact. It is a ready-made question Velociraptor asks of running machines, named from general to specific. Compares executables in memory with their files on disk, live and without dumping.Memory. Mem2 Disk - PE-sieve (documentation, opens in a new tab)Live Machine A free scanner that compares a process's code in memory with its file on disk. It is known to raise false alarms. Reports the changes made in memory to the modules of the process it scans.
In a Memory Image
windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin from its malware group. It checks a memory image for code that hides, or code put into a process. Checks each process's executable and DLLs for a replaced image. It skips processes with fewer than three DLLs or five memory regions and does not name the technique.malware. hollowprocesses windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin from its malware group. It checks a memory image for code that hides, or code put into a process. Processes whose image file is marked for deletion or has no file object, or that map an executable or DLL set to be deleted on close.malware. processghosting windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin from its malware group. It checks a memory image for code that hides, or code put into a process. Sets the kernel's image name and audit path beside the PEB's, so a PEB that names another file stands out, and you read its two flags together.malware. pebmasquerade windows.(documentation, opens in a new tab)Memory Image A Volatility 3 plugin from its malware group. It checks a memory image for code that hides, or code put into a process. Compares the ntdll.dll system call stubs across processes to find one that has been changed in memory.malware. unhooked_ system_ calls windows.etwpatch(documentation, opens in a new tab)Memory Image A Volatility 3 plugin. It is one check run on a saved copy of a Windows machine's memory. etwpatch means Event Tracing for Windows patch. Looks for the event tracing functions in ntdll.dll or advapi32.dll changed in memory inside a process.
Who holds the process open with the rights to write its memory, and does it connect anywhere its job never needs?
On a Running Machine
- Handle (documentation, opens in a new tab)Live Machine A free Microsoft tool that lists the handles each process holds open. Its granted-access view shows who holds this process open with the rights to write its memory or start a thread in it.
- Get-NetTCPConnection (documentation, opens in a new tab)Live Machine A PowerShell command built into Windows that lists TCP connections and the process that owns each.
Get-NetTCPConnectionLists the connections by owning process, so a connection the process never normally makes stands out.
In a Memory Image
windows.handles(documentation, opens in a new tab)Memory Image A Volatility 3 plugin. It is one check run on a saved copy of a Windows machine's memory. The handles other processes hold on this one, with the granted access, so a handle that carries write or thread rights is a lead.windows.netscan(documentation, opens in a new tab)Memory Image A Volatility 3 plugin. It is one check run on a saved copy of a Windows machine's memory. netscan means network scan. The connections and listeners tied to the process in the image, to match what a live tool shows.
What It Misses
Every way of looking has gaps, so cover these from another source.
- windows.malware.malfind judges a region by the protection it was created with, so it can miss a region whose protection changed later, and it never judges an image region created as Execute, write copy. Pair it with windows.malware.suspicious_threads and Windows.Memory.Mem2Disk, because a clean result never clears a process.
- A start address records where a thread began, not what it runs now. A thread whose context was set from another process, or that was handed queued work, keeps a normal start, and Defender's SetThreadContextRemoteApiCall and QueueUserApcRemoteApiCall record those. A thread that ended is gone from memory, kept only by Sysmon event 8 or a Defender record.
- Pages saved to the page file are missing from a raw image unless the page file is added, and pages Windows compresses sit in the Memory Compression process, not the one they belong to. A region you cannot read is not proof it was empty.
False Positives
These look odd but are normal, so rule them out before you act.
- .NET, Java and other just-in-time compilers write code into memory no file backs, and security products inject into processes to watch them, so compare with a clean machine of the same build before calling it injection.
- Packed and copy-protected programs unpack themselves in memory and look injected.
- PE-sieve, HollowsHunter and Get-InjectedThreadEx flag legitimate hooks and generated code too, so treat a hit as a region to dump and examine, not a verdict.
Proves These Red Flags
This check is a step in confirming each sign below.
- How Malware Hides in a Process
- Remote Thread InjectionProcess Hollowing
Hunt It
The same hunt is written for four tools, so pick the one you use. Defender XDR and Sentinel queries are in KQL, which you paste into Advanced hunting in the Defender portal or into Sentinel's Logs page. Splunk queries are in SPL, for its Search page, and Velociraptor queries are in VQL, which you run as a hunt.
Needs Defender for Endpoint. Microsoft lists DeviceEvents action types in the portal's schema reference and in the data catalogue it published with its NL2KQL research, so confirm this one with DeviceEvents | distinct ActionType before you rely on it. Security products and other legitimate programs start remote threads too, so rare pairs come first, and a hit is a lead to examine in memory.
// One process starting a thread in another (CreateRemoteThreadApiCall), by source and target, rarest pairs firstDeviceEvents| where Timestamp > ago( 30d)| where ActionType == "CreateRemoteThreadApiCall" and ProcessId != InitiatingProcessId| summarize Devices = dcount( DeviceId), Threads = count( ), FirstSeen = min( Timestamp), LastSeen = max( Timestamp) by InitiatingProcessFileName, InitiatingProcessFolderPath, FileName| order by Devices asc, Threads ascSources
- Microsoft Sysinternals: Sysmon (opens in a new tab)
- Microsoft Learn: DeviceEvents table (opens in a new tab)
- Microsoft NL2KQL research data: the Defender data catalogue (Defender_DataCatalog.yml) naming the DeviceEvents action types (opens in a new tab)
- Microsoft Learn: investigate devices with live response (opens in a new tab)
- FalconForce FalconFriday: process injection with CreateRemoteThread (opens in a new tab)
- FalconForce FalconFriday: work queued to another process by rare or unsigned programs (opens in a new tab)
- FalconForce FalconFriday: memory allocated in another process by rare or unsigned programs (opens in a new tab)
- SigmaHQ: Potential Process Hollowing Activity (opens in a new tab)
- Elastic Security Labs: Hunting In Memory (opens in a new tab)
- Elastic Security Labs: Get-InjectedThreadEx, detecting thread creation trampolines (opens in a new tab)
- Velociraptor: Windows.System.VAD (opens in a new tab)
- Velociraptor exchange: Windows.Memory.Mem2Disk (opens in a new tab)
- hasherezade: PE-sieve (opens in a new tab)
- hasherezade: PE-sieve FAQ (opens in a new tab)
- hasherezade: HollowsHunter (opens in a new tab)
- John Uhlmann: Get-InjectedThreadEx (opens in a new tab)
- Volatility 3: windows.malware.malfind (opens in a new tab)
- Volatility 3 source: malfind.py (opens in a new tab)
- Volatility 3: windows.malware.suspicious_threads (opens in a new tab)
- Volatility 3 source: suspicious_threads.py (opens in a new tab)
- Volatility 3: windows.malware.hollowprocesses (opens in a new tab)
- Volatility 3 source: hollowprocesses.py (opens in a new tab)
- Volatility 3: windows.malware.ldrmodules (opens in a new tab)
- Volatility 3: windows.malware.processghosting (opens in a new tab)
- Volatility 3: windows.malware.pebmasquerade (opens in a new tab)
- Volatility 3 source: pebmasquerade.py (opens in a new tab)
- Volatility 3: windows.vadinfo (opens in a new tab)
- Volatility 3: windows.threads (opens in a new tab)
- Volatility 3: windows.malware.unhooked_system_calls (opens in a new tab)
- Volatility 3: windows.etwpatch (opens in a new tab)
- Volatility 3: memory layers and the page file (opens in a new tab)
- Windows Insider blog: build 17063, the Registry and memory compression processes (opens in a new tab)
- MITRE ATT&CK T1055: Process Injection (opens in a new tab)
- MITRE ATT&CK T1620: Reflective Code Loading (opens in a new tab)