i am trying to work on easyhook in python and here is my code
# Hook/EasyHook.py
from ctypes import *
from ctypes.util import find_library
from pathlib import Path
c_ulong_p = POINTER(c_ulong)
c_void_pp=POINTER(c_void_p)
res_path = str(Path(__file__).parent / 'res' / 'EasyHook64.dll')
lib_path = find_library(res_path)
clib = cdll.LoadLibrary(lib_path)
class TRACED_HOOK_HANDLE(Structure):
_fields_ = [("Link", c_void_p)]
lh_install_hook = clib.LhInstallHook
lh_install_hook.restype = c_ulong
lh_install_hook.argtypes = [c_void_p, c_void_p, c_void_p, TRACED_HOOK_HANDLE]
# some definition of other functions...
if __name__ == '__main__':
from ctypes.wintypes import *
t_dll = CDLL('User32.dll')
test=lambda:t_dll.MessageBoxW(None, 'hi content!', 'hi title!', 0)
test()
interface=CFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
def fake_function(handle, title, message, flag):
return t_original(handle, "hooked "+title, "hooked "+message, flag)
t_hook_info = TRACED_HOOK_HANDLE(None)
if lh_install_hook(t_dll.MessageBoxW, interface(fake_function), None, byref(t_hook_info)):
raise Exception("Hook error[%s]:\n%s" % (rtl_get_last_error(), rtl_get_last_error_string()))
# error occur here and the program terminate
# some other tests...
after a try, it exit on code 0xC0000005 when running to lh_install_hook calling and without any exception printed
then I tried to use those Api after inject into a C++ program by
lh_install_hook(func_address, interface(hook_function), None, byref(hook_info))
where func_address is the actual address of target call,and it cause
python38.dll+24174
_ctypes.pyd+A48D
python38.dll+33E00
python38.dll+3DA6E
_ctypes.pyd+3C69
_ctypes.pyd+38AB
python38.dll+507F5
python38.dll+491C8
is there any way to make it run?
Edit:
here is my code inject and run in the c++ programe
# Hook/__init__.py
from .EasyHook import *
class Hook(object):
def __init__(self, func_address: int):
self.enabled = False
self.hook_info = TRACED_HOOK_HANDLE(None)
self._ACLEntries = (c_ulong * 1)(0)
self.ACLEntries = cast(self._ACLEntries, POINTER(c_ulong))
interface = CFUNCTYPE(self.restype, *self.argtypes)
def hook_function(*args):
return self.hook_function(*args)
if lh_install_hook(func_address, interface(hook_function), None, byref(self.hook_info)):
raise LocalHookError()
# error occur here and the program terminate
# some other codes...
restype = c_void_p
argtypes = []
def hook_function(self, *args):
return self.original(*args)
# main.py
from Hook import Hook
from ctypes import *
from ctypes.wintypes import *
class kernel32_beep_hook(Hook):
restype = c_bool
argtypes = [DWORD,DWORD]
def hook_function(self, a1, a2):
if logger is not None:
logger.log('beep_hook','%s,%s'%(a1,a2))
return self.original(a1,a2)
# some skip codes
addr=kernel32.GetProcAddress(kernel32_module,b"Beep")
ctypes.windll.kernel32.Beep(500,500)
hook=kernel32_beep_hook(addr)
# error occur here and the program terminate
According to [GitHub]: EasyHook/EasyHook - (master) EasyHook/Public/easyhook.h:
typedef struct _HOOK_TRACE_INFO_
{
PLOCAL_HOOK_INFO Link;
}HOOK_TRACE_INFO, *TRACED_HOOK_HANDLE;
TRACED_HOOK_HANDLE is actually a pointer (although its name suggests the opposite), therefore your lh_install_hook.argtypes (1st snippet) is incorrect. It should be:
lh_install_hook.argtypes = [c_void_p, c_void_p, c_void_p, POINTER(TRACED_HOOK_HANDLE)]
Technically, you ran into [SO]: C function called from Python via ctypes returns incorrect value (#CristiFati's answer).
Regarding no exception being thrown, maybe [SO]: Python exception thrown by libtidy is amusingly impossible to catch (#CristiFati's answer) should shed some light.
This should get past the problem, at least the main one. I'm not sure whether there are others, as I didn't install (or build) the .lib, so I didn't run your code. My knowledge is very limited (so this might be complete nonsense), but one potential spot to generate problems is TRACED_HOOK_HANDLE->Link being initialized to NULL.
Related
I am trying to call the following function in a DLL, which (to my limited understanding), takes a callback as a parameter.
C# definitions:
//Error handler
public delegate int FPtrErrorHandler(int ErrorType, int MessageType, int WhomToInform, String ErrorMessage);
//Desired function to call
[ DllImport ( Globals.LibDLLPath, CallingConvention = CallingConvention.Cdecl ) ]
public static extern int OpenLib(String TempDirectory, Bool IfCleanTempDir, FPtrErrorHandler ErrorHandler);
Per this SO entry, I created the Python code below:
import ctypes as c
from ctypes import *
#c.WINFUNCTYPE(c.c_int, c.c_int, c.c_int, c.c_int, c.c_char_p)
def FPtrErrorHandler(ErrorIdentifier, ErrorMessageType, WhomToInform, ErrorMessage):
print(f'Error ID={ErrorIdentifier}, Type={ErrorMessageType}, WhomToInform={WhomToInform}, msg={ErrorMessage}')
return 0
lib = windll.LoadLibrary('path_to_dll')
_OpenLib = lib[163]
_OpenLib.restype = c_int
_OpenLib.argtypes = [c_char_p, c_bool, c_void_p]
def OpenLib(TempDirectory,IfCleanTempDir):
cb1 = FPtrErrorHandler
return _OpenLib(TempDirectory, IfCleanTempDir, cb1)
n = OpenLib(r:'c:\temp',c_bool(True))
Unfortunately, I get the dreaded message
ArgumentError: argument 1: <class 'TypeError'>: wrong type
Thanks in advance for helping me with this. I'm really happy to learn ctypes and how to use Python to interact with the amazing DLLs that are out there.
OMG, I think this might be an easy one.
If I change the call to the function from
n = OpenLib(r:'c:\temp',c_bool(True))
to
n = OpenLib(rb:'c:\temp',c_bool(True))
it seems to be working. I just added the b modifier in the string prefix.
I am trying to access the CoreMidi functions from the CoreFoundation framework in MacOSX using ctypes in Python.
When I call a function that doesn't have any parameters, everything goes fine. For example, the following code:
from ctypes import *
core_midi = cdll.LoadLibrary("/System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI")
numOfDevices = core_midi.MIDIGetNumberOfDevices()
print numOfDevices
returns
3
which is the number of MIDI devices in my computer.
However, I am not able to execute functions which require parameters. Check this example (EDIT: as eryksun pointed out, I was using a char* as client_name, and the function prototype demanded a CFString. I corrected this in the code example below but I still get the same error):
core_midi = cdll.LoadLibrary("/System/Library/Frameworks/CoreMIDI.framework/Versions/A/CoreMIDI")
client_name = core_foundation.CFStringCreateWithCString(None, "MIDI Client", 0)
midi_client = c_uint()
result = core_midi.MIDIClientCreate(client_name, None, None, byref(midi_client))
print midi_client
print result
This code doesn't print anything at all, doesn't even raise an exception. The MIDIClientCreate function's prototype is:
extern OSStatus MIDIClientCreate(
CFStringRef name,
MIDINotifyProc notifyProc,
void *notifyRefCon,
MIDIClientRef *outClient );
MIDIClientRef is defined as a UInt32, and, as I understand, it receives a pointer to a MIDIClient structure that is created, that is why I use byref() to pass it as a parameter. If I just pass the variable without byref(), the function call returns a value -50, which probably indicates some bizarre error.
EDIT: I am not sure if I am creating the CFString correctly. I tried to test the result with the following code but it doesn't print anything on screen.
client_name = core_foundation.CFStringCreateWithCString(None, "MIDI Client", 0)
cstr = ctypes.create_string_buffer(20)
core_foundation.CFStringGetCString(client_name, cstr, 20, 0)
print cstr
Thanks!
EDIT: Answered by eryksun!
I didn't know this of course, but setting the pointers is not as obvious as my naive example was trying to do.
class _CFString(Structure):
pass
cf_path = ctypes.util.find_library("CoreFoundation")
cm_path = ctypes.util.find_library("CoreMIDI")
core_foundation = ctypes.cdll.LoadLibrary(cf_path)
core_midi = ctypes.cdll.LoadLibrary(cm_path)
CFStringRef = POINTER(_CFString)
midi_client = ctypes.c_uint()
core_foundation.CFStringCreateWithCString.restype = CFStringRef
core_foundation.CFStringCreateWithCString.argtypes = [c_void_p, c_char_p, c_uint32]
client_name = core_foundation.CFStringCreateWithCString(None, "MIDI Client", 0)
core_midi.MIDIClientCreate.argtypes = [CFStringRef, c_void_p, c_void_p, POINTER(c_uint32)]
result = core_midi.MIDIClientCreate(client_name, None, None, byref(midi_client))
print midi_client
print result
Actually, I though restype and argtypes didn't affect how functions were executed or how parameters were passed to them, but it seems that they do.
The above code results in:
c_uint(4657178L)
0
That is, my MIDI client is created somewhere and the function returns without error.
Thanks again eryksun!
I've been trying to use the digi Advanced Device Discovery protocol library with python using ctypes.
the context:
Windows 7 x64
python 2.7.5
dll library
here's my current code:
guid = (0xbf6db409,0xc83d,0x44a3,0xa3,0x6d,0x21,0x79,0x7d,0x2f,0x73,0xf9)
class ADDP():
from ctypes import Structure
class GUID(Structure):
from ctypes.wintypes import DWORD,WORD,BYTE
_fields_ = [("Data1",DWORD),
("Data2",WORD),
("Data3",WORD),
("Data4",BYTE * 8)]
def __init__(self, guid):
from ctypes import windll, c_void_p, c_byte, pointer,c_char,POINTER
from ctypes.wintypes import HANDLE
import ctypes
self.dll = windll.LoadLibrary("D:\\Lib\\addp.dll")
self.guid = self.GUID()
self.guid.Data1 = guid[0]
self.guid.Data2 = guid[1]
self.guid.Data3 = guid[2]
self.guid.Data4 = (c_byte * 8)(guid[3],guid[4],guid[5],guid[6],guid[7],guid[8],guid[9],guid[10])
addpopen = self.dll[1]
addpopen.argtypes = [POINTER(self.GUID),]
addpopen.restype = c_void_p
#print addpopen.restype
self.handler = addpopen(pointer(self.guid))
if self.handler == None:
raise RuntimeError()
self.opened = False
else:
self.opened = True
def isOpen(self):
return self.opened
def Discover(self):
from ctypes import c_int
srch = self.dll[6]
srch.restype = c_int
print srch(self.handler,10,10)
def Close(self):
close = self.dll[3]
close.restype = None
self.opened = False
#print close(self.handler)
conn = ADDP(guid)
#print conn.handler
conn.Discover()
#conn.Close()
print conn.handler
i searched a lot for how to handle a handle returned from a c function, but couldn't find much about it, i read the ctypes docs for a while, and then inspected the header file too..
the handle is defined in the header file with
typedef void* addp_handle_t;
so i assumed i had to set 'restype' to 'c_void_p', the function always returns 'None'
its specified in the header file that it returns 'None' when an error has occurred, else it return the handle to ADDP session.
another thing, this dll does not export functions by name... i had to, more or less, guess what function is what by expected bytes in arguments.
any ideas on this?
i've found a project on google code but apparently it didn't go far...
if you need any other details, just say
Is there any support of the syscall clone(2) (not os.fork) in Python? I want to play with Linux namespaces under Python but it seems that there is not a lot of information about it.
Edits:
I think ctypes with libc is the answer but I'm still not having any success. fork works without problems since it doesn't have any argument then this code work:
from ctypes import *
libc = CDLL("libc.so.6")
libc.fork()
With clone I'm trying this:
from ctypes import *
def f():
print "In callback."
return 0
libc = CDLL("libc.so.6")
f_c = CFUNCTYPE(c_int)(f)
print libc.getpid()
print libc.clone(f_c)
print get_errno()
Clone actually has this signature:
int clone(int (*fn)(void *), void *child_stack,
int flags, void arg, ...
/ pid_t *ptid, struct user_desc *tls, pid_t *ctid */ );
I still need to pass *child_stack and flags but have no idea how to do it. Any help?
More Edits:
I got this now:
from ctypes import *
def f():
print "In callback."
return 0
libc = CDLL("libc.so.6")
f_c = CFUNCTYPE(c_int)(f)
stack = c_char_p(" " * 1024 * 1024)
libc.clone(f_c, c_void_p(cast(stack, c_void_p).value + 1024 * 1024), 0)
This actually works but I imagine I making a big hole in my system with the stack, there is a cleaner way to do this?
Edits:
Almost done, adding the correct flag for newpid:
from ctypes import *
libc = CDLL("libc.so.6")
def f():
print libc.getpid()
return 0
f_c = CFUNCTYPE(c_int)(f)
stack = c_char_p(" " * 1024 * 1024)
libc.clone(f_c, c_void_p(cast(stack, c_void_p).value + 1024 * 1024), 0x20000000)
This is not runnable just for root, and print a nice 1.
And following this post the stack seems to be fine: http://code.google.com/p/chromium/wiki/LinuxPidNamespaceSupport
Well, finally I think I got the answer, is just usign ctypes with libc,
This is a simple proof of concept:
from ctypes import *
libc = CDLL("libc.so.6")
# Create stack.
stack = c_char_p(" " * 8096)
def f():
print libc.getpid()
return 0
# Conver function to c type returning an integer.
f_c = CFUNCTYPE(c_int)(f)
# We need the top of the stack.
stack_top = c_void_p(cast(stack, c_void_p).value + 8096)
# Call clone with the NEWPID Flag
libc.clone(f_c, stack_top, 0x20000000)
This has to be run by root.
If you want to have "fork() but with new namespace" semantics, you can directly call the SYS_clone syscall instead. Note that if you do so, the os.getpid() method will return the wrong process ID in the child because glibc caches the process ID and doesn't know about the SYS_clone invocation to invalidate its cache.
Assuming x86_64 (NR_clone == 56, NR_getpid == 39), you can call libc.syscall(56, signal.SIGCHLD|0x000200000, 0 0 0) to "fork", and then libc.syscall(39) to get the current PID of the "forked" child process.
I am trying to use the ctypes module to make calls to Windows' Common Item Dialog API. The code shown below is roughly based on the steps outlined in the MSDN documentation. Its only dependency is the comtypes.GUID module.
import ctypes
from ctypes import byref, POINTER, c_int, c_long
from ctypes.wintypes import HWND, HRESULT
from comtypes import GUID
CLSID_FileOpenDialog = '{DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7}'
IID_IFileDialog = '{42F85136-DB7E-439C-85F1-E4075D135FC8}'
#IID_IFileOpenDialog = '{D57C7288-D4AD-4768-BE02-9D969532D960}'
CLSCTX_SERVER = 5
COINIT_APARTMENTTHREADED = 2
FOS_PICKFOLDERS = 32
FOS_FORCEFILESYSTEM = 64
ole32 = ctypes.windll.ole32
CoCreateInstance = ole32.CoCreateInstance
CoInitializeEx = ole32.CoInitializeEx
CoInitializeEx(None, COINIT_APARTMENTTHREADED)
ptr = c_int()
error = CoCreateInstance(
byref(GUID(CLSID_FileOpenDialog)), None, CLSCTX_SERVER,
byref(GUID(IID_IFileDialog)), byref(ptr))
assert error == 0
ptr = ptr.value
c_long_p = ctypes.POINTER(ctypes.c_int)
print('Pointer to COM object: %s' % ptr)
vtable = ctypes.cast(ptr, c_long_p).contents.value
print('Pointer to vtable: %s' % vtable)
func_proto = ctypes.WINFUNCTYPE(HRESULT, HWND)
# Calculating function pointer offset: 3rd entry in vtable; 32-bit => 4 bytes
show_p = ctypes.cast(vtable + 3*4, c_long_p).contents.value
print('Pointer to show(): %s' % show_p)
show = func_proto(show_p)
show(0)
Everything works as intended until the first call to show(0):
WindowsError: exception: access violation reading 0xXXXXXXXX
(Output may vary.) For comparison, I have carried out the same steps in AutoHotkey_L, which has direct access to COM.
CLSID := "{DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7}"
IID := "{42F85136-DB7E-439C-85F1-E4075D135FC8}"
ptr := ComObjCreate(CLSID, IID)
vtable := NumGet(ptr + 0, 0, "Ptr")
show := NumGet(vtbl + 0, 3 * A_PtrSize, "Ptr")
MsgBox ptr: %ptr% vtable: %vtable% show: %A_PtrSize%
DllCall(show, "Ptr", ptr, "Ptr", 44)
The resulting macro pops up an Open File dialog, as expected. The vtable pointer offsets are the same in both cases, but only the Python version throws up an access violation.
Can anyone shed some light on this?
[I apologize for not adding more hyperlinks where appropriate, but as a new user I am limited to two at a time.]
Background:
I am putting together a lightweight module which provides a native save/open file dialog for use in Python scripts. So far I have been unable to find an implementation in pure Python. Those that exist rely on UI toolkits such as Tkinter or wxPython.
Here is the solution:
COM methods take an additional parameter: The 'this' pointer. It is implicit when you call the method from C++, in C (and in ctypes) you must supply it yourself.
Change the line
func_proto = ctypes.WINFUNCTYPE(HRESULT, HWND)
into
func_proto = ctypes.WINFUNCTYPE(HRESULT, c_long, HWND)
and this line
show(0)
into
show(ptr, 0)
and your code will work.