From 153e92426f234ad79b8fc3377a5fe5f2d240857d Mon Sep 17 00:00:00 2001 From: cosine0 Date: Thu, 27 Oct 2022 09:40:49 +0900 Subject: [PATCH 1/4] apply python 3.6+ style --- dllhook/hooker.py | 57 ++++++++++++++++++++++++++--------------------- dllhook/main.py | 11 +++++---- setup.py | 2 +- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/dllhook/hooker.py b/dllhook/hooker.py index fde0577..917b8da 100644 --- a/dllhook/hooker.py +++ b/dllhook/hooker.py @@ -36,16 +36,16 @@ def error_string(errno): ) if buffer_pointer.value is None: - raise ValueError('Failed to get message for error number {}'.format(errno)) + raise ValueError(f'Failed to get message for error number {errno}') as_string = ctypes.wstring_at(buffer_pointer) kernel32.LocalFree(buffer_pointer) - return '({:#x}) {}'.format(errno, as_string) + return f'({errno:#x}) {as_string}' def get_last_error_string(): errno = kernel32.GetLastError() - as_astring = error_string(errno) - return as_astring + as_string = error_string(errno) + return as_string def read_memory(address, size): @@ -105,9 +105,12 @@ def hook_dll(module_name, target_export_name_or_offset, timeout_seconds=5): time.sleep(0.1) continue else: - raise ValueError('Unable to find module: {!r}: {}'.format(module_name, error_string(errno))) + raise ValueError('Unable to find module: ' + f'{module_name!r}: ' + f'{error_string(errno)}') else: - raise ValueError('Unable to find module: {!r}: {}'.format(module_name, error_string(errno))) + raise ValueError(f'Unable to find module: {module_name!r}: ' + f'{error_string(errno)}') else: break @@ -162,67 +165,69 @@ def hook_dll(module_name, target_export_name_or_offset, timeout_seconds=5): invoker_c_asm.append('push [ebp]') elif arg_name in fetchable_general_registers: # arg is another register! - invoker_c_asm.append('push {}'.format(arg_name)) + invoker_c_asm.append(f'push {arg_name}') else: # arg is a standard argument! arg_order -= 1 - invoker_c_asm.append('push [ebp+{}]'.format(4 * arg_order + 8)) + invoker_c_asm.append(f'push [ebp+{4 * arg_order + 8}]') + invoker_c_asm = '\n'.join(invoker_c_asm) target_head_long = read_memory(target_address, 19) cs = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_32) original_asm = [] total_length = 0 for address, size, mnemonic, op_str in cs.disasm_lite(target_head_long, target_address): - disasm = '{} {}'.format(mnemonic, op_str) - disasm = re.sub(r'([^:])(\[0x[0-9a-fA-F]+\])', r'\1 ds:\2', disasm) + disasm = f'{mnemonic} {op_str}' + disasm = re.sub(r'([^:])(\[0x[0-9a-fA-F]+])', r'\1 ds:\2', disasm) original_asm.append(disasm) total_length = address + size - target_address if total_length >= 5: break + original_asm = '\n'.join(original_asm) target_head = target_head_long[:5] decorator_scope_vars['target_head'] = target_head - invoker_c_code = ''' -__declspec(naked) void invoker_{count}() + invoker_c_code = f''' +__declspec(naked) void invoker_{invoker_count}() {{ __asm {{ push ebp mov ebp, esp pushad - {before_call} - mov eax, {callbacker:#x} + {invoker_c_asm} + mov eax, {callbacker_c_address:#x} call eax - add esp, {arg_depth:#x} + add esp, {4 * len(argspec.args):#x} popad pop ebp {original_asm} - push {target_resume:#x} + push {target_address + total_length:#x} ret }} }} -int invoker_{count}_addr() +int invoker_{invoker_count}_addr() {{ - return (int)invoker_{count}; + return (int)invoker_{invoker_count}; }} -'''.format(count=invoker_count, before_call='\n'.join(invoker_c_asm), callbacker=callbacker_c_address, - arg_depth=4 * len(argspec.args), original_asm='\n'.join(original_asm), - target_resume=target_address + total_length) +''' # allocate callbacker_invoker_native using cffi ffi = cffi.FFI() - ffi.cdef('void invoker_{count}();int invoker_{count}_addr();'.format(count=invoker_count)) + ffi.cdef(f'void invoker_{invoker_count}();' + f'int invoker_{invoker_count}_addr();') invoker_lib = ffi.verify(invoker_c_code) - invoker_address = getattr(invoker_lib, 'invoker_{count}_addr'.format(count=invoker_count))() + invoker_address = getattr(invoker_lib, + f'invoker_{invoker_count}_addr')() decorator_scope_vars['invoker_address'] = invoker_address # install jumper to callbacker_invoker_native on target install_jump(target_address, invoker_address) invoker_count += 1 - print('target: {:#x}'.format(target_address)) - print('invoke: {:#x}'.format(invoker_address)) - print('callbacker: {:#x}'.format(callbacker_c_address)) + print(f'target: {target_address:#x}') + print(f'invoke: {invoker_address:#x}') + print(f'callbacker: {callbacker_c_address:#x}') return callback # return original function, not wrapped return decorator diff --git a/dllhook/main.py b/dllhook/main.py index 05e1848..494fedf 100644 --- a/dllhook/main.py +++ b/dllhook/main.py @@ -4,6 +4,7 @@ import os import subprocess import sys import tempfile +from pathlib import Path MAIN_DIR = os.path.dirname(os.path.abspath(inspect.getframeinfo(inspect.currentframe()).filename)) INTERPRETER_DIR = os.path.dirname(os.path.abspath(sys.executable)) @@ -39,8 +40,9 @@ def main(): if os.path.basename(INTERPRETER_DIR).lower() == 'scripts': # in venv - library_path = os.path.join(os.path.dirname(INTERPRETER_DIR), 'lib', 'site-packages') - venv_setup = 'import sys\nsys.path.append({!r})'.format(library_path) + library_path = Path(INTERPRETER_DIR) / 'lib' / 'site-packages' + venv_setup = f'import sys\nsys.path.append(' \ + f'{library_path.absolute().as_posix()!r})' else: venv_setup = '' @@ -50,6 +52,7 @@ def main(): venv_setup=venv_setup).encode('utf_8') f.write(formatted_script) - injector_path = os.path.join(MAIN_DIR, 'mayhem', 'tools', 'python_injector.py') - subprocess.Popen([sys.executable, injector_path, f.name, str(pid)], env=envs).wait() + injector_path = Path(MAIN_DIR) / 'mayhem' / 'tools' / 'python_injector.py' + subprocess.Popen([sys.executable, injector_path, f.name, str(pid)], + env=envs).wait() os.unlink(f.name) diff --git a/setup.py b/setup.py index e104c17..2772232 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ setup( version=version, description=package_description, packages=packages, - package_data={'dllhook': package_files(os.path.join('dllhook', 'mayhem'))}, + package_data={'dllhook': package_files('dllhook/mayhem')}, license="GPLv3", author="cosine0 at github", author_email="ksitht@gmail.com", From af77d3791232c55cc49ac75e9edf897c9c6ba4dc Mon Sep 17 00:00:00 2001 From: cosine0 Date: Thu, 27 Oct 2022 09:59:28 +0900 Subject: [PATCH 2/4] fix python dll finding mechanism for venvs --- dllhook/main.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/dllhook/main.py b/dllhook/main.py index 494fedf..dc4229d 100644 --- a/dllhook/main.py +++ b/dllhook/main.py @@ -6,8 +6,8 @@ import sys import tempfile from pathlib import Path -MAIN_DIR = os.path.dirname(os.path.abspath(inspect.getframeinfo(inspect.currentframe()).filename)) -INTERPRETER_DIR = os.path.dirname(os.path.abspath(sys.executable)) +MAIN_DIR = Path(inspect.getframeinfo(inspect.currentframe()).filename).parent +INTERPRETER_DIR = Path(sys.executable).parent injected_script = ''' import time @@ -34,22 +34,33 @@ def main(): envs = os.environ.copy() if 'PATH' in os.environ: - envs['PATH'] = os.path.pathsep.join([r'C:\windows\syswow64', INTERPRETER_DIR, os.environ['PATH']]) + envs['PATH'] = os.path.pathsep.join([ + r'C:\windows\syswow64', + INTERPRETER_DIR.absolute().as_posix(), + os.environ['PATH']]) else: - envs['PATH'] = INTERPRETER_DIR + envs['PATH'] = INTERPRETER_DIR.absolute().as_posix() if os.path.basename(INTERPRETER_DIR).lower() == 'scripts': # in venv - library_path = Path(INTERPRETER_DIR) / 'lib' / 'site-packages' + library_path = INTERPRETER_DIR / 'lib' / 'site-packages' venv_setup = f'import sys\nsys.path.append(' \ f'{library_path.absolute().as_posix()!r})' + with open(INTERPRETER_DIR.parent / 'pyvenv.cfg') as f: + for line in f: + if line.startswith('base-prefix'): + envs['PATH'] = os.path.pathsep.join([ + line.split('=')[1].strip(), + envs['PATH']]) + break else: venv_setup = '' with tempfile.NamedTemporaryFile(suffix='.py', delete=False) as f: - formatted_script = injected_script.format(working_dir=os.path.abspath(os.curdir), - script_path=script_path, - venv_setup=venv_setup).encode('utf_8') + formatted_script = injected_script.format( + working_dir=os.path.abspath(os.curdir), + script_path=script_path, + venv_setup=venv_setup).encode('utf_8') f.write(formatted_script) injector_path = Path(MAIN_DIR) / 'mayhem' / 'tools' / 'python_injector.py' From 264b56f4f3f40de3c915baaeeef78ceb90d11549 Mon Sep 17 00:00:00 2001 From: cosine0 Date: Thu, 27 Oct 2022 10:07:30 +0900 Subject: [PATCH 3/4] fix/update old api use --- dllhook/hooker.py | 5 ++--- dllhook/main.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/dllhook/hooker.py b/dllhook/hooker.py index 917b8da..8b94dfd 100644 --- a/dllhook/hooker.py +++ b/dllhook/hooker.py @@ -8,7 +8,6 @@ import re import struct import time import types -from typing import Union import capstone import cffi @@ -138,8 +137,8 @@ def hook_dll(module_name, target_export_name_or_offset, timeout_seconds=5): install_jump(decorator_scope_vars['target_address'], decorator_scope_vars['invoker_address']) # make c wrapper for callbacker - argspec = inspect.getargspec(callback) - if argspec.varargs is not None or argspec.keywords is not None: + argspec = inspect.getfullargspec(callback) + if argspec.varargs is not None or argspec.varkw is not None: raise ValueError("Varargs are not allowed in 'callback'") callbacker_c_wrapper = ctypes.CFUNCTYPE(None, *[ctypes.c_uint32] * len(argspec.args)) diff --git a/dllhook/main.py b/dllhook/main.py index dc4229d..3d23523 100644 --- a/dllhook/main.py +++ b/dllhook/main.py @@ -43,7 +43,7 @@ def main(): if os.path.basename(INTERPRETER_DIR).lower() == 'scripts': # in venv - library_path = INTERPRETER_DIR / 'lib' / 'site-packages' + library_path = INTERPRETER_DIR.parent / 'lib' / 'site-packages' venv_setup = f'import sys\nsys.path.append(' \ f'{library_path.absolute().as_posix()!r})' with open(INTERPRETER_DIR.parent / 'pyvenv.cfg') as f: From 2999287e159f9684e7ed1164a528e891f54aba53 Mon Sep 17 00:00:00 2001 From: cosine0 Date: Thu, 27 Oct 2022 10:08:53 +0900 Subject: [PATCH 4/4] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f55653e..9aba32f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ to the application and executes your script. And also this provides a convenient This tool uses `mayhem python_injector` as injector. See https://github.com/zeroSteiner/mayhem/blob/master/tools/python_injector.py When you clone this repository, you must also clone submodule `mayhem`. -This tool is tested on Python 3.6-3.9. +This tool is tested on Python 3.6-3.11. Installation ===