<100 subscribers
Share Dialog

DLL Injection is an exploit technique that can be used for anything from harmless game mods to sophisticated malware campaigns. At its core, DLL Injection is about convincing a running process to execute code it never intended to run - whether that's adding custom features to your favorite game, or establishing persistence in a compromised system.
As part of my 100 day Infosec challenge, I decided I would spend today diving deeper into DLLs, and learn about how they can be used to exploit Windows machines. Here are some of my key takeaways.
Before we can understand what DLL injection is and how it works, we need to know what a DLL is!
A DLL (Dynamic Link Library) is a Windows binary in the Portable Executable (PE) format. They are used to export code/data that can be imported by other programs at runtime. Every PE file contains several important components. We'll briefly go over only a few of them here:
Import Address Table (IAT): Lists the functions that the DLL depends on from other DLLs. During the loading of these external DLLs, the Windows loader resolves the symbolic names and populates the IAT with function pointers. (Ref. https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#import-address-table)
Export Table: Lists the functions that the DLL exposes to other programs; its public API. (Ref. https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-edata-section-image-only)
DllMain: An optional entry point called by the loader during process/thread attach/detach events. It allows the DLL to execute initialization code. (Ref. https://learn.microsoft.com/en-us/windows/win32/dlls/dllmain)
For the sake of this article we’ll use the term “Windows Loader” to describe the cooperation between user-mode components (like ntdll.dll) and kernel-mode components (like the PE loader in ntoskrnl.exe) that make loading DLLs possible. So when we say “the loader” just know that it’s a system of multiple components working together, not a single program.
When a process needs a DLL, the Windows loader performs a carefully choreographed sequence:
Map the PE file from disk into the process's virtual address space.
Apply address relocations if needed.
Walk the import table and resolve all external function references.
Call DllMain with DLL_PROCESS_ATTACH to allow the DLL to initialize.
The DLL is now ready for use by the process.
Ref. Microsoft Docs Run-Time Dynamic Linking
The key idea behind DLL injection is to subvert or otherwise manipulate this loading sequence to execute arbitrary code inside the target process.
The traditional approach to achieving DLL injection follows this pattern:
Get a handle to the target process (OpenProcess with appropriate privileges)
Allocate memory in the target’s address space (VirtualAllocEx)
Write the DLL path into that memory (WriteProcessMemory)
Create a remote thread that calls LoadLibrary on that path (CreateRemoteThread)
LoadLibrary does all the heavy lifting. The normal Windows loader takes over and performs all the steps we described earlier. The catch with this approach though is that it’s noisy. Modern Endpoint Detection and Response (EDR) solutions watch for exactly this pattern: OpenProcess → VirtualAllocEx → WriteProcessMemory → CreateRemoteThread.
The weakness (e.g., the ease in which it can be detected) in the classic injection method above is due to the traces left behind by the loading process. In the classic injection method, the malicious DLL must exist as a file on disk and LoadLibrary must be called. This leaves behind two easily detected artifacts: (1) the DLL file itself and (2) The event logs associated with LoadLibrary invocations. Reflective injection, on the other hand, contains its own custom loader code, and only lives in-memory (not on disk). Hence reflective injection is harder to detect.
Because reflective DLLs bypass LoadLibrary, they must include a special function (the “reflective loader”) that manually implements everything the OS loader would normally do.
The Meterpreter payload does all of this! First the initial stager (essentially a tiny piece of shellcode) establishes a reverse connection. This allows us to transfer the Meterpreter DLL over the network into target memory. The remaining shellcode stages allocate memory (
VirtualAllocEx) and write the reflective DLL into a process's memory region (WriteProcessMemory). Execution is transferred to the reflective loader , which performs all the PE loading tasks manually.DllMainthen executes, and Meterpreter establishes its full C2 channel.
In-memory injection makes incident response and forensics significantly harder. There’s no malicious binary to find on disk, no file creation events, and no LoadLibrary logs. Memory analysis requires knowing what to look for, so it is harder to detect, but by no means impossible (runtime/ memory artifacts are left behind).
I often get confused about related but distinct exploit techniques that are not considered injection attacks. I’ll attempt to make distinctions between them here.
Disk-based DLL loading, for example, involves placing a malicious DLL where a process will naturally load it. Two methods of doing this are:
DLL search-order hijacking: Exploits the DLL search path when calling LoadLibrary to load a malicious DLL instead of the legitimate one
DLL sideloading: placing a malicious DLL with the right name next to a legitimate executable that will load it. The following article goes more in-depth on this: Bitdefender - What is DLL Sideloading.
Process replacement techniques like process hollowing or process doppelgänging create what looks like a benign process but replace its code entirely. These are all related but distinct from DLL injection; the main distinction is that they’re not adding code to a running process.
They key difference between DLL injection and process doppelgänging, for example, is that DLL injection involves forcefully loading a malicious DLL into an already running legitimate process, while process doppelgänging involves creating a new, hidden, malicious process that masquerades as a legitimate one using file transactions.
DLL injection is a fundamental technique in offensive security. From dropping a Meterpreter shell in a CTF to advanced persistent threats, the ability to run code in another process’s context remains valuable across the threat spectrum. Reflective DLL injection specifically demonstrates how adversaries evolve techniques to evade detection -- by removing disk artifacts and implementing their own custom loaders, they operate in the gaps of traditional security mechanisms.
For security practitioners, understanding these techniques isn’t only about learning to attack systems, but about comprehending how modern malware operates so we can build better defenses, hunt more effectively, and respond to incidents with full knowledge of what adversaries are capable of.
Microsoft Docs PE Format
MITRE ATT&CK Process Injection: Dynamic-link Library Injection
red canary DLL Search Order Hijacking
OffSec About the Metasploit Meterpreter
MITRE ATT&CK Process Injection: Process Hollowing
MITRE ATT&CK Process Injection: Process Doppelgänging
Scott Nusbaum, TrustedSec Loading DLLS Reflections
No comments yet