mirror of
https://github.com/naksyn/PythonMemoryModule
synced 2026-06-06 16:24:25 +00:00
db1893910c
This update include support to passing command line parameters to unmanaged exe via PEB stomping. This technique is not working with every executable since it depends on which functions are used to pass arguments. Generally, to get a universally working technique would be required to hook GetCommandlineA GetCommandlineW __getmainargs and __wgetmainargs since PEB stomping won't cover all cases, more details here: https://blog-30cm-tw.translate.goog/2020/08/windows-c-mainargc-argv.html?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=it&_x_tr_pto=wapp However, during my testing I found that mimikatz and several go binaries are working just by doing PEB stomping. On the other hand, cmdline passing via PEB stomping alone to mingw and VS compiled binaries won't likely work.
123 lines
4.5 KiB
Python
123 lines
4.5 KiB
Python
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
|