Files
naksyn-PythonMemoryModule/pythonmemorymodule/windows/winproxy/apiproxy.py
T
naksyn db1893910c command line support (partial) via PEB stomping
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.
2023-07-27 06:44:29 -07:00

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