5

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!

2 Answers 2

7

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.