I\'m trying to perform DLL injection using Python\'s Ctypes. I attach Olly to the process that I\'m trying to inject and the thread that I\'m trying to creates gives the er
ctypes.get_last_error
instead of GetLastError
. This requires the use_last_error
option, e.g. WinDLL('kernel32.dll', use_last_error=True)
. GetModuleHandleW
and GetProcAddress
steps are unnecessary. ctypes already does this for you. Just use kernel32.LoadLibraryW
. This depends on kernel32.dll always being mapped to the same base address in each process, which I think is true for existing versions of Windows.len(dll_path) + 1
. In this case you're committing a new page of memory (4 KiB on x86 and x64 systems), which is initially all zeros. VIRTUAL_MEM
allocation type. Does that include MEM_COMMIT
?retype
instead of restype
for the prototype of CreateRemoteThread
, which means the return value is still the default 32-bit C int
. The following works for me, loading a DLL into a Python process.
dllinject.py (ctypes defintions):
import ctypes
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32.dll', use_last_error=True)
PROCESS_VM_OPERATION = 0x0008
PROCESS_VM_WRITE = 0x0020
PROCESS_CREATE_THREAD = 0x0002
MEM_COMMIT = 0x1000
MEM_RELEASE = 0x8000
PAGE_READWRITE = 0x0004
INFINITE = -1
SIZE_T = ctypes.c_size_t
LPSIZE_T = ctypes.POINTER(SIZE_T)
WCHAR_SIZE = ctypes.sizeof(wintypes.WCHAR)
LPSECURITY_ATTRIBUTES = wintypes.LPVOID
LPTHREAD_START_ROUTINE = wintypes.LPVOID
class BOOL_CHECKED(ctypes._SimpleCData):
_type_ = "l"
def _check_retval_(retval):
if retval == 0:
raise ctypes.WinError(ctypes.get_last_error())
return retval
class LPVOID_CHECKED(ctypes._SimpleCData):
_type_ = "P"
def _check_retval_(retval):
if retval is None:
raise ctypes.WinError(ctypes.get_last_error())
return retval
HANDLE_CHECKED = LPVOID_CHECKED # not file handles
kernel32.OpenProcess.restype = HANDLE_CHECKED
kernel32.OpenProcess.argtypes = (
wintypes.DWORD, # dwDesiredAccess
wintypes.BOOL, # bInheritHandle
wintypes.DWORD) # dwProcessId
kernel32.VirtualAllocEx.restype = LPVOID_CHECKED
kernel32.VirtualAllocEx.argtypes = (
wintypes.HANDLE, # hProcess
wintypes.LPVOID, # lpAddress
SIZE_T, # dwSize
wintypes.DWORD, # flAllocationType
wintypes.DWORD) # flProtect
kernel32.VirtualFreeEx.argtypes = (
wintypes.HANDLE, # hProcess
wintypes.LPVOID, # lpAddress
SIZE_T, # dwSize
wintypes.DWORD) # dwFreeType
kernel32.WriteProcessMemory.restype = BOOL_CHECKED
kernel32.WriteProcessMemory.argtypes = (
wintypes.HANDLE, # hProcess
wintypes.LPVOID, # lpBaseAddress
wintypes.LPCVOID, # lpBuffer
SIZE_T, # nSize
LPSIZE_T) # lpNumberOfBytesWritten _Out_
kernel32.CreateRemoteThread.restype = HANDLE_CHECKED
kernel32.CreateRemoteThread.argtypes = (
wintypes.HANDLE, # hProcess
LPSECURITY_ATTRIBUTES, # lpThreadAttributes
SIZE_T, # dwStackSize
LPTHREAD_START_ROUTINE, # lpStartAddress
wintypes.LPVOID, # lpParameter
wintypes.DWORD, # dwCreationFlags
wintypes.LPDWORD) # lpThreadId _Out_
kernel32.WaitForSingleObject.argtypes = (
wintypes.HANDLE, # hHandle
wintypes.DWORD) # dwMilliseconds
kernel32.CloseHandle.argtypes = (
wintypes.HANDLE,) # hObject
dllinject.py (injectdll
):
def injectdll(pid, dllpath):
size = (len(dllpath) + 1) * WCHAR_SIZE
hproc = hthrd = addr = None
try:
hproc = kernel32.OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
PROCESS_VM_WRITE, False, pid)
addr = kernel32.VirtualAllocEx(
hproc, None, size, MEM_COMMIT, PAGE_READWRITE)
kernel32.WriteProcessMemory(
hproc, addr, dllpath, size, None)
hthrd = kernel32.CreateRemoteThread(
hproc, None, 0, kernel32.LoadLibraryW, addr, 0, None)
kernel32.WaitForSingleObject(hthrd, INFINITE)
finally:
if addr is not None:
kernel32.VirtualFreeEx(hproc, addr, 0, MEM_RELEASE)
if hthrd is not None:
kernel32.CloseHandle(hthrd)
if hproc is not None:
kernel32.CloseHandle(hproc)
test.c:
#include <Windows.h>
#include <stdio.h>
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad)
{
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
printf("DLL Attach\n");
break;
case DLL_PROCESS_DETACH:
printf("DLL Detach\n");
}
return TRUE;
}
demo:
>>> import sys
>>> from subprocess import Popen, PIPE
>>> from dllinject import injectdll
>>> cmd = [sys.executable, '-c', 'import time; time.sleep(10)']
>>> p = Popen(cmd, stdout=PIPE); injectdll(p.pid, 'test.dll')
>>> r = p.wait(); print(p.stdout.read().decode())
DLL Attach
DLL Detach