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 error, "ERROR_INVALID_PARAMETER 00000057". I've been doing some research and I've found that as the error says one of my parameters is bad when I call CreateRemoteThread. I can't seem to figure out what parameter is bad as all the values that I send in seem valid. I set an Olly conditional break point on the call to LoadLibrary and the dll name and (full) path are correct. I also don't see my custom dll loaded in the memory space of the process (in Olly). I'm wondering if it has to do with the fact that my dll and path are unicode when I send them in as a parameter. Although the conditional breakpoint on LoadLibrary says the correct name and path. I also set the argtype and as I understand it an error would be thrown if it was the wrong type and it would try to convert it to the correct type when possible.
import sys
from ctypes import *
from ctypes import wintypes
import ctypes
BYTE = c_ubyte
WORD = c_ushort
DWORD = c_ulong
LPBYTE = POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE = c_void_p
PVOID = c_void_p
LPVOID = c_void_p
UNIT_PTR = c_ulong
SIZE_T = c_ulong
LPTHREAD_START_ROUTINE = c_void_p
class SECURITY_ATTRIBUTES(ctypes.Structure):
_fields_ = [("nLength", DWORD),
("lpSecurityDescriptor", LPVOID),
("bInheritHandle", wintypes.BOOL)]
LPSECURITY_ATTRIBUTES = POINTER(SECURITY_ATTRIBUTES)
kernel32.CreateRemoteThread.retype = wintypes.HANDLE
kernel32.CreateRemoteThread.argtypes = [wintypes.HANDLE, LPSECURITY_ATTRIBUTES, ctypes.c_size_t, LPTHREAD_START_ROUTINE, wintypes.LPVOID, wintypes.DWORD, wintypes.LPDWORD]
pid = sys.argv[1]
dll_path = sys.argv[2] #'myDLL.dll'
dll_len = len(dll_path) * 2 #Multiplied by 2 so it would take into account the unicode characters
h_process = kernel32.OpenProcess( PROCESS_ALL_ACCESS, False, int(pid))
arg_address = kernel32.VirtualAllocEx(h_process, 0, dll_len, VIRTUAL_MEM, PAGE_READWRITE)
written = c_ubyte(0)
bSuccess = kernel32.WriteProcessMemory(h_process, arg_address, dll_path, dll_len, byref(written))
h_kernel32 = kernel32.GetModuleHandleW('kernel32.dll')
h_loadlib = kernel32.GetProcAddress(h_kernel32, b"LoadLibraryW")
thread_id = c_ulong(0)
h_thread = kernel32.CreateRemoteThread(h_process, #404
None,
0,
h_loadlib, #0x770a0000
arg_address, #0x770eef42
0,
byref(thread_id))
h_threadError = GetLastError() #This says ERROR 0 - Operation completed Successfully
h_dllToHook = kernel32.GetModuleHandleW('myDLL.dll') #h_dllToHook returns '0'
error = GetLastError() #This says ERORR 0 - Operation completed Successfully
Another weird thing is the fact that the executable I'm injected is a console application and prints some stuff out. The dll that I'm injecting has an exported function that is called from DLLMAIN that prints stuff out as well. When I check the console it looks like it successfully ran as the stuff in the injected DLL was printed out as well. Also when I put a conditional logging breakpoint on CreateRemoteThread it never gets hit. So my questions are if it is successfully injecting as it seems to be 1) why can't I get a handle to the injected DLL using GetModuleHandleW and 2) why isn't Ollydbg showing that the injected DLL isn't mapped into the process' memory space. I'm stepping through my code and breaking so it's not like the thread is running through and exiting. I've been researching for a while so any help is greatly appreciated! Thanks.
ctypes.get_last_error
instead ofGetLastError
. This requires theuse_last_error
option, e.g.WinDLL('kernel32.dll', use_last_error=True)
.GetModuleHandleW
andGetProcAddress
steps are unnecessary. ctypes already does this for you. Just usekernel32.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 includeMEM_COMMIT
?retype
instead ofrestype
for the prototype ofCreateRemoteThread
, which means the return value is still the default 32-bit Cint
.The following works for me, loading a DLL into a Python process.
dllinject.py (ctypes defintions):
dllinject.py (
injectdll
):test.c:
demo: