Call to GetModuleHandle on kernel32 using Python C-types

Question:

I’m trying to use Python’s C-Types to call GetModuleHandleA on the kernel32 library. I’d like to get a handle to the library so I can use it to call GetProcAddress for LoadLibraryA. Below is my code…

import sys
from ctypes

kernel32 = windll.kernel32
print("The kernel32 is %s" % kernel32)
#The kernel32 is <WinDLL 'kernel32', handle 765b0000 at 1c2a9f0>

h_kernel32 = kernel32.GetModuleHandleA("C:\Windows\System32\kernel32.dll")
if h_kernel32 == False:
        error = GetLastError()
        print("ERROR: %d - %s" % (error, FormatError(error)))

I’m getting an error, “ERROR: 126 – The specified module could not be found”. I’ve also tried “C:/Windows/System32/kernel32.dll” and just “kernel32”. I’m using Python 3.2 and this is on a Windows 7 machine. I’ve verified that the dll is there and in the path that I have set in the code above. I’ve been doing some research and can’t seem to find out what the issue is. Any help is greatly appreciated. Thanks!

Asked By: User4679

||

Answers:

The handle is stored in kernel32._handle. Calling GetModuleHandle should return the same value, but make sure you set restype and argtypes for type safety [*]:

import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

kernel32.GetModuleHandleW.restype = wintypes.HMODULE
kernel32.GetModuleHandleW.argtypes = [wintypes.LPCWSTR]

hMod = kernel32.GetModuleHandleW('kernel32.dll')

Notice the ‘W’ suffix instead of ‘A’. Python 3 uses Unicode strings, for which ctypes creates a c_wchar_p (LPCWSTR). There’s no reason to call the [A]NSI version since it’s just a wrapper around the [W]ide string version. But if you must, then you need to use bytes:

kernel32.GetModuleHandleA.restype = wintypes.HMODULE
kernel32.GetModuleHandleA.argtypes = [wintypes.LPCSTR]

hMod = kernel32.GetModuleHandleA(b'kernel32.dll')

[*] I recommend using kernel32 = WinDLL('kernel32', use_last_error=True) instead of windll.kernel32. This avoids conflicts with other modules that use windll. It also enables protection for the thread’s LastErrorValue. In this case, use ctypes.get_last_error() and ctypes.set_last_error(err) instead of directly calling WinAPI GetLastError and SetLastError.

Answered By: Eryk Sun

As for how it’s done internally, ctypes.windll.kernel32 is WinDLL("kernel32"). WinDLL inherits CDLL, whose __init__ opens a handle to kernel32.dll using self._handle = ctypes._dlopen (from _ctypes import LoadLibrary as _dlopen), and LoadLibrary is bound to the load_library C function in the runtime, which calls LoadLibraryExW, and then returns a call to PyLong_FromVoidPtr, which creates a PyObject (PyLong) from a C void pointer (HMODULE) and returns it, and just like a regular function implemented in python, it now internally has a PyObject that it uses to assign to self._handke). The CDLL __getattr__ is called by the runtime the first time "GetModuleHandle" is used on kernel32, and this python code instantiates an object of _FuncPtr type for it, which inherits _CFuncPtr, which is a _ctypes.CFuncPtr (from _ctypes import _CFuncPtr as CFuncPtr).

The type identifier _ctypes.CFuncPtr is bound to a C object (PyCFuncPtr_Type of type PyTypeObject) in the definition of said C object in the runtime library; the identifier string is a member of the object. Remember that under the hood the runtime creates PyObjects for all python objects (including python functions), and a PyTypeObject for all python types, this is usually done implicitly by the runtime but in this case the python code explicitly creates a function object that can be used to make the native call. It now uses the constructor PyCFuncPtr_new set in the type object (PyCFuncPtr_Type) in C to instantiate the actual function object from the function type object, which takes the args, in this case the kernel32 WinDLL object and "GetModuleHandle", and calls PyCFuncPtr_FromDll (because the very fact there are arguments means it’s not a regular function) to create a new function object and associate it with the actual function address using GetProcAddress on the handle to the module in WinDLL set up in the CDLL __init__.

A call performed on this function object will cause python to call the address in this object, which will be GetProcAddress instead of an address of a function that interprets the bytecode at the bytecode address… I assume.

Next time the __getattr__ is not called because the attribute now exists.

Answered By: Lewis Kelsey
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.