Security

Demystifying DLL Injection: Manipulating Process Memory

24 Jan 20247 min read

Whether you're developing game cheats, writing an EDR (Endpoint Detection and Response) solution, or building debugging tools, the ability to execute your own code inside another running process is a fundamental capability. In the Windows ecosystem, the most common technique for this is DLL Injection. It sounds like black magic, but at its core, it is just a sequence of four specific Windows API calls.

The Anatomy of Injection

To inject a DLL, we need to interact with the target process's memory space and force it to execute LoadLibraryA with the path to our custom DLL. Here is the exact pipeline:

  1. OpenProcess: We request a handle to the target process with PROCESS_ALL_ACCESS (or at least memory write and thread creation rights).
  2. VirtualAllocEx: We allocate a block of memory inside the target process. This space will hold the string of our DLL's file path.
  3. WriteProcessMemory: We write the actual string (e.g., "C:\\my_hook.dll") into that newly allocated memory block.
  4. CreateRemoteThread: This is the execution trigger. We spawn a new thread in the target process.

The kernel32.dll Trick

The brilliance of standard DLL injection lies in how we use CreateRemoteThread. This function expects a pointer to a start address. But how do we know the address of LoadLibraryA inside the target process?

Due to how the Windows loader optimizes memory, core system DLLs like kernel32.dll are loaded at the exact same base address across all processes in a given boot session. Therefore, if we find the address of LoadLibraryA in our own injector process, we can safely pass that exact address to CreateRemoteThread for the target process.

// The crux of DLL Injection in C++
LPVOID pAllocatedMem = VirtualAllocEx(hTargetProcess, NULL, dllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
WriteProcessMemory(hTargetProcess, pAllocatedMem, dllPath, dllPathLen, NULL);

// Get local address of LoadLibraryA, which matches the remote address
LPVOID pLoadLibrary = (LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");

// Trigger execution
HANDLE hThread = CreateRemoteThread(hTargetProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pLoadLibrary, pAllocatedMem, 0, NULL);

Modern Mitigation

While this technique is academically beautiful, using CreateRemoteThread today is a surefire way to get flagged by any modern Anti-Virus or EDR. Security solutions aggressively hook this API. Consequently, modern system programmers and malware authors pivot to stealthier methods like APC Injection (QueueUserAPC), Thread Hijacking (SetThreadContext), or manual mapping (writing the PE file into memory without using Windows loaders at all).

Instead of a Conclusion

Understanding DLL injection teaches you exactly how the Windows kernel isolates processes and how those boundaries can be legally bypassed. It is a perfect exercise in understanding virtual memory, handles, and system architecture.

F
Ferivonus
Architecting Systems.
Windows APIC++Memory ManagementReverse EngineeringSecurity