mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
execute_python doest not reinject the shellcode everytime + test hook 64b
This commit is contained in:
@@ -10,4 +10,5 @@ Since 0.2:
|
||||
* registry.py handle REG_QWORD manually (_winreg does not)
|
||||
* CurrentProcessReadSyswow doest not use ``current_process.handle`` anymore but ``OpenProcess(current_process.pid)`` (Compat windows10 where method 1 doest not work)
|
||||
* Add: system.handles (winobject\handle.py)
|
||||
* You can have multiple execute_python_unsafe at the same time in the same process (didn't know: consequence of new injection code)
|
||||
* You can have multiple execute_python_unsafe at the same time in the same process (didn't know: consequence of new injection code)
|
||||
* WinProcess.execute_python does not regenerate/reinject the python_exec_shellcode for each execution
|
||||
@@ -16,12 +16,14 @@ TODO:
|
||||
- Quid IAT hook stub ? just einit threads and remove this ?
|
||||
- Continue test with new generate_callback_stub and remove commented code if it works
|
||||
|
||||
- rewrite/REMOVE? generate_stub_64[32] : it's a non-sens to not save stuff on the stack..
|
||||
I can re-copy the args on stack..
|
||||
|
||||
- Injection
|
||||
- code generated by generate_python_exec_shellcode_64[32] may be reused
|
||||
Just need to pass the address of the python string as argument
|
||||
|
||||
- rewrite generate_stub_64[32] : it's a non-sens to not save stuff on the stack..
|
||||
I can re-copy the args on stack..
|
||||
|
||||
|
||||
- Winproxy:
|
||||
- rethink OptionalExport ? not useful with lazy resolution (or we need to force resolution..)
|
||||
@@ -44,15 +46,11 @@ TODO:
|
||||
- Some test/doc on windows.system.handles
|
||||
|
||||
|
||||
CHANGELOG:
|
||||
* re-check every sample
|
||||
|
||||
Documentation
|
||||
* verif samples
|
||||
|
||||
FIXME:
|
||||
- setup.py build seems to raise an error
|
||||
- winutils.create_process : use WinProcess._from_handle
|
||||
- Push("[ECX]") in simple_x64 as a "H" rex and i think it should not..
|
||||
|
||||
RESSOURCE
|
||||
|
||||
+18
-12
@@ -122,7 +122,7 @@ def load_dll_in_remote_process(target, dll_name):
|
||||
|
||||
python_function_32_bits = {}
|
||||
# 32 to 32 injection
|
||||
def generate_python_exec_shellcode_32(target, PYCODE_ADDR, PyDll):
|
||||
def generate_python_exec_shellcode_32(target, PyDll):
|
||||
if not python_function_32_bits:
|
||||
pymodule = [mod for mod in target.peb.modules if mod.name == PyDll][0]
|
||||
Py_exports = pymodule.pe.exports
|
||||
@@ -158,7 +158,9 @@ def generate_python_exec_shellcode_32(target, PYCODE_ADDR, PyDll):
|
||||
code += x86.Mov('EAX', PyGILState_Ensure)
|
||||
code += x86.Call('EAX')
|
||||
code += x86.Push('EAX')
|
||||
code += x86.Push(PYCODE_ADDR)
|
||||
# Get the string to execute from parameters
|
||||
code += x86.Mov("EAX", x86.mem("[ESP + 0x8]"))
|
||||
code += x86.Push('EAX')
|
||||
code += x86.Mov('EAX', PyRun_SimpleString)
|
||||
code += x86.Call('EAX')
|
||||
code += x86.Mov("ESI", "EAX")
|
||||
@@ -180,7 +182,7 @@ def generate_python_exec_shellcode_32(target, PYCODE_ADDR, PyDll):
|
||||
|
||||
python_function_64_bits = {}
|
||||
# 64 to 64 injection
|
||||
def generate_python_exec_shellcode_64(target, PYCODE_ADDR, PyDll):
|
||||
def generate_python_exec_shellcode_64(target, PyDll):
|
||||
if not python_function_64_bits:
|
||||
pymodule = [mod for mod in target.peb.modules if mod.name == PyDll][0]
|
||||
Py_exports = pymodule.pe.exports
|
||||
@@ -219,8 +221,8 @@ def generate_python_exec_shellcode_64(target, PYCODE_ADDR, PyDll):
|
||||
code += x64.Mov('RAX', PyGILState_Ensure)
|
||||
code += x64.Call('RAX')
|
||||
code += x64.Mov('R15', 'RAX')
|
||||
code += x64.Mov("RCX", x64.mem("[RSP + 0x20]"))
|
||||
code += x64.Mov('RAX', PyRun_SimpleString)
|
||||
code += x64.Mov('RCX', PYCODE_ADDR)
|
||||
code += x64.Call('RAX')
|
||||
code += x64.Mov('RCX', 'R15')
|
||||
code += x64.Mov('R15', 'RAX')
|
||||
@@ -245,18 +247,22 @@ def inject_python_command(target, code_injected, PYDLL):
|
||||
"""Postulate: PYDLL is already loaded in target process"""
|
||||
PYCODE = code_injected + "\x00"
|
||||
# TODO: free this (how ? when ?)
|
||||
remote_addr = target.virtual_alloc(len(PYCODE) + 0x100)
|
||||
target.write_memory(remote_addr, PYCODE)
|
||||
SHELLCODE_ADDR = remote_addr + len(PYCODE)
|
||||
remote_python_code_addr = target.virtual_alloc(len(PYCODE))
|
||||
target.write_memory(remote_python_code_addr, PYCODE)
|
||||
shellcode_addr = getattr(target, "_execute_python_shellcode", None)
|
||||
if shellcode_addr is not None:
|
||||
return shellcode_addr, remote_python_code_addr
|
||||
|
||||
if target.bitness == 32:
|
||||
shellcode_generator = generate_python_exec_shellcode_32
|
||||
else:
|
||||
shellcode_generator = generate_python_exec_shellcode_64
|
||||
|
||||
shellcode = shellcode_generator(target, remote_addr, PYDLL)
|
||||
target.write_memory(SHELLCODE_ADDR, shellcode)
|
||||
return SHELLCODE_ADDR
|
||||
shellcode = shellcode_generator(target, PYDLL)
|
||||
shellcode_addr = target.virtual_alloc(len(shellcode))
|
||||
target.write_memory(shellcode_addr, shellcode)
|
||||
target._execute_python_shellcode = shellcode_addr
|
||||
return shellcode_addr, remote_python_code_addr
|
||||
|
||||
|
||||
def validate_python_dll_presence_on_disk(process):
|
||||
@@ -276,8 +282,8 @@ def validate_python_dll_presence_on_disk(process):
|
||||
def execute_python_code(process, code):
|
||||
validate_python_dll_presence_on_disk(process)
|
||||
load_dll_in_remote_process(process, "python27.dll")
|
||||
addr = inject_python_command(process, code, "python27.dll")
|
||||
t = process.create_thread(addr, 0)
|
||||
shellcode, pythoncode = inject_python_command(process, code, "python27.dll")
|
||||
t = process.create_thread(shellcode, pythoncode)
|
||||
return t
|
||||
|
||||
|
||||
|
||||
@@ -101,171 +101,171 @@ class CustomAllocator(object):
|
||||
allocator = CustomAllocator()
|
||||
|
||||
|
||||
def get_functions():
|
||||
version = sys.version_info
|
||||
python_dll = "python" + str(version.major) + str(version.minor)
|
||||
|
||||
PyGILState_Ensure = windows.utils.get_func_addr(python_dll, 'PyGILState_Ensure'.encode())
|
||||
PyObject_CallObject = windows.utils.get_func_addr(python_dll, 'PyObject_CallObject'.encode())
|
||||
PyGILState_Release = windows.utils.get_func_addr(python_dll, 'PyGILState_Release'.encode())
|
||||
return [PyGILState_Ensure, PyObject_CallObject, PyGILState_Release]
|
||||
|
||||
|
||||
def analyse_callback(callback):
|
||||
if not callable(callback):
|
||||
raise ValueError("Need a callable object :)")
|
||||
obj_id = id(callback)
|
||||
if not hasattr(callback, '_objects'):
|
||||
raise ValueError("Need a ctypes PyCFuncPtr")
|
||||
return obj_id
|
||||
|
||||
|
||||
# For windows 32 bits with stdcall
|
||||
def generate_stub_32(callback):
|
||||
c_callback = ctypes.cast(callback, ctypes.c_void_p).value
|
||||
|
||||
gstate_save_addr = x86.create_displacement(disp=allocator.reserve_int())
|
||||
return_addr_save_addr = x86.create_displacement(disp=allocator.reserve_int())
|
||||
save_ebx = x86.create_displacement(disp=allocator.reserve_int())
|
||||
save_ecx = x86.create_displacement(disp=allocator.reserve_int())
|
||||
save_edx = x86.create_displacement(disp=allocator.reserve_int())
|
||||
save_esi = x86.create_displacement(disp=allocator.reserve_int())
|
||||
save_edi = x86.create_displacement(disp=allocator.reserve_int())
|
||||
|
||||
ensure, objcall, release = get_functions()
|
||||
|
||||
code = x86.MultipleInstr()
|
||||
# ## Shellcode ## #
|
||||
code += x86.Mov(save_ebx, 'EBX')
|
||||
code += x86.Mov(save_ecx, 'ECX')
|
||||
code += x86.Mov(save_edx, 'EDX')
|
||||
code += x86.Mov(save_esi, 'ESI')
|
||||
code += x86.Mov(save_edi, 'EDI')
|
||||
|
||||
code += x86.Mov('EAX', ensure)
|
||||
code += x86.Call('EAX')
|
||||
code += x86.Mov(gstate_save_addr, 'EAX')
|
||||
|
||||
# Save real return addr (for good argument parsing by the callback)
|
||||
code += x86.Pop('EAX')
|
||||
code += x86.Mov(return_addr_save_addr, 'EAX')
|
||||
|
||||
code += x86.Mov('EAX', c_callback)
|
||||
code += x86.Call('EAX')
|
||||
|
||||
# Restore real return value
|
||||
code += x86.Mov('EBX', return_addr_save_addr)
|
||||
code += x86.Push('EBX')
|
||||
|
||||
# Save return value
|
||||
code += x86.Push('EAX')
|
||||
code += x86.Mov('EBX', gstate_save_addr)
|
||||
code += x86.Push('EBX')
|
||||
|
||||
code += x86.Mov('EAX', release)
|
||||
code += x86.Call('EAX')
|
||||
|
||||
# Discard `release` argument
|
||||
code += x86.Pop('EAX')
|
||||
# Restore return value
|
||||
code += x86.Pop('EAX')
|
||||
code += x86.Mov('EBX', save_ebx)
|
||||
code += x86.Mov('ECX', save_ecx)
|
||||
code += x86.Mov('EDX', save_edx)
|
||||
code += x86.Mov('ESI', save_esi)
|
||||
code += x86.Mov('EDI', save_edi)
|
||||
code += x86.Ret()
|
||||
return code
|
||||
|
||||
|
||||
def generate_stub_64(callback):
|
||||
c_callback = ctypes.cast(callback, ctypes.c_void_p).value
|
||||
REG_LEN = ctypes.sizeof(ctypes.c_void_p)
|
||||
register_to_save = ("RBX", "RCX", "RDX", "RSI", "RDI", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15")
|
||||
|
||||
push_all_save_register = x64.MultipleInstr([x64.Push(reg) for reg in register_to_save])
|
||||
pop_all_save_register = x64.MultipleInstr([x64.Pop(reg) for reg in reversed(register_to_save)])
|
||||
# Reserve parallel `stack`
|
||||
save_register_space = allocator.reserve_int(len(register_to_save))
|
||||
save_register_space_end = save_register_space + (ctypes.sizeof(ctypes.c_void_p) * (len(register_to_save)))
|
||||
|
||||
save_rbx = save_register_space_end - REG_LEN
|
||||
save_rbx # Fuck the linter :D
|
||||
save_rcx = save_register_space_end - REG_LEN - REG_LEN
|
||||
save_rdx = save_register_space_end - REG_LEN - (REG_LEN * 2)
|
||||
save_rsi = save_register_space_end - REG_LEN - (REG_LEN * 3)
|
||||
save_rsi # Fuck the linter :D
|
||||
save_rdi = save_register_space_end - REG_LEN - (REG_LEN * 4)
|
||||
save_rdi # Fuck the linter :D
|
||||
save_r8 = save_register_space_end - REG_LEN - (REG_LEN * 5)
|
||||
save_r9 = save_register_space_end - REG_LEN - (REG_LEN * 6)
|
||||
|
||||
gstate_save_addr = x64.create_displacement(disp=allocator.reserve_int())
|
||||
return_addr_save_addr = x64.create_displacement(disp=allocator.reserve_int())
|
||||
return_value_save_addr = x64.create_displacement(disp=allocator.reserve_int())
|
||||
|
||||
Reserve_space_for_call = x64.MultipleInstr([x64.Push('RDI')] * 4)
|
||||
Clean_space_for_call = x64.MultipleInstr([x64.Pop('RDI')] * 4)
|
||||
Do_stack_alignement = x64.MultipleInstr([x64.Push('RDI')] * 1)
|
||||
Remove_stack_alignement = x64.MultipleInstr([x64.Pop('RDI')] * 1)
|
||||
|
||||
ensure, objcall, release = get_functions()
|
||||
|
||||
# ## Shellcode ## #
|
||||
code = x64.MultipleInstr()
|
||||
# Save all registers
|
||||
code += x64.Mov('RAX', save_register_space_end)
|
||||
code += x64.Xchg('RAX', 'RSP')
|
||||
code += push_all_save_register
|
||||
code += x64.Xchg('RAX', 'RSP')
|
||||
# GOOO
|
||||
code += x64.Mov('RAX', ensure)
|
||||
code += Reserve_space_for_call
|
||||
code += Do_stack_alignement
|
||||
code += x64.Call('RAX')
|
||||
code += Remove_stack_alignement
|
||||
code += Clean_space_for_call
|
||||
code += x64.Mov(gstate_save_addr, 'RAX')
|
||||
# Save real return addr (for good argument parsing by the callback)
|
||||
code += x64.Pop('RAX')
|
||||
code += x64.Mov(return_addr_save_addr, 'RAX')
|
||||
# Restore parameters for real function call
|
||||
code += x64.Mov('RAX', save_rcx)
|
||||
code += x64.Mov('RCX', x64.mem('[RAX]'))
|
||||
code += x64.Mov('RAX', save_rdx)
|
||||
code += x64.Mov('RDX', x64.mem('[RAX]'))
|
||||
code += x64.Mov('RAX', save_r9)
|
||||
code += x64.Mov('R9', x64.mem('[RAX]'))
|
||||
code += x64.Mov('RAX', save_r8)
|
||||
code += x64.Mov('R8', x64.mem('[RAX]'))
|
||||
# Call python code
|
||||
code += x64.Mov('RAX', c_callback)
|
||||
# no need for stack alignement here as we poped the return addr
|
||||
# no need for Reserve_space_for_call as we must use the previous one for correct argument parsing
|
||||
code += x64.Call('RAX')
|
||||
# Save return value
|
||||
code += x64.Mov(return_value_save_addr, 'RAX')
|
||||
# Repush real return value
|
||||
code += x64.Mov('RAX', return_addr_save_addr)
|
||||
code += x64.Push('RAX')
|
||||
# Call release(gstate_save)
|
||||
code += x64.Mov('RAX', gstate_save_addr)
|
||||
code += x64.Mov('RCX', 'RAX')
|
||||
code += x64.Mov('RAX', release)
|
||||
code += Reserve_space_for_call
|
||||
code += Do_stack_alignement
|
||||
code += x64.Call('RAX')
|
||||
code += Remove_stack_alignement
|
||||
code += Clean_space_for_call
|
||||
# Restore registers
|
||||
code += x64.Mov('RAX', save_register_space)
|
||||
code += x64.Xchg('RAX', 'RSP')
|
||||
code += pop_all_save_register
|
||||
code += x64.Xchg('RAX', 'RSP')
|
||||
# Restore return value
|
||||
code += x64.Mov('RAX', return_value_save_addr)
|
||||
code += x64.Ret()
|
||||
return code
|
||||
#def get_functions():
|
||||
# version = sys.version_info
|
||||
# python_dll = "python" + str(version.major) + str(version.minor)
|
||||
#
|
||||
# PyGILState_Ensure = windows.utils.get_func_addr(python_dll, 'PyGILState_Ensure'.encode())
|
||||
# PyObject_CallObject = windows.utils.get_func_addr(python_dll, 'PyObject_CallObject'.encode())
|
||||
# PyGILState_Release = windows.utils.get_func_addr(python_dll, 'PyGILState_Release'.encode())
|
||||
# return [PyGILState_Ensure, PyObject_CallObject, PyGILState_Release]
|
||||
#
|
||||
#
|
||||
#def analyse_callback(callback):
|
||||
# if not callable(callback):
|
||||
# raise ValueError("Need a callable object :)")
|
||||
# obj_id = id(callback)
|
||||
# if not hasattr(callback, '_objects'):
|
||||
# raise ValueError("Need a ctypes PyCFuncPtr")
|
||||
# return obj_id
|
||||
#
|
||||
#
|
||||
## For windows 32 bits with stdcall
|
||||
#def generate_stub_32(callback):
|
||||
# c_callback = ctypes.cast(callback, ctypes.c_void_p).value
|
||||
#
|
||||
# gstate_save_addr = x86.create_displacement(disp=allocator.reserve_int())
|
||||
# return_addr_save_addr = x86.create_displacement(disp=allocator.reserve_int())
|
||||
# save_ebx = x86.create_displacement(disp=allocator.reserve_int())
|
||||
# save_ecx = x86.create_displacement(disp=allocator.reserve_int())
|
||||
# save_edx = x86.create_displacement(disp=allocator.reserve_int())
|
||||
# save_esi = x86.create_displacement(disp=allocator.reserve_int())
|
||||
# save_edi = x86.create_displacement(disp=allocator.reserve_int())
|
||||
#
|
||||
# ensure, objcall, release = get_functions()
|
||||
#
|
||||
# code = x86.MultipleInstr()
|
||||
# # ## Shellcode ## #
|
||||
# code += x86.Mov(save_ebx, 'EBX')
|
||||
# code += x86.Mov(save_ecx, 'ECX')
|
||||
# code += x86.Mov(save_edx, 'EDX')
|
||||
# code += x86.Mov(save_esi, 'ESI')
|
||||
# code += x86.Mov(save_edi, 'EDI')
|
||||
#
|
||||
# code += x86.Mov('EAX', ensure)
|
||||
# code += x86.Call('EAX')
|
||||
# code += x86.Mov(gstate_save_addr, 'EAX')
|
||||
#
|
||||
# # Save real return addr (for good argument parsing by the callback)
|
||||
# code += x86.Pop('EAX')
|
||||
# code += x86.Mov(return_addr_save_addr, 'EAX')
|
||||
#
|
||||
# code += x86.Mov('EAX', c_callback)
|
||||
# code += x86.Call('EAX')
|
||||
#
|
||||
# # Restore real return value
|
||||
# code += x86.Mov('EBX', return_addr_save_addr)
|
||||
# code += x86.Push('EBX')
|
||||
#
|
||||
# # Save return value
|
||||
# code += x86.Push('EAX')
|
||||
# code += x86.Mov('EBX', gstate_save_addr)
|
||||
# code += x86.Push('EBX')
|
||||
#
|
||||
# code += x86.Mov('EAX', release)
|
||||
# code += x86.Call('EAX')
|
||||
#
|
||||
# # Discard `release` argument
|
||||
# code += x86.Pop('EAX')
|
||||
# # Restore return value
|
||||
# code += x86.Pop('EAX')
|
||||
# code += x86.Mov('EBX', save_ebx)
|
||||
# code += x86.Mov('ECX', save_ecx)
|
||||
# code += x86.Mov('EDX', save_edx)
|
||||
# code += x86.Mov('ESI', save_esi)
|
||||
# code += x86.Mov('EDI', save_edi)
|
||||
# code += x86.Ret()
|
||||
# return code
|
||||
#
|
||||
#
|
||||
#def generate_stub_64(callback):
|
||||
# c_callback = ctypes.cast(callback, ctypes.c_void_p).value
|
||||
# REG_LEN = ctypes.sizeof(ctypes.c_void_p)
|
||||
# register_to_save = ("RBX", "RCX", "RDX", "RSI", "RDI", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15")
|
||||
#
|
||||
# push_all_save_register = x64.MultipleInstr([x64.Push(reg) for reg in register_to_save])
|
||||
# pop_all_save_register = x64.MultipleInstr([x64.Pop(reg) for reg in reversed(register_to_save)])
|
||||
# # Reserve parallel `stack`
|
||||
# save_register_space = allocator.reserve_int(len(register_to_save))
|
||||
# save_register_space_end = save_register_space + (ctypes.sizeof(ctypes.c_void_p) * (len(register_to_save)))
|
||||
#
|
||||
# save_rbx = save_register_space_end - REG_LEN
|
||||
# save_rbx # Fuck the linter :D
|
||||
# save_rcx = save_register_space_end - REG_LEN - REG_LEN
|
||||
# save_rdx = save_register_space_end - REG_LEN - (REG_LEN * 2)
|
||||
# save_rsi = save_register_space_end - REG_LEN - (REG_LEN * 3)
|
||||
# save_rsi # Fuck the linter :D
|
||||
# save_rdi = save_register_space_end - REG_LEN - (REG_LEN * 4)
|
||||
# save_rdi # Fuck the linter :D
|
||||
# save_r8 = save_register_space_end - REG_LEN - (REG_LEN * 5)
|
||||
# save_r9 = save_register_space_end - REG_LEN - (REG_LEN * 6)
|
||||
#
|
||||
# gstate_save_addr = x64.create_displacement(disp=allocator.reserve_int())
|
||||
# return_addr_save_addr = x64.create_displacement(disp=allocator.reserve_int())
|
||||
# return_value_save_addr = x64.create_displacement(disp=allocator.reserve_int())
|
||||
#
|
||||
# Reserve_space_for_call = x64.MultipleInstr([x64.Push('RDI')] * 4)
|
||||
# Clean_space_for_call = x64.MultipleInstr([x64.Pop('RDI')] * 4)
|
||||
# Do_stack_alignement = x64.MultipleInstr([x64.Push('RDI')] * 1)
|
||||
# Remove_stack_alignement = x64.MultipleInstr([x64.Pop('RDI')] * 1)
|
||||
#
|
||||
# ensure, objcall, release = get_functions()
|
||||
#
|
||||
# # ## Shellcode ## #
|
||||
# code = x64.MultipleInstr()
|
||||
# # Save all registers
|
||||
# code += x64.Mov('RAX', save_register_space_end)
|
||||
# code += x64.Xchg('RAX', 'RSP')
|
||||
# code += push_all_save_register
|
||||
# code += x64.Xchg('RAX', 'RSP')
|
||||
# # GOOO
|
||||
# code += x64.Mov('RAX', ensure)
|
||||
# code += Reserve_space_for_call
|
||||
# code += Do_stack_alignement
|
||||
# code += x64.Call('RAX')
|
||||
# code += Remove_stack_alignement
|
||||
# code += Clean_space_for_call
|
||||
# code += x64.Mov(gstate_save_addr, 'RAX')
|
||||
# # Save real return addr (for good argument parsing by the callback)
|
||||
# code += x64.Pop('RAX')
|
||||
# code += x64.Mov(return_addr_save_addr, 'RAX')
|
||||
# # Restore parameters for real function call
|
||||
# code += x64.Mov('RAX', save_rcx)
|
||||
# code += x64.Mov('RCX', x64.mem('[RAX]'))
|
||||
# code += x64.Mov('RAX', save_rdx)
|
||||
# code += x64.Mov('RDX', x64.mem('[RAX]'))
|
||||
# code += x64.Mov('RAX', save_r9)
|
||||
# code += x64.Mov('R9', x64.mem('[RAX]'))
|
||||
# code += x64.Mov('RAX', save_r8)
|
||||
# code += x64.Mov('R8', x64.mem('[RAX]'))
|
||||
# # Call python code
|
||||
# code += x64.Mov('RAX', c_callback)
|
||||
# # no need for stack alignement here as we poped the return addr
|
||||
# # no need for Reserve_space_for_call as we must use the previous one for correct argument parsing
|
||||
# code += x64.Call('RAX')
|
||||
# # Save return value
|
||||
# code += x64.Mov(return_value_save_addr, 'RAX')
|
||||
# # Repush real return value
|
||||
# code += x64.Mov('RAX', return_addr_save_addr)
|
||||
# code += x64.Push('RAX')
|
||||
# # Call release(gstate_save)
|
||||
# code += x64.Mov('RAX', gstate_save_addr)
|
||||
# code += x64.Mov('RCX', 'RAX')
|
||||
# code += x64.Mov('RAX', release)
|
||||
# code += Reserve_space_for_call
|
||||
# code += Do_stack_alignement
|
||||
# code += x64.Call('RAX')
|
||||
# code += Remove_stack_alignement
|
||||
# code += Clean_space_for_call
|
||||
# # Restore registers
|
||||
# code += x64.Mov('RAX', save_register_space)
|
||||
# code += x64.Xchg('RAX', 'RSP')
|
||||
# code += pop_all_save_register
|
||||
# code += x64.Xchg('RAX', 'RSP')
|
||||
# # Restore return value
|
||||
# code += x64.Mov('RAX', return_value_save_addr)
|
||||
# code += x64.Ret()
|
||||
# return code
|
||||
|
||||
|
||||
def generate_callback_stub(callback, types):
|
||||
|
||||
@@ -117,6 +117,56 @@ class HookTestCase(unittest.TestCase):
|
||||
with calc.allocated_memory(0x1000) as addr:
|
||||
calc.write_memory(addr, "DLLNOTFOUND.NOT_A_REAL_DLL" + "\x00")
|
||||
t = calc.create_thread(load_libraryA, addr)
|
||||
t.wait()
|
||||
self.assertEqual(remote_ask("windows.current_thread.exit(len(calling_thread))"), 3)
|
||||
|
||||
def test_remote_iat_hook_64(self):
|
||||
with Calc64() as calc:
|
||||
calc.execute_python("import windows")
|
||||
calc.execute_python("windows.utils.create_console()")
|
||||
|
||||
code = """
|
||||
import windows.generated_def as gdef
|
||||
|
||||
cp = windows.current_process
|
||||
kernelbase_mod = [m for m in cp.peb.modules if m.name == "kernelbase.dll"][0]
|
||||
LdrLoadDll = [n for n in kernelbase_mod.pe.imports['ntdll.dll'] if n.name == "LdrLoadDll"][0]
|
||||
|
||||
calling_thread = set([])
|
||||
hooking_thread = windows.current_thread.tid
|
||||
@windows.hooks.Callback(*[gdef.PVOID] * 5)
|
||||
def MyHook(*args, **kwargs):
|
||||
calling_thread.add(windows.current_thread.tid)
|
||||
print(windows.current_thread.tid)
|
||||
return kwargs["real_function"]()
|
||||
|
||||
LdrLoadDll.set_hook(MyHook)
|
||||
print("Hooker = " + str(windows.current_thread.tid))
|
||||
import ctypes
|
||||
try:
|
||||
ctypes.WinDLL("NOT_A_REAL_DLL")
|
||||
except WindowsError as e:
|
||||
pass
|
||||
"""
|
||||
calc.execute_python_unsafe(textwrap.dedent(code))
|
||||
# Tricky part: we use an injected thread exit_value to ask stuff about the remote python
|
||||
def remote_ask(request):
|
||||
t = calc.execute_python_unsafe(request)
|
||||
t.wait()
|
||||
return t.exit_code
|
||||
|
||||
self.assertEqual(remote_ask("windows.current_thread.exit(len(calling_thread))"), 1)
|
||||
self.assertEqual(remote_ask("windows.current_thread.exit(calling_thread == set([hooking_thread]))"), 1)
|
||||
|
||||
# Trigger hook from another Python thread
|
||||
calc.execute_python_unsafe("ctypes.WinDLL('ANOTHER_FAKE_DLL')").wait()
|
||||
self.assertEqual(remote_ask("windows.current_thread.exit(len(calling_thread))"), 2)
|
||||
|
||||
# Trigger hook from a NONPython thread
|
||||
k32 = [m for m in calc.peb.modules if m.name == "kernel32.dll"][0]
|
||||
load_libraryA = k32.pe.exports["LoadLibraryA"]
|
||||
with calc.allocated_memory(0x1000) as addr:
|
||||
calc.write_memory(addr, "DLLNOTFOUND.NOT_A_REAL_DLL" + "\x00")
|
||||
t = calc.create_thread(load_libraryA, addr)
|
||||
t.wait()
|
||||
self.assertEqual(remote_ask("windows.current_thread.exit(len(calling_thread))"), 3)
|
||||
|
||||
@@ -207,7 +207,7 @@ def get_short_path(path):
|
||||
return buffer[:rsize]
|
||||
|
||||
def get_shared_mapping(name, size=0x1000):
|
||||
# TODO: real cod
|
||||
# TODO: real code
|
||||
h = windows.winproxy.CreateFileMappingA(INVALID_HANDLE_VALUE, dwMaximumSizeLow=size, lpName=name)
|
||||
addr = windows.winproxy.MapViewOfFile(h, dwNumberOfBytesToMap=size)
|
||||
return addr
|
||||
|
||||
Reference in New Issue
Block a user