forked from naksyn/PythonMemoryModule
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapiproxy.py
More file actions
122 lines (101 loc) · 4.48 KB
/
apiproxy.py
File metadata and controls
122 lines (101 loc) · 4.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import ctypes
import functools
import windows.generated_def as gdef
from .error import ExportNotFound
from windows.pycompat import is_py3
# Utils
def is_implemented(apiproxy):
"""Return :obj:`True` if DLL/Api can be found"""
try:
apiproxy.force_resolution()
except ExportNotFound:
return False
return True
def get_target(apiproxy):
"""POC for newshook"""
return apiproxy.target_dll, apiproxy.target_func
def resolve(apiproxy):
"""Resolve the address of ``apiproxy``. Might raise if ``apiproxy`` is not implemented"""
apiproxy.force_resolution()
func = ctypes.WinDLL(apiproxy.target_dll)[apiproxy.target_func]
return ctypes.cast(func, gdef.PVOID).value
class NeededParameterType(object):
_inst = None
def __new__(cls):
if cls._inst is None:
cls._inst = super(NeededParameterType, cls).__new__(cls)
return cls._inst
def __repr__(self):
return "NeededParameter"
NeededParameter = NeededParameterType()
sentinel = object()
class ApiProxy(object):
APIDLL = None
"""Create a python wrapper around a kernel32 function"""
def __init__(self, func_name=None, error_check=sentinel, deffunc_module=None):
self.deffunc_module = deffunc_module if deffunc_module is not None else gdef.winfuncs
self.func_name = func_name
if error_check is sentinel:
error_check = self.default_error_check
self.error_check = error_check
self._cprototyped = None
def __call__(self, python_proxy):
# Use the name of the sub-function if None was given
if self.func_name is None:
self.func_name = python_proxy.__name__
errchk = None
if self.error_check is not None:
errchk = functools.wraps(self.error_check)(functools.partial(self.error_check, self.func_name))
prototype = getattr(self.deffunc_module, self.func_name + "Prototype")
params = getattr(self.deffunc_module, self.func_name + "Params")
python_proxy.prototype = prototype
python_proxy.params = params
python_proxy.errcheck = errchk
python_proxy.target_dll = self.APIDLL
python_proxy.target_func = self.func_name
# Give access to the 'ApiProxy' object from the function
python_proxy.proxy = self
params_name = [param[1] for param in params]
if (self.error_check.__doc__):
doc = python_proxy.__doc__
doc = doc if doc else ""
python_proxy.__doc__ = doc + "\nErrcheck:\n " + self.error_check.__doc__
def generate_ctypes_function():
try:
api_dll = ctypes.windll[self.APIDLL]
except WindowsError as e:
if e.winerror == gdef.ERROR_BAD_EXE_FORMAT:
e.strerror = e.strerror.replace("%1", "<{0}>".format(self.APIDLL))
raise
try:
c_prototyped = prototype((self.func_name, api_dll), params)
except (AttributeError, WindowsError):
raise ExportNotFound(self.func_name, self.APIDLL)
if errchk is not None:
c_prototyped.errcheck = errchk
self._cprototyped = c_prototyped
def perform_call(*args):
if self._cprototyped is None:
generate_ctypes_function()
try:
return self._cprototyped(*args)
except ctypes.ArgumentError as e:
# We just add a conversion ctypes argument fail
# We can do some heavy computation if needed
# Not a case that normally happen
# "argument 2: <type 'exceptions.TypeError'>: wrong type"
# Thx ctypes..
argnbstr, ecx, reason = e.args[0].split(":") # py2 / py3 compat :)
if not argnbstr.startswith("argument "):
raise # Don't knnow if it can happen
argnb = int(argnbstr[len("argument "):])
badarg = args[argnb - 1]
if badarg is NeededParameter:
badargname = params_name[argnb - 1]
raise TypeError("{0}: Missing Mandatory parameter <{1}>".format(self.func_name, badargname))
# Not NeededParameter: the caller need to fix the used param :)
# raise the real ctypes error
raise
setattr(python_proxy, "ctypes_function", perform_call)
setattr(python_proxy, "force_resolution", generate_ctypes_function)
return python_proxy