Merge pull request #6 from cosine0/dev

Merge dev
This commit is contained in:
Bumsu Hyeon 2022-10-27 10:09:32 +09:00 committed by GitHub
commit 26f7006794
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 42 deletions

View File

@ -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
===

View File

@ -8,7 +8,6 @@ import re
import struct
import time
import types
from typing import Union
import capstone
import cffi
@ -36,16 +35,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 +104,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
@ -135,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))
@ -162,67 +164,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

View File

@ -4,9 +4,10 @@ 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))
MAIN_DIR = Path(inspect.getframeinfo(inspect.currentframe()).filename).parent
INTERPRETER_DIR = Path(sys.executable).parent
injected_script = '''
import time
@ -33,23 +34,36 @@ 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 = os.path.join(os.path.dirname(INTERPRETER_DIR), 'lib', 'site-packages')
venv_setup = 'import sys\nsys.path.append({!r})'.format(library_path)
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:
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 = 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)

View File

@ -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",