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