I am working with Grey Hat Python book at the moment. It describes on how to create a debugger in python. So far my debugger is able to start the process and attach to it. The problem happens when I try to retrieve a module handle from the process. According to OllyDbg the DLL is present in the program, but GetModuleHandleA
fails to get a handle. I improved a code from the book a little bit so in case GetModuleHandleA
fails to retrieve a handle the function will try to create a remote thread and force to load this module into the process. But even so it GetModuleHandleA
fails (while everything else works fine). So maybe someone can take a quick glance at the code and see the problem in it?
def func_resolve(self,dll,function):
handle = kernel32.GetModuleHandleA(dll)
print "%s module handle is at 0x%08x" % (dll, handle)
error = kernel32.GetLastError()
if error:
print "There was an error in func_resolve::GetModuleHandleA(%s): %d" % (dll, error)
print "Loading library into the process"
pLibRemote = kernel32.VirtualAllocEx(self.h_process, 0, len(dll), 0x00001000, 0x04)
print "Allocated %d bytes of memory at 0x%08x" % (len(dll), pLibRemote)
written = c_int(0)
kernel32.WriteProcessMemory(self.h_process, pLibRemote, dll, len(dll), byref(written))
print "Written %d bytes" % written.value
handle = kernel32.GetModuleHandleA("kernel32.dll")
print "Kernel module handle is 0x%08x" % handle
address = kernel32.GetProcAddress(handle, "LoadLibraryA")
print "LoadLibraryA address is 0x%08x" % address
thread_id = c_ulong(0)
kernel32.CreateRemoteThread(self.h_process, None, 0, address, pLibRemote, 0, byref(thread_id))
print "Created thread %d" % thread_id.value
handle = kernel32.GetModuleHandleA(dll)
address = kernel32.GetProcAddress(handle, function)
kernel32.CloseHandle(handle)
return address
The output looks like this:
[*] We have successfully launched the process!
[*] The Process ID I have is: 10380
Proces handle is 228
opengl32.dll module handle is at 0x00000000
There was an error in func_resolve::GetModuleHandleA(opengl32.dll): 126
Loading library into the process
Allocated 12 bytes of memory at 0x002c0000
Written 12 bytes
Kernel module handle is 0x772c0000
LoadLibraryA address is 0x772d498f
Created thread 11136
[*] Address of func: 0x00000000
[*] Setting breakpoint at: 0x00000000
The module handle is retrieved fine if it is used by python.exe (is among the imported list of python.exe process). But modules that are not in python.exe processes fail. Maybe that could be related somehow to OS Windows 7 (64 bit), but still application that I test against was compiled with a 32 bit compiler.
Update 2: According to recommendation in comments I wrote my own function:
def my_func_resolve(self, dll, function):
module32 = MODULEENTRY32()
CreateToolhelp32Snapshot = kernel32.CreateToolhelp32Snapshot
CreateToolhelp32Snapshot.restype = HANDLE
CreateToolhelp32Snapshot.argtypes = [DWORD, DWORD]
Module32First = kernel32.Module32First
Module32First.restype = BOOL
Module32First.argtypes = [HANDLE, POINTER(MODULEENTRY32)]
Module32Next = kernel32.Module32Next
Module32Next.restype = BOOL
Module32Next.argtypes = [HANDLE, POINTER(MODULEENTRY32)]
thandle = 24
while thandle == 24:
thandle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, self.pid)
if thandle == 0 or thandle == 0xFFFFFFFF:
print "Failed to create a snapshot. Error: %d" % kernel32.GetLastError()
exit()
if not Module32First(thandle, byref(module32)):
print "Module32First failed. Error: %d" % kernel32.GetLastError()
kernel32.CloseHandle(thandle)
exit()
while module32:
print "DLL %s is loaded at 0x%08x" % (module32.szModule, module32.modBaseAddr)
Module32Next(thandle, byref(module32))
kernel32.CloseHandle(thandle)
return True
but it fails with
[*] We have successfully launched the process!
[*] The Process ID I have is: 9584
Proces handle is 228
Failed create snapshot. Error: 299
Which is ERROR_PARTIAL_COPY and happens if we are trying to retrieve 64 bit process from 32 bit process. I have 32 bit python. My OS is 64 bit. I compiled testprog.exe using mingw 32 bit compiler. How that happened that I get this error now?
For TH32CS_SNAPMODULE
I used both 0x00000008
and 0x00000010
Just in case, the process is created in this way:
if kernel32.CreateProcessA(path_to_exe,
None,
None,
None,
None,
creation_flags,
None,
None,
byref(startupinfo),
byref(process_information)):
print "[*] We have successfully launched the process!"
print "[*] The Process ID I have is: %d" % \
process_information.dwProcessId
self.pid = process_information.dwProcessId
self.h_process = self.open_process(process_information.dwProcessId)
print "Proces handle is %d" % self.h_process
GetModuleHandle
looks for a module in the current process. To find a module in another process you need to use the PSAPI functionsEnumProcessModulesEx
&GetModuleBaseName
or the Tool Help functionsCreateToolhelp32Snapshot
,Module32First
, &Module32Next
.If the target process has the same architecture as the current process, then you can indirectly find procedure addresses in its loaded DLLs. First, load the DLL in the current process via
LoadLibraryEx
withDONT_RESOLVE_DLL_REFERENCES
. Then callGetProcAddress
with this localHMODULE
to get the local address. Finally, adjust the local address relative to the module base address in the target process. Remember to callFreeLibrary
to unload the DLL from the current process.Note that
HMODULE
handles are actually pointers, so you'll need to setrestype
andargtypes
for all ctypes functions. This prevents truncating 64-bit pointer values as 32-bit Cint
values.Here's an example using the Tool Help functions.
Here's a test that creates another Python process and verifies that kernel32.dll is loaded at the same address as the current process; that
LoadLibraryExW
is resolved at the same address; and that the first 1000 bytes are equal.Note that I use a Windows Event object to wait for the child process to finish loading before attempting to read its module table. This avoids the problem with
ERROR_PARTIAL_COPY
. If the target is a GUI process with a message queue, you can instead useWaitForInputIdle
.According to the documentation on System Error Codes, error code 126 is
ERROR_MOD_NOT_FOUND
. You might want to review the DLL Search Path to make sure the DLL is installed in the right place. opengl32.dll is pretty common though, so I'd expect this to be available.Another possibility could be that your code is calling
GetModuleHandleA
(the Windows code page or "ANSI" version of the function), but passing wide character Unicode strings.GetModuleHandleA
would not be able to interpret Unicode strings properly, so it would search for the wrong module. If this were the case, then the fix would be to change your code to callGetModuleHandleW
. Python 3 in particular uses Unicode for strings, so if you're running with Python 3, then this is likely to be relevant.The Unicode in the Windows API documentation has more discussion of the
A
vs.W
naming convention for functions and the distinction between functions capable of handling Windows code pages and functions capable of handling Unicode.This previous question looks similar.
Call to GetModuleHandle on kernel32 using Python C-types