Rewrite injected stub for remote python -> now have nice threads handling

This commit is contained in:
Clement Rouault
2016-01-29 13:51:35 +01:00
parent 7be3561ccf
commit 5b5db57e17
3 changed files with 108 additions and 29 deletions
+8 -3
View File
@@ -12,13 +12,18 @@ TODO:
- Injection with suspended process ?
- FIX/add test (inject suspended 32 -> 64)
- VEH:
- reprise sur erreur
- reprise context modification
- Free all the virtual_alloc
- Real API arround alloc/free memory in WinProcess..
- Threading
- code injection do real thread stuff
- Quid IAT hook stub ? just einit threads and remove this ?
- Injection
- code generated by generate_python_exec_shellcode_64[32] may be reused
Just need to passe the address of the python string as argument
FIXME:
- WMI
- COM initialisation when injected in another process
+97 -25
View File
@@ -83,62 +83,134 @@ def load_dll_in_remote_process(target, dll_name):
dbgprint("DLL Injected via manual GetProc(LoadLibray)", "DLLINJECT")
return True
python_function_32_bits = {}
# 32 to 32 injection
def generate_python_exec_shellcode_32(target, PyInit, PyRun, PYCODE_ADDR):
code = x86.MultipleInstr()
# Call PyInit
def generate_python_exec_shellcode_32(target, PYCODE_ADDR, 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
python_function_32_bits["PyEval_InitThreads"] = Py_exports["PyEval_InitThreads"]
python_function_32_bits["Py_IsInitialized"] = Py_exports["Py_IsInitialized"]
python_function_32_bits["PyGILState_Release"] = Py_exports["PyGILState_Release"]
python_function_32_bits["PyGILState_Ensure"] = Py_exports["PyGILState_Ensure"]
python_function_32_bits["PyEval_SaveThread"] = Py_exports["PyEval_SaveThread"]
python_function_32_bits["Py_Initialize"] = Py_exports["Py_Initialize"]
python_function_32_bits["PyRun_SimpleString"] = Py_exports["PyRun_SimpleString"]
Py_exports = python_function_32_bits
PyEval_InitThreads = Py_exports["PyEval_InitThreads"]
Py_IsInitialized = Py_exports["Py_IsInitialized"]
PyGILState_Release = Py_exports["PyGILState_Release"]
PyGILState_Ensure = Py_exports["PyGILState_Ensure"]
PyEval_SaveThread = Py_exports["PyEval_SaveThread"]
Py_Initialize = Py_exports["Py_Initialize"]
PyRun_SimpleString = Py_exports["PyRun_SimpleString"]
code += x86.Mov('EAX', PyInit)
code = x86.MultipleInstr()
code += x86.Mov('EAX', Py_IsInitialized)
code += x86.Call('EAX')
# Get PyRun function into pythondll
# Call PyRun with python code to exec
code += x86.Mov("EDI", "EAX")
code += x86.Cmp("EAX", 0)
code += x86.Jnz(":DO_ENSURE")
# Python Initilisation code
# init multithread (for other injection)
code += x86.Mov('EAX', PyEval_InitThreads)
code += x86.Call('EAX')
code += x86.Mov('EAX', Py_Initialize)
code += x86.Call('EAX')
code += x86.Label(":DO_ENSURE")
code += x86.Mov('EAX', PyGILState_Ensure)
code += x86.Call('EAX')
code += x86.Push('EAX')
code += x86.Push(PYCODE_ADDR)
code += x86.Mov('EAX', PyRun)
code += x86.Mov('EAX', PyRun_SimpleString)
code += x86.Call('EAX')
code += x86.Mov("ESI", "EAX")
code += x86.Mov('EAX', PyGILState_Release)
code += x86.Call('EAX')
code += x86.Pop('EAX')
code += x86.Cmp("EDI", 0)
code += x86.Jnz(":RETURN")
# If PyEval_InitThreads was called (init done in this thread)
# We must release the GIL
code += x86.Mov('EAX', PyEval_SaveThread)
code += x86.Call('EAX')
code += x86.Label(":RETURN")
code += x86.Mov("EAX", "ESI")
code += x86.Pop("EDI")
code += x86.Ret()
return code.get_code()
python_function_64_bits = {}
# 64 to 64 injection
def generate_python_exec_shellcode_64(target, PyInit, PyRun, PYCODE_ADDR):
def generate_python_exec_shellcode_64(target, PYCODE_ADDR, 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
python_function_64_bits["PyEval_InitThreads"] = Py_exports["PyEval_InitThreads"]
python_function_64_bits["Py_IsInitialized"] = Py_exports["Py_IsInitialized"]
python_function_64_bits["PyGILState_Release"] = Py_exports["PyGILState_Release"]
python_function_64_bits["PyGILState_Ensure"] = Py_exports["PyGILState_Ensure"]
python_function_64_bits["PyEval_SaveThread"] = Py_exports["PyEval_SaveThread"]
python_function_64_bits["Py_Initialize"] = Py_exports["Py_Initialize"]
python_function_64_bits["PyRun_SimpleString"] = Py_exports["PyRun_SimpleString"]
Py_exports = python_function_64_bits
PyEval_InitThreads = Py_exports["PyEval_InitThreads"]
Py_IsInitialized = Py_exports["Py_IsInitialized"]
PyGILState_Release = Py_exports["PyGILState_Release"]
PyGILState_Ensure = Py_exports["PyGILState_Ensure"]
PyEval_SaveThread = Py_exports["PyEval_SaveThread"]
Py_Initialize = Py_exports["Py_Initialize"]
PyRun_SimpleString = Py_exports["PyRun_SimpleString"]
Reserve_space_for_call = x64.MultipleInstr([x64.Push('RDI')] * 4)
Clean_space_for_call = x64.MultipleInstr([x64.Pop('RDI')] * 4)
code = x64.MultipleInstr()
# Do stack alignement
code += x64.Push('RCX')
# Load python27.dll
# Get PyInit function into pythondll
code += Reserve_space_for_call
code += x64.Mov('RAX', PyInit)
# Call PyInit
code += x64.Mov('RAX', Py_IsInitialized)
code += x64.Call('RAX')
code += Clean_space_for_call
code += Reserve_space_for_call
code += x64.Mov('RAX', PyRun)
code += x64.Mov("RDI", "RAX")
code += x64.Cmp("RAX", 0)
code += x64.Jnz(":DO_ENSURE")
code += x64.Mov('RAX', PyEval_InitThreads)
code += x64.Call('RAX')
code += x64.Mov('RAX', Py_Initialize)
code += x64.Call('RAX')
code += x64.Label(":DO_ENSURE")
code += x64.Mov('RAX', PyGILState_Ensure)
code += x64.Call('RAX')
code += x64.Mov('R15', 'RAX')
code += x64.Mov('RAX', PyRun_SimpleString)
code += x64.Mov('RCX', PYCODE_ADDR)
# Call PyRun
code += x64.Call('RAX')
code += x64.Mov('RCX', 'R15')
code += x64.Mov('R15', 'RAX')
code += x64.Mov('RAX', PyGILState_Release)
code += x64.Call('RAX')
code += x64.Cmp("RDI", 0)
code += x64.Jnz(":RETURN")
# If PyEval_InitThreads was called (init done in this thread)
# We must release the GIL
code += x64.Mov('RAX', PyEval_SaveThread)
code += x64.Call('RAX')
code += x64.Label(":RETURN")
code += Clean_space_for_call
# Remove stack alignement
code += x64.Pop('RCX')
code += x64.Mov("RAX", "R15")
code += x64.Ret()
return code.get_code()
def inject_python_command(target, code_injected, PYDLL):
"""Postulate: PYDLL is already loaded in target process"""
PyInit = "Py_Initialize\x00"
Pyrun = "PyRun_SimpleString\x00"
PYCODE = code_injected + "\x00"
pymodule = [mod for mod in target.peb.modules if mod.name == PYDLL][0]
Py_exports = pymodule.pe.exports
PyInit = Py_exports["Py_Initialize"]
Pyrun = Py_exports["PyRun_SimpleString"]
remote_addr = target.virtual_alloc(len(PYCODE) + 0x100)
target.write_memory(remote_addr, PYCODE)
SHELLCODE_ADDR = remote_addr + len(PYCODE)
@@ -148,7 +220,7 @@ def inject_python_command(target, code_injected, PYDLL):
else:
shellcode_generator = generate_python_exec_shellcode_64
shellcode = shellcode_generator(target, PyInit, Pyrun, remote_addr)
shellcode = shellcode_generator(target, remote_addr, PYDLL)
target.write_memory(SHELLCODE_ADDR, shellcode)
return SHELLCODE_ADDR
+3 -1
View File
@@ -301,7 +301,9 @@ class VectoredException(object):
def __new__(cls, func):
self = object.__new__(cls)
self.func = func
return self.func_type(self.decorator)
v = self.func_type(self.decorator)
v.self = self
return v
def decorator(self, exception_pointers):
try: