Remote Thread Injection
Remote thread injection leaves one process's code running inside another, on a thread the first one created, under the target's name, path, parent and signature. A process runs only its own program and the DLLs it loaded, mapped from files, so a thread that starts in memory no file backs is out of place. It usually means code was put there to borrow a trusted name, its account and its network access. Debuggers, security tools and just-in-time compilers leave some of the same traces. Check where each thread starts first, then the region that holds the start.
What It Is
Before, every thread in the process starts in its own program or in a DLL it loaded, and each of those is mapped from a file on disk. After, the process keeps its name, path, parent and signature, but it holds one more thread, created by another process, that starts in private memory no file backs.
That region is the one thing that changed. It is executable, and it often holds a whole program image, which starts with MZ (4D 5A). Where a DLL was forced in through the Windows loader, the module list also shows a DLL from a folder the program never uses.
What Changes Inside the Process
The process is shown before and after, and the pointer marks what changed.
Each thread is drawn where its start address lands, in a module mapped from a file or in private memory that no file backs.
Before
A clean Windows 11 install
Loaded Modules
C:\TID 2460Start AddressWindows\ System32\ spoolsv.exe spoolsv.exe+0x1240 C:\TID 2464Start AddressWindows\ System32\ ntdll.dll ntdll.dll+0x9A20
Private Memory
- No thread starts here.
After
ws-042,
Loaded Modules
C:\TID 3276Start AddressWindows\ System32\ spoolsv.exe spoolsv.exe+0x4B10 C:\TID 3304Start AddressWindows\ System32\ ntdll.dll ntdll.dll+0x2A3B0
Private Memory
None (no file)TID 9012Start Address0x21C4F3A1A40File at the StartStarts in memory no file backs: a spooler thread starts in spoolsv.exe or a DLL it loaded. Remote Thread Injection
What It Leaves Behind
Inside the target there is a thread that starts in executable memory no file backs, outside every loaded module, and that memory often holds code or a PE header. Sysmon event 8 records such a thread with an empty StartModule, and Sysmon event 10 can record the open that came first. Afterwards the genuine process often does things its job never needs, such as reaching the internet.
Why It Matters
Injected code runs under a genuine process's name, path, parent and signature, so a listing never shows it. Its traces are a thread one process started in another, or executable memory that no file backs.
Which Evidence Can See It
Each tile says whether that kind of evidence can see it, and what it shows.
Memory
Sees it
A memory image holds the thread that starts in private memory no file backs, and the executable region it starts in, while the process runs.
EDR Telemetry
Sees it
Defender XDR records CreateRemoteThreadApiCall in DeviceEvents, with the process that started the thread and the one it runs in.
Disk
Cannot see
The target's file stays genuine, so a hash or signature check passes it. Only a dropped program that started the thread is on disk to find.
Logs
Partly
Sysmon event 8 in Microsoft-Windows-Sysmon/Operational records the thread and where it starts, but only on machines running Sysmon.
How to Confirm It
Work through these in order. Each step names the page that shows how.
- Read where each thread starts, live with Windows.System.Threads or in a memory image with windows.threads' Win32StartAddress. Where Sysmon runs, event 8 with an empty StartModule records the same start.Threads
- Find the region that holds the start with windows.vadinfo, which gives its type, the protection it was created with and its file. Then windows.malware.malfind flags private memory created executable and writable, or executable with a written page. A hit is a lead to dump, and a clean result does not clear it.Memory Regions
- Find who holds the process open with the rights to write its memory and start threads. In memory, windows.handles lists that Process handle, and live, Handle lists it with every handle type and the granted access shown. Where Sysmon runs, event 10 records the open.Handles, Pipes and Mutexes
- Tie its connections to it, live with Get-NetTCPConnection and in memory with windows.netscan. A connection its job never needs, such as the print spooler reaching the internet, points to code that is not its own.Network Connections
- Sweep the whole process. Live, Windows.System.VAD lists executable memory no file backs. In a memory image, windows.malware.ldrmodules shows a private region that starts with an MZ header in no loader list. A hit is a lead to dump, once whoever runs the response agrees.Checking for Injected Code
Investigation Steps With Named Tools
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.
Which thread starts in memory no file backs, and which process started it?
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. Lists each thread with its creation time, its start address and the file mapped there. A start with no file is the thread to follow.System. Threads 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. A DeviceEvents row naming the process that started the thread, InitiatingProcessFileName, and the one it runs in, FileName.Remote Thread Api Call - 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.
processesIts processes command finds the process by PID or name. No built-in command lists threads, so reach them through a read-only library script started with run.
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 live threads that start in memory no file backs, and names the region each one starts in.malware. suspicious_ threads
What does the memory at the start hold?
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. An exchange artifact, imported first, that reports threads whose start looks injected. It runs PowerShell, which some EDRs block, so begin with its Brief scan.Memory. Injected Thread Ex
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 created executable and writable, with their first bytes, so a program image shows as MZ (4D 5A).malware. malfind
What Defenders See
Each record names its log and what must be switched on to get it.
| Record and Where It Lands | What to Read | How to Turn It On |
|---|---|---|
8 CreateMicrosoft-Windows-Sysmon/OperationalIt is written on any Windows computer running Sysmon. | SourceImage started a thread in TargetImage. An empty StartModule means the thread starts outside every loaded module, usually in memory no file backs.Checking for Injected Code shows how to read it. | Run Sysmon with Create |
10 ProcessAccess (opens in a new tab)Microsoft-Windows-Sysmon/OperationalIt is written on any Windows computer running Sysmon. | SourceImage opened TargetImage. GrantedAccess with the rights to write memory and create threads, from a source with no reason to, often comes first.Handles, Pipes and Mutexes shows how to read it. | Run Sysmon with ProcessAccess events in its configuration, filtered to the accesses that matter. |
CreateDeviceEventsDefender XDR, advanced hunting | Initiating | The Defender for Endpoint sensor on the device |
AsrDeviceEventsDefender XDR, advanced hunting | An Office app was blocked, or audited, as it tried to inject code into another process. | The Defender for Endpoint sensor on the device |
What It Looks Like
This record is decoded field by field, with what each value means.
| Field, as the Record Writes It | What It Tells You |
|---|---|
UtcTime2026-10-12 10:07:30 | When the thread was created, one second before the spooler's first connection to 198 |
SourceImageC:\ | The process that created the thread, the new service binary in Temp, running as SYSTEM. It has no reason to touch the spooler. |
TargetImageC:\ | The process the thread runs in, the genuine print spooler. Code there borrows its name, its SYSTEM account and its network access. |
StartAddress0x0000021C4F3A1A40 | Where the new thread begins, in no module, inside the private region at 0x21C4F3A0000. Find that region with windows |
StartModule- | Empty, so the start lies outside every loaded module. It is a lead, not proof, so read the region in memory and judge the source process. |
StartFunction- | Empty too, so the start is at no known exported function either. Together the two rule out a normal library entry point. |
- False Positives
Debugging, diagnostic, management and security tools start threads in other processes for good reasons, Microsoft notes, and SigmaHQ's rule leaves out a print vendor service and VMware Tools for spoolsv.exe. Judge the source, since a known, signed tool in its own folder is usual and a binary in Temp is not.
.NET and other just-in-time compilers make executable private memory in their own processes, which memory scans report.
- Blind Spots
A remote thread record covers one way in. Memory checks look for the code whichever way it arrived, so pair the two.
A clean windows.malware.malfind result never clears a process, because it reads the protection each region was created with, not the one it has now. Pair it with windows.malware.suspicious_threads and thread records.
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 documents this action type only in the portal's schema reference, so confirm it with DeviceEvents | distinct ActionType. Security tools and debuggers start remote threads too, so a hit is a lead to check, not proof of injection.
// Threads one process started in another (CreateRemoteThreadApiCall), initiators outside the system folders firstDeviceEvents| where Timestamp > ago( 30d)| where ActionType == "CreateRemoteThreadApiCall" and ProcessId != InitiatingProcessId| extend FromSystemFolder = InitiatingProcessFolderPath startswith @"C:\ Windows\ System32\ " or InitiatingProcessFolderPath startswith @"C:\ Windows\ SysWOW64\ " or InitiatingProcessFolderPath startswith @"C:\ Program Files"| summarize Threads = count( ), Devices = dcount( DeviceId), FirstSeen = min( Timestamp), LastSeen = max( Timestamp) by InitiatingProcessFileName, InitiatingProcessFolderPath, FromSystemFolder, Target = FileName| order by FromSystemFolder asc, Devices asc, Threads ascIn a Memory Image
Volatility 3 reads a memory image, and each of its plugins below shows part of this sign.
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. It lists private regions, and mapped ones not created as Execute, write copy, that were created executable and writable or executable with a written page, with their first bytes.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. It gives the flagged region's protection and its file, and injected code sits in a region that names no file.windows.dlllist(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. dlllist means DLL list. It lists modules loaded the normal way, with their paths, so a DLL forced in through the loader shows here, often from an odd folder.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. It sets every region that starts with an MZ header, private memory included, against the loader's three lists. A private region in no list and with no file is what ws-042's spooler shows, while the executable and resource files have ordinary reasons to miss a list.malware. ldrmodules windows.(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. It runs YARA rules over every region, to name what the injected code is.vadyarascan
Rules to Compare With
Security vendors publish detection rules anyone can read. These look for the same thing, so compare your own with them.
Show the 5 Public Rules
- Remote Thread Creation In Uncommon Target Image (opens in a new tab) SigmaHQ: It lists spoolsv.exe as a target, so where Sysmon runs it would flag the thread on ws-042.
- Remote Thread Creation By Uncommon Source Image (opens in a new tab) SigmaHQ: Flags Sysmon event 8, a remote thread, created by processes that rarely create one.
- Windows Process Injection Remote Thread (opens in a new tab) Splunk: Its fixed list of targets leaves out spoolsv.exe, so add the processes you care about.
- Windows Process Injection into Commonly Abused Processes (opens in a new tab) Splunk: Flags Sysmon event 10 where a process asks for broad access to processes such as notepad.exe, wordpad.exe and calc.exe.
- T1055 - Process Injection with CreateRemoteThread (opens in a new tab) FalconForce: Flags CreateRemoteThreadApiCall rows, where one process starts a thread in another, narrowed to source binaries that are rare worldwide.
How to Prevent It
Each control helps prevent it, and its line says what it does here.
- ASR: Block Office applications from injecting code into other processes (opens in a new tab) Block Office apps from injecting code into other processes, which Microsoft says has no known legitimate business purpose.
- Added LSA protection (opens in a new tab) Run lsass.exe as a protected process, so code that is not protected can neither inject into it nor read its memory. Check it in the Registry: LSA Protection (RunAsPPL)
- App Control for Business (opens in a new tab) Let only the code a policy trusts run, so an injector dropped in Temp never starts.
- Secure Boot, which turns AppInit_DLLs off (opens in a new tab) Keep Secure Boot on, so AppInit_DLLs stays off and a DLL listed there loads into nothing. Check it in the Registry: AppInit_DLLs
- No antivirus exclusions for temporary folders (opens in a new tab) Keep C:\Windows\Temp scanned. On ws-042 an exclusion for it came two minutes before updsvc64.exe landed there. Check it in the Registry: Microsoft Defender Exclusions
Sources
- Microsoft Learn: Sysmon, event IDs 8 and 10 (opens in a new tab)
- Microsoft Learn: Process Security and Access Rights (opens in a new tab)
- Microsoft Security Blog: Uncovering cross-process injection with Windows Defender ATP (opens in a new tab)
- Microsoft Defender XDR: DeviceEvents table (opens in a new tab)
- Microsoft Learn: Live response in Defender for Endpoint (opens in a new tab)
- Microsoft Learn: ASR rule, block Office applications from injecting code (opens in a new tab)
- Elastic Security Labs: Hunting In Memory (opens in a new tab)
- SigmaHQ: Remote Thread Creation In Uncommon Target Image (opens in a new tab)
- Velociraptor: Windows.System.VAD (opens in a new tab)
MITRE ATT&CK is a free public catalogue of the techniques attackers use. Each technique has an ID, such as T1055 for Process Injection, so defenders can name the same behaviour the same way. This page covers these techniques.
- T1055 (opens in a new tab) Process Injection
- T1055.001 (opens in a new tab) Process Injection: Dynamic-link Library Injection
- T1055.002 (opens in a new tab) Process Injection: Portable Executable Injection