Process Injection: A Beginner's Guide to the Techniques and the Basics Behind Them
A step-by-step guide that starts from how Windows runs processes and ends with the main process injection techniques, how they work, and how defenders detect them. No prior knowledge of Windows internals is needed.
Level: Beginner to Intermediate. Focus: Windows.
This guide is for authorized red team work, malware analysis, and defensive research. Only run these techniques against systems you own or have written permission to test. Injecting code into other people's processes without permission is illegal in most places.
Process injection is a family of techniques for running your code inside another running program. Attackers use it to hide, to steal data, and to get past security tools. Defenders need to understand it to detect it. Red teamers use it during authorized engagements. This guide explains the building blocks first, then walks through each technique.
Part 1: Setting the Scene
What Is Process Injection?
Process injection means placing code into the memory of another process and getting that process to run it. The code then runs with the identity and permissions of the target process, not your own.
Attackers do this for a few reasons:
- Hiding. Malicious activity looks like it comes from a trusted program such as
explorer.exeorsvchost.exe. - Bypassing controls. Some security tools trust certain processes, so code running inside them draws less attention.
- Access. Running inside a target process gives access to that process's memory, handles, and network connections.
- Persistence and privilege. Injecting into a higher-privilege process can raise the attacker's access level.
Why Learn It
If you defend systems, you cannot detect what you do not understand. Most modern malware and most red team tools use some form of injection. If you do authorized offensive testing, injection is a core skill for staying quiet and reaching your goal.
Where It Sits in an Attack
Injection is usually a middle step, not the first one. The attacker already has code running (for example from a phishing document or a dropped file). Injection is how they move that code into a better host process to run more quietly or with more access.
Part 2: The Windows Basics You Need First
Injection makes sense only once you know a few core ideas. Take these one at a time.
Processes
A process is a running program. When you launch notepad.exe, Windows creates a process for it. Each process gets:
- Its own private virtual memory space.
- One or more threads that actually run code.
- A set of handles to resources such as files and other objects.
- A security token that says who the process runs as and what it can do.
One key point: a process is mostly a container. It holds memory and resources. It does not run code by itself. Threads do that.
Threads
A thread is the unit that actually runs instructions. A process has at least one thread, and can have many. Each thread has:
- Its own stack.
- Its own set of CPU register values, saved when the thread is not running. This saved set is called the thread context.
- A current instruction pointer, which is the address of the next instruction it will run.
Injection almost always comes down to one goal: get a thread in the target process to run your code. You can do this by creating a new thread, hijacking an existing one, or queueing work onto one.
Virtual Memory
Each process has its own private virtual address space. Addresses in one process do not point to the same data in another process. This is why you cannot just write to another process's memory directly. You have to ask the operating system to do it for you, through special functions.
Memory in a process has protection flags that say what you can do with each region:
- Read
- Write
- Execute
For example, PAGE_EXECUTE_READWRITE means a region can be read, written, and run as code. Code that can be both written and executed is a classic sign of injection, because normal program code is usually not writable.
Handles
A handle is a reference to a system object, such as a process, thread, or file. To act on another process you first open a handle to it. The handle you get is limited by access rights. For example, to write to another process's memory you need a handle opened with rights that allow it.
Access Tokens and Privileges
A token describes the security identity of a process: the user, the groups, and the privileges. Some injection techniques need a special privilege called SeDebugPrivilege, which lets a process open almost any other process. Administrators can enable it. This is why running as admin makes many injection techniques easier.
DLLs
A DLL (Dynamic Link Library) is a file of code that a process can load and use at runtime. Windows itself is built from DLLs such as kernel32.dll and ntdll.dll. A common injection goal is to force a target process to load a DLL you control. Once loaded, the DLL's startup code runs inside the target.
The Windows API Layers
When code calls a Windows function, the call usually passes through layers:
Your program
|
kernel32.dll / kernelbase.dll (documented Win32 API, for example VirtualAllocEx)
|
ntdll.dll (native API, for example NtAllocateVirtualMemory)
|
system call into the kernel
Most injection uses the documented Win32 functions in kernel32.dll. More advanced or stealthy tools call the lower ntdll.dll functions directly to skip security hooks placed on the higher layer. Knowing these layers exist helps you understand both attacks and detection.
Part 3: The Core Building Blocks
Almost every classic injection technique is built from the same small set of API calls. Learn these five and you will recognize most techniques.
| Step | Win32 function | What it does |
|---|---|---|
| 1. Open the target | OpenProcess | Get a handle to the target process with the rights you need |
| 2. Allocate memory | VirtualAllocEx | Reserve a region of memory inside the target |
| 3. Write your data | WriteProcessMemory | Copy your code or DLL path into that region |
| 4. Set permissions | VirtualProtectEx | Mark the region executable if needed |
| 5. Run it | CreateRemoteThread | Start a new thread in the target that runs your code |
The lower-level ntdll.dll equivalents are NtOpenProcess, NtAllocateVirtualMemory, NtWriteVirtualMemory, NtProtectVirtualMemory, and NtCreateThreadEx. Same idea, one layer down.
The differences between injection techniques mostly come down to two questions:
- How do you get your code into the target's memory.
- How do you get a thread to run it.
Everything else is a variation on those two.
Part 4: The Types of Process Injection
Here is the landscape. Each type is explained in more detail below the table.
| Technique | How code gets in | How it runs | Notes |
|---|---|---|---|
| Classic DLL injection | Write a DLL path, call LoadLibrary | New remote thread | Simple, well known, easy to detect |
| Reflective DLL injection | Write the whole DLL, load it manually | New remote thread | No DLL on disk, no LoadLibrary |
| PE injection | Write a full executable image | New remote thread | Self-contained, no file on disk |
| Process hollowing | Start a process suspended, replace its image | Resume the main thread | Malware looks like a trusted program |
| Thread execution hijacking | Write code, point an existing thread at it | Redirect a suspended thread | No new thread created |
| APC injection | Write code, queue it to a thread | Runs when the thread goes alertable | Includes the Early Bird variant |
| SetWindowsHookEx | Load a DLL via a message hook | Runs on the hooked event | Needs a message-driven target |
| AppInit_DLLs | Registry value lists a DLL | Loaded by every GUI process | Legacy, mostly blocked now |
| Atom bombing | Store code in the global atom table | APC runs it | Older, evasion focused |
| Process doppelganging | Load an image from a transacted file | Runs the crafted section | Abuses NTFS transactions |
| Process ghosting / herpaderping | Alter or delete the file before it runs | Runs the crafted section | Confuses tools that read the file later |
| Module stomping | Overwrite a legit loaded DLL in memory | Runs from a trusted module | Hides code inside a real module |
Classic DLL Injection
This is the simplest and most common technique. The idea is to make the target process load a DLL you control.
Steps:
- Open a handle to the target with
OpenProcess. - Allocate memory in the target with
VirtualAllocEx. - Write the full path to your DLL into that memory with
WriteProcessMemory. - Create a remote thread with
CreateRemoteThread, and set its start function toLoadLibraryA, with the argument being the address of the DLL path you wrote.
Because LoadLibraryA lives at the same address in every process (kernel32 is loaded at the same base in a given boot session), you can pass its address directly. When the remote thread runs LoadLibrary("C:\\path\\evil.dll"), the target loads your DLL and runs its startup code.
// Illustrative outline, not a complete program.
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPid);
// 1. allocate memory in the target for the DLL path
LPVOID remoteBuf = VirtualAllocEx(hProc, NULL, pathLen,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// 2. write the DLL path into the target
WriteProcessMemory(hProc, remoteBuf, "C:\\path\\evil.dll", pathLen, NULL);
// 3. find LoadLibraryA in kernel32 (same address in the target)
LPVOID loadLib = GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
// 4. create a thread in the target that calls LoadLibraryA(remoteBuf)
CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)loadLib,
remoteBuf, 0, NULL);
Strengths: simple and reliable. Weaknesses: the DLL must exist on disk, and the pattern of VirtualAllocEx plus WriteProcessMemory plus CreateRemoteThread pointing at LoadLibrary is one of the most watched patterns in security tools.
Reflective DLL Injection
Classic injection needs the DLL on disk, which leaves a file for defenders to find. Reflective DLL injection avoids this.
Instead of writing a path and calling LoadLibrary, the attacker writes the entire DLL file into the target's memory, then runs a small piece of loader code that does what LoadLibrary would do: map the sections, fix up addresses, resolve imports, and call the entry point. The Windows loader is never asked to load the DLL, so there is no file on disk and no standard load event.
This is stealthier because it skips LoadLibrary and leaves nothing on disk. It is harder to write because the loader logic must be included in the DLL itself.
PE Injection
PE injection is similar in spirit. Instead of a DLL, the attacker copies a full executable image (a PE, Portable Executable) into the target and runs it there. The injected image is written directly into the target's memory and executed with a new thread. Like reflective injection, nothing needs to be on disk.
Process Hollowing (also called RunPE)
Process hollowing makes a malicious program appear to be a trusted one. The steps are:
- Start a legitimate program in a suspended state, for example
CreateProcess("svchost.exe", CREATE_SUSPENDED). The process exists but its main thread has not run yet. - Unmap (hollow out) the original image from the process's memory.
- Write the malicious image into that space.
- Point the process's entry point at the new image.
- Resume the main thread. The process now runs the malicious code, but its name, path, and command line still say it is the trusted program.
Before: [ svchost process, suspended, original image loaded ]
Step: hollow out the original image
After: [ svchost process, malicious image loaded, resumed ]
still shows up as svchost.exe in Task Manager
This is popular because the process looks legitimate to a casual look. Detection focuses on the mismatch between the file on disk and the code actually running in memory.
Thread Execution Hijacking
Instead of creating a new thread, this technique takes over one that already exists. Creating a remote thread is a watched event, so reusing an existing thread can be quieter.
Steps:
- Open the target and find a thread in it.
- Suspend that thread with
SuspendThread. - Read its current registers with
GetThreadContext. - Write your code into the target's memory.
- Change the saved instruction pointer to point at your code with
SetThreadContext. - Resume the thread with
ResumeThread. It now runs your code first.
The core idea is that a thread's context includes its instruction pointer. If you change it while the thread is suspended, the thread runs wherever you point it when it resumes.
APC Injection
APC stands for Asynchronous Procedure Call. Windows lets you queue a function to run on a specific thread. The queued function runs the next time that thread enters an alertable state, which happens when it calls certain waiting functions.
Steps:
- Write your code into the target's memory.
- Get a handle to a thread in the target.
- Queue your code onto that thread with
QueueUserAPC. - When the thread becomes alertable, it runs your code.
// Illustrative outline.
LPVOID remoteBuf = VirtualAllocEx(hProc, NULL, codeLen,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProc, remoteBuf, shellcode, codeLen, NULL);
QueueUserAPC((PAPCFUNC)remoteBuf, hThread, 0);
A well-known variant is Early Bird APC injection. The attacker creates a process in a suspended state, queues an APC to its main thread, and then resumes it. The APC runs very early, before many security hooks are in place, which makes it harder to catch.
SetWindowsHookEx
Windows lets a program install a hook that runs code in response to events such as keystrokes or window messages. SetWindowsHookEx can load a DLL into other processes that receive those messages. An attacker installs a hook backed by a malicious DLL, and the DLL gets loaded into target processes when the hooked events occur. This works best against processes that have a message loop, such as GUI applications.
AppInit_DLLs
This is a legacy Windows feature. A registry value named AppInit_DLLs lists DLLs that get loaded into almost every process that uses user32.dll. Setting it to a malicious DLL causes wide loading. It is mostly historical now, because modern Windows disables it when Secure Boot is on, but you may still see it referenced.
Atom Bombing
The global atom table is a shared area where processes can store small pieces of string data. Atom bombing stores attacker code as atom data, then uses an APC to get the target to read that data back and run it. It was notable because it used only legitimate Windows features and avoided the usual suspicious calls. It is older now and well documented for detection.
Process Doppelganging
This technique abuses NTFS transactions, a feature that lets file changes be grouped and rolled back. The attacker creates a file inside a transaction, writes malicious content, creates a memory section from it, and then rolls the transaction back so the file never really lands on disk in its malicious form. The process is created from the crafted section. Security tools that scan the file on disk see nothing wrong, because the malicious version was never committed.
Process Ghosting and Herpaderping
These are related tricks that confuse tools which read a process's backing file after it starts.
- Process ghosting: the file is marked for deletion before the process is created from it, so by the time a tool tries to read it, the file is gone.
- Process herpaderping: the file's contents are changed after the image is mapped but before the process runs, so the file on disk no longer matches the code in memory.
Both aim to break the assumption that the file on disk equals the code that is running.
Module Stomping
Also called module overloading. The attacker gets the target to load a legitimate, signed DLL, then overwrites that DLL's code in memory with malicious code. The malicious code now runs from inside what looks like a trusted, signed module, which helps it blend in.
Part 5: A Simple Mental Model
If the list feels long, group the techniques by the two questions from Part 3.
How does the code get in:
- Point to a DLL on disk and load it (classic DLL injection, SetWindowsHookEx, AppInit_DLLs).
- Write the whole payload into memory (reflective DLL, PE injection, most shellcode methods).
- Replace or overwrite an existing image (process hollowing, module stomping).
- Use a file trick so the on-disk view is wrong (doppelganging, ghosting, herpaderping).
How does it run:
- Create a new thread (classic, reflective, PE injection).
- Reuse an existing thread (thread hijacking, APC, atom bombing).
- Resume a suspended process at the new code (hollowing, Early Bird, doppelganging).
Almost every technique is one choice from each list.
Part 6: How Defenders Detect Injection
Understanding detection makes the techniques clearer, and it is the point of the exercise for defenders.
Common signals:
- API call patterns.
OpenProcessthenVirtualAllocExthenWriteProcessMemorythenCreateRemoteThreadis a classic sequence. Endpoint tools watch for it. - Remote thread creation. A thread created in a process by a different process is unusual and is logged (for example Sysmon event ID 8).
- Memory that is both writable and executable. Regions marked
PAGE_EXECUTE_READWRITE, especially ones not backed by a file on disk, are suspicious. - Image and file mismatch. When the code running in memory does not match the file on disk, that points to hollowing, doppelganging, or herpaderping.
- Unbacked executable memory. Executable memory that has no matching file (private memory running code) is a strong signal, since normal code runs from mapped image files.
- Unusual parent and child relationships. A document reader spawning a suspended system process is a red flag.
- Calls into
ntdllthat skip the usual higher-layer hooks, which can indicate direct syscall use.
Useful tools for study and detection:
- Sysmon, for logging process creation, remote threads, and image loads.
- Process Hacker or System Informer, for viewing memory regions and their protection flags.
- Moneta and pe-sieve, for scanning a process for injected or unbacked code.
- Volatility, for finding injection in a full memory capture.
- A debugger such as WinDbg or x64dbg, for watching the injection happen step by step.
Summary
- Process injection means running your code inside another process, so it runs with that process's identity and access.
- The basics you need are processes (containers of memory and resources), threads (which run code and have a context with an instruction pointer), virtual memory (private per process, with read, write, and execute flags), handles (references to objects), tokens (security identity), and DLLs (loadable code).
- Most classic techniques are built from five calls:
OpenProcess,VirtualAllocEx,WriteProcessMemory,VirtualProtectEx, andCreateRemoteThread, or theirntdllequivalents. - Every technique answers two questions: how the code gets into the target, and how a thread runs it.
- Techniques range from simple classic DLL injection to hollowing, thread hijacking, APC, and file-based tricks such as doppelganging and ghosting.
- Defenders detect injection through API call patterns, remote thread creation, writable and executable memory, and mismatches between the file on disk and the code in memory.
Practical Appendix: Injection Techniques at a Glance
This appendix summarizes each technique in a fixed format. Each entry has the Key APIs, a short Description, and a Detection note. It is split into two collapsible sections: memory-write techniques and image or file techniques.
Note: the expand and collapse blocks below use <details> HTML. They work on GitHub, GitLab, VS Code, and most static site tools. Medium does not support them, so on Medium treat each summary line as a subheading.
Section A: Memory-Write and Thread-Based Techniques
These write code or a DLL reference into a target and then get a thread to run it.
A1. Classic DLL injection
Key APIs
OpenProcess -> VirtualAllocEx -> WriteProcessMemory -> CreateRemoteThread(LoadLibraryA)
Description
Write the path to a DLL into the target, then create a remote thread that calls LoadLibraryA on that path. The target loads and runs your DLL. Simple and reliable, but the DLL must exist on disk.
Detection
The allocate, write, create-remote-thread sequence pointing at LoadLibrary is heavily monitored. A DLL file on disk is also available for scanning.
A2. Reflective DLL injection
Key APIs
VirtualAllocEx -> WriteProcessMemory (whole DLL) -> CreateRemoteThread (custom loader)
Description
Write the entire DLL into the target's memory, then run a small custom loader that maps sections, fixes addresses, resolves imports, and calls the entry point. LoadLibrary is never used and no file is on disk.
Detection
Look for unbacked executable memory (executable private memory with no matching file) and for a remote thread starting inside a private region.
A3. PE injection
Key APIs
VirtualAllocEx -> WriteProcessMemory (full PE image) -> CreateRemoteThread
Description
Copy a full executable image into the target and run it there. Self-contained, needs nothing on disk. Similar to reflective injection but for a full program image rather than a DLL.
Detection
Unbacked executable memory and a remote thread whose start address is in private memory.
A4. Thread execution hijacking
Key APIs
OpenThread -> SuspendThread -> GetThreadContext -> WriteProcessMemory -> SetThreadContext -> ResumeThread
Description
Take over an existing thread instead of creating one. Suspend it, change its saved instruction pointer to point at your code, then resume it. No new thread is created, which can be quieter.
Detection
Watch for GetThreadContext and SetThreadContext against threads in another process, and for a thread's instruction pointer landing in private, executable memory.
A5. APC injection (including Early Bird)
Key APIs
VirtualAllocEx -> WriteProcessMemory -> QueueUserAPC (-> ResumeThread for Early Bird)
Description
Queue your code as an Asynchronous Procedure Call onto a target thread. It runs when the thread becomes alertable. The Early Bird variant queues the APC to a suspended new process and resumes it, so the code runs very early, before many defenses are ready.
Detection
QueueUserAPC targeting a thread in another process, especially right after a process is created in a suspended state.
A6. SetWindowsHookEx
Key APIs
LoadLibrary (local) -> SetWindowsHookEx -> (hook fires in target)
Description
Install a message hook backed by a malicious DLL. Processes that receive the hooked events load the DLL. Works best against GUI processes that have a message loop.
Detection
Unusual global hooks and a DLL being loaded into many processes at once.
A7. Atom bombing
Key APIs
GlobalAddAtom -> NtQueueApcThread -> (target reads atom and runs it)
Description
Store code in the global atom table, then use an APC to make the target read the data back and run it. Notable for using only legitimate features and avoiding the usual write-to-remote-memory calls.
Detection
APC activity combined with atom table use, and code running from unexpected memory. Well documented for modern tools.
Section B: Image Replacement and File-Based Techniques
These replace a process image or use a file trick so the on-disk view does not match the running code.
B1. Process hollowing (RunPE)
Key APIs
CreateProcess(CREATE_SUSPENDED) -> NtUnmapViewOfSection -> VirtualAllocEx -> WriteProcessMemory -> SetThreadContext -> ResumeThread
Description
Start a trusted program suspended, remove its original image, write a malicious image in its place, point the entry to the new code, and resume. The process still shows the trusted name, path, and command line.
Detection
Mismatch between the file on disk and the code in memory, and a process whose main image region does not match its backing file.
B2. Module stomping
Key APIs
(target loads a signed DLL) -> WriteProcessMemory over the DLL's code section
Description
Get a legitimate signed DLL loaded, then overwrite its code in memory with your own. The malicious code runs from inside what looks like a trusted module.
Detection
Compare a loaded module's in-memory code against the file on disk. A signed module whose memory does not match the file is a strong signal.
B3. Process doppelganging
Key APIs
CreateTransaction -> CreateFileTransacted -> WriteFile -> NtCreateSection -> RollbackTransaction -> NtCreateProcessEx
Description
Use an NTFS transaction to write a malicious file, create a memory section from it, then roll the transaction back so the malicious file never commits to disk. Create the process from the crafted section. On-disk scanners see nothing wrong.
Detection
Transacted file activity around process creation, and a process backed by a section with no clean on-disk file.
B4. Process ghosting
Key APIs
CreateFile -> set delete-pending -> NtCreateSection -> close file (deletes it) -> NtCreateProcessEx
Description
Mark the backing file for deletion before creating the process from it. By the time a tool tries to read the file, it is gone, so the running code cannot be checked against a file.
Detection
A process whose backing file is missing or pending deletion at creation time.
B5. Process herpaderping
Key APIs
CreateFile -> write payload -> NtCreateSection -> overwrite the file with benign content -> NtCreateProcessEx
Description
Create the process image from a file, then change the file's contents before the process runs. The file on disk no longer matches the code in memory, which fools tools that scan the file after the fact.
Detection
Compare the running image to the current file contents, and watch for a file being rewritten right after a section is created from it.