Add support for 64bits injection + hooks

This commit is contained in:
Clement Rouault
2015-03-12 18:25:49 +01:00
parent 9c80593a97
commit e2c2bdc236
13 changed files with 729 additions and 394 deletions
+135 -12
View File
@@ -2,6 +2,9 @@ import ctypes
import mmap
import platform
import windows
import windows.k32testing as k32api
class PyObj(ctypes.Structure):
_fields_ = [("ob_refcnt", ctypes.c_size_t),
("ob_type", ctypes.c_void_p)] #must be cast
@@ -36,7 +39,7 @@ class Win32MyMap(MyMap):
#access = mmap.ACCESS_READ | mmap.ACCESS_WRITE
#return cls(-1, size, access=access)
access = mmap.ACCESS_READ | mmap.ACCESS_WRITE
addr = ctypes.windll.kernel32.VirtualAlloc(0, size, 0x1000, 0x40)
addr = k32api.VirtualAlloc(0, size, 0x1000, 0x40)
new_map = (ctypes.c_char * size).from_address(addr)
new_map.addr = addr
@@ -95,15 +98,13 @@ allocator = CustomAllocator()
def get_functions():
# Windows only with python27.dll | improve this
# Windows only with python27.dll | improve this ?
import sys
sys.path.append(r"C:\Users\hakril\Documents\Work\Python_Injection")
import windows
PyGILState_Ensure = windows.get_func_addr('python27', 'PyGILState_Ensure')
PyObject_CallObject = windows.get_func_addr('python27', 'PyObject_CallObject')
PyGILState_Release = windows.get_func_addr('python27', 'PyGILState_Release')
PyGILState_Ensure = windows.utils.get_func_addr('python27', 'PyGILState_Ensure')
PyObject_CallObject = windows.utils.get_func_addr('python27', 'PyObject_CallObject')
PyGILState_Release = windows.utils.get_func_addr('python27', 'PyGILState_Release')
return [PyGILState_Ensure, PyObject_CallObject, PyGILState_Release]
def analyse_callback(callback):
@@ -114,16 +115,17 @@ def analyse_callback(callback):
raise ValueError("Need a ctypes PyCFuncPtr")
return obj_id
from simple_x86 import *
# For windows 32 bits with stdcall
def generate_stub(callback):
from simple_x86 import *
obj_id = analyse_callback(callback)
c_callback = ctypes.c_ulong.from_address(id(callback._objects['0']) + 0xc).value
c_callback = ctypes.c_ulong.from_address(id(callback._objects['0']) + 3 * ctypes.sizeof(ctypes.c_void_p)).value
gstate_save_addr = allocator.reserve_int()
return_addr_save_addr = allocator.reserve_int()
save_ebx = allocator.reserve_int()
save_ecx = allocator.reserve_int()
save_edx = allocator.reserve_int()
@@ -155,6 +157,7 @@ def generate_stub(callback):
code += Mov_EAX_X(c_callback)
code += Call_EAX()
# TODO : Remove this and setup callback as WinFuncType
for i in range(len(callback.argtypes)):
code += Pop_EBX()
@@ -181,12 +184,132 @@ def generate_stub(callback):
code += Mov_EDI_DX(save_edi)
code += Ret()
return code
# For windows 32 bits with stdcall
def generate_stub_64(callback):
from simple_x64 import *
obj_id = analyse_callback(callback)
c_callback = ctypes.c_ulong.from_address(id(callback._objects['0']) + 3 * ctypes.sizeof(ctypes.c_void_p)).value
gstate_save_addr = allocator.reserve_int()
return_addr_save_addr = allocator.reserve_int()
Reserve_space_for_call = MultipleInstr([Push_RDI()] * 4)
Clean_space_for_call = MultipleInstr([Pop_RDI()] * 4)
save_rbx = allocator.reserve_int()
save_rcx = allocator.reserve_int()
save_rdx = allocator.reserve_int()
save_rsi = allocator.reserve_int()
save_rdi = allocator.reserve_int()
ensure, objcall, release = get_functions()
print("save_rbx = {0}".format(hex(save_rbx)))
### Shellcode ###
code = MultipleInstr()
code += Mov_RAX_X(save_rbx)
code += Mov_DRAX_RBX()
code += Mov_RAX_X(save_rcx)
code += Mov_DRAX_RCX()
code += Mov_RAX_X(save_rdx)
code += Mov_DRAX_RDX()
code += Mov_RAX_X(save_rsi)
code += Mov_DRAX_RSI()
code += Mov_RAX_X(save_rdi)
code += Mov_DRAX_RDI()
# need to save other registers ?
code += Mov_RAX_X(ensure)
code += Reserve_space_for_call
code += Call_RAX()
code += Clean_space_for_call
code += Mov_DX_RAX(gstate_save_addr)
#Save real return addr (for good argument parsing by the callback)
code += Pop_RAX()
code += Mov_DX_RAX(return_addr_save_addr)
# Restore parameters for real function call
code += Mov_RAX_X(save_rbx)
code += Mov_RBX_DRAX()
code += Mov_RAX_X(save_rcx)
code += Mov_RCX_DRAX()
code += Mov_RAX_X(save_rdx)
code += Mov_RDX_DRAX()
code += Mov_RAX_X(save_rsi)
code += Mov_RSI_DRAX()
code += Mov_RAX_X(save_rdi)
code += Mov_DRAX_RDI()
code += Reserve_space_for_call
# Call python code
code += Mov_RAX_X(c_callback)
code += Clean_space_for_call
code += Call_RAX()
code += Mov_RAX_DX(return_addr_save_addr)
code += Push_RAX()
code += Mov_RAX_X(release)
code += Reserve_space_for_call
code += Call_RAX()
code += Clean_space_for_call
# Restore registers
code += Mov_RAX_X(save_rbx)
code += Mov_RBX_DRAX()
code += Mov_RAX_X(save_rcx)
code += Mov_RCX_DRAX()
code += Mov_RAX_X(save_rdx)
code += Mov_RDX_DRAX()
code += Mov_RAX_X(save_rsi)
code += Mov_RSI_DRAX()
code += Mov_RAX_X(save_rdi)
code += Mov_DRAX_RDI()
code += Ret()
###### CODE 32 %%%%%%%%
#
##Save real return addr (for good argument parsing by the callback)
#
## TODO : Remove this and setup callback as WinFuncType
#for i in range(len(callback.argtypes)):
# code += Pop_EBX()
#
## Restore real return value
#code += Mov_EBX_DX(return_addr_save_addr)
#code += Push_EBX()
#
## Save return value
#code += Push_EAX()
#code += Mov_EBX_DX(gstate_save_addr)
#code += Push_EBX()
#
#code += Mov_EAX_X(release)
#code += Call_EAX()
#
## Discard `release` argument
#code += Pop_EAX()
## Restore return value
#code += Pop_EAX()
#code += Mov_EBX_DX(save_ebx)
#code += Mov_ECX_DX(save_ecx)
#code += Mov_EDX_DX(save_edx)
#code += Mov_ESI_DX(save_esi)
#code += Mov_EDI_DX(save_edi)
#code += Ret()
return code
def generate_callback_stub(callback, types):
func_type = ctypes.CFUNCTYPE(*types)
c_callable = func_type(callback)
stub = generate_stub(c_callable)
if windows.current_process.bitness == 32:
stub = generate_stub(c_callable)
else:
stub = generate_stub_64(c_callable)
stub_addr = allocator.write_code(stub.get_code())
generate_callback_stub.l.append((stub, c_callable))
return stub_addr