DLL Injector Tutorial: Step-by-Step Injection Methods
Warning: DLL injection is a technique used by both legitimate developers (for debugging, extending apps, or hooking APIs) and by malware authors. Use the techniques below only on systems and applications you own or have explicit permission to test. Unauthorized use may be illegal.
What is DLL injection?
DLL injection is the process of forcing a target process to load and execute code from a Dynamic Link Library (DLL). Common legitimate uses include debugging, instrumentation, and extending application behavior; malicious uses include persistence, keylogging, and code execution.
Prerequisites
- Windows development environment (Windows ⁄11 or newer).
- Visual Studio or another C/C++ compiler.
- Basic knowledge of C/C++, Win32 API, and process internals.
- Administrator privileges for injecting into protected processes.
Overview of methods covered
- CreateRemoteThread + LoadLibrary
- SetWindowsHookEx
- AppInitDLLs (legacy)
- Manual mapping (reflective DLL injection)
- Process hollowing / code cave techniques (brief overview)
1) CreateRemoteThread + LoadLibrary (most common)
Concept: Write the DLL path into the target process memory, then call LoadLibraryW inside the remote process via CreateRemoteThread.
Step-by-step:
- Open target process:
c
HANDLE hProc = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VMREAD, FALSE, pid);
- Allocate memory in target:
c
LPVOID remoteMem = VirtualAllocEx(hProc, NULL, (wcslen(dllPath)+1)sizeof(wchar_t), MEM_COMMIT, PAGEREADWRITE);
- Write DLL path:
c
WriteProcessMemory(hProc, remoteMem, dllPath, (wcslen(dllPath)+1)sizeof(wchart), NULL);
- Get LoadLibraryW address:
c
LPVOID loadLibAddr = (LPVOID)GetProcAddress(GetModuleHandleW(L“kernel32.dll”), “LoadLibraryW”);
- Create remote thread to call LoadLibraryW:
c
HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibAddr, remoteMem, 0, NULL); WaitForSingleObject(hThread, INFINITE);
- Clean up:
- Optionally get remote module handle via GetExitCodeThread.
- Free allocated memory with VirtualFreeEx.
- Close handles.
Notes:
- Use Unicode LoadLibraryW with wide strings.
- Some modern protections (ASLR, DEP, code signing, anti-cheat) may block or complicate this.
2) SetWindowsHookEx
Concept: Install a system-wide or thread-specific hook that forces Windows to load a DLL into processes where the hook runs (e.g., WHGETMESSAGE).
Steps (summary):
- Create a DLL exporting the hook procedure (CALLBACK).
- Call SetWindowsHookEx with target thread ID or NULL for system-wide:
c
HHOOK hHook = SetWindowsHookEx(WH_GETMESSAGE, HookProc, hDllModule, targetThreadId);
- For system-wide hooks, the DLL must be located in a writable location and all sessions will attempt to load it.
Notes:
- Requires appropriate privileges.
- Hooks are limited to GUI-enabled processes; not effective for headless services.
- This method can be noisy and visible to the user.
3) AppInit_DLLs (legacy, not recommended)
Concept: Windows used to provide an AppInit_DLLs registry value to load DLLs into every user-mode process that loads User32.dll.
Summary:
- Set registry key HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs with a semicolon-separated list of DLL paths.
- Requires enabling LoadAppInit_DLLs and potentially disabling Secure Boot/patch guard on newer systems.
Notes:
- Deprecated and disabled by default in modern Windows due to security.
- Not suitable for modern development or testing.
4) Manual mapping / Reflective DLL injection (advanced)
Concept: Load a DLL into a process without calling LoadLibrary, by parsing the DLL PE headers, allocating memory, copying sections, resolving imports, performing relocations, and executing its entry point. This avoids touching the IAT and can bypass some hooks and detection.
High-level steps:
- Read DLL file into memory in the injector.
- Parse DOS/PE headers and allocate a properly sized region in the target process with EXECUTE/READ permissions (VirtualAllocEx).
- Write headers and sections into the target.
- Resolve imports: for each IMAGE_IMPORT_DESCRIPTOR, locate module and function addresses, write the import table.
- Apply relocations based on the chosen base address.
- Create a remote thread (or use RtlCreateUserThread) to call the DLL’s entrypoint (DllMain) with DLL_PROCESS_ATTACH.
Notes:
- Complex, error-prone, and often flagged by AV/EDR.
- Useful for stealthier injection and for injecting DLLs not present on disk (loading from memory).
- Consider building the reflective loader inside the DLL itself (reflective DLL).
5) Process hollowing / code cave (overview)
Concept: Start a legitimate process in suspended state, unmap its sections, write your own executable code into it, then resume so it runs your code under the guise of the legitimate process.
Steps (summary):
- CreateProcess with CREATESUSPENDED.
- Unmap or overwrite the main image using ZwUnmapViewOfSection or similar.
- Allocate and write new image, fix context (entry point) and resume thread.
Notes:
- Often used by advanced malware; high detection risk.
- Not technically DLL injection but achieves similar goals.
Defensive considerations and mitigations
- Use code signing, driver signing, and protected processes to limit injection.
- Enable Windows Defender, exploit protection (Control Flow Guard, DEP), and anti-tamper measures.
- Monitor suspicious OpenProcess/CreateRemoteThread/WriteProcessMemory API usage.
- Employ process integrity checks and minimize running privileged processes under user contexts.
Example: Minimal injector (CreateRemoteThread + LoadLibrary)
Compile in Visual Studio as a 64-bit executable when targeting 64-bit processes.
c
#include#include int wmain(int argc, wchar_t argv[]){ if(argc<3) return -1; DWORD pid = (DWORD)_wtoi(argv[1]); const wchar_t dllPath = argv[2]; HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); LPVOID mem = VirtualAllocEx(hProc, NULL, (wcslen(dllPath)+1)2, MEM_COMMIT, PAGE_READWRITE); WriteProcessMemory(hProc, mem, (LPVOID)dllPath, (wcslen(dllPath)+1)2, NULL); LPVOID addr = GetProcAddress(GetModuleHandleW(L“kernel32.dll”), “LoadLibraryW”); HANDLE th = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)addr, mem, 0, NULL); WaitForSingleObject(th, INFINITE); VirtualFreeEx(hProc, mem, 0, MEM_RELEASE); CloseHandle(th); CloseHandle(hProc); return 0; }
Final notes
- Test on isolated VMs.
- Prefer higher-level APIs (debugging/instrumentation frameworks) where possible.
- Respect software licenses and laws.
Date: February 5, 2026.
Leave a Reply