diff --git a/TODO b/TODO index baed916..6fa4f16 100644 --- a/TODO +++ b/TODO @@ -16,6 +16,9 @@ TODO: - reprise sur erreur - reprise context modification + - Free all the virtual_alloc + - Real API arround alloc/free memory in WinProcess.. + FIXME: - WMI - COM initialisation when injected in another process diff --git a/windows/injection.py b/windows/injection.py index de27358..f063d3d 100644 --- a/windows/injection.py +++ b/windows/injection.py @@ -8,52 +8,101 @@ import windows.utils as utils from .native_exec import simple_x86 as x86 from .native_exec import simple_x64 as x64 +from windows.native_exec.nativeutils import GetProcAddress64 -def get_loadlib_getproc(target): - if windows.current_process.bitness == target.bitness: - LoadLibraryA = utils.get_func_addr('kernel32', 'LoadLibraryA') - GetProcAddress = utils.get_func_addr('kernel32', 'GetProcAddress') - return LoadLibraryA, GetProcAddress - else: - k32 = [x for x in target.peb.modules if x.name == "kernel32.dll"][0] - exp = k32.pe.exports - return exp['LoadLibraryA'], exp['GetProcAddress'] +from windows.dbgprint import dbgprint + + +def load_dll_in_remote_process(target, dll_name): + rpeb = target.peb + if rpeb.Ldr: + # LDR est parcourable, ca va etre deja plus simple.. + modules = rpeb.modules + if any(mod.name == dll_name for mod in modules): + # DLL already loaded + dbgprint("DLL already present in DLL", "DLLINJECT") + return True + k32 = [mod for mod in modules if mod.name.lower() == "kernel32.dll"] + if k32: + # We have kernel32 \o/ + k32 = k32[0] + try: + load_libraryA = k32.pe.exports["LoadLibraryA"] + except KeyError: + raise ValueError("Kernel32 have no export (wtf)") + + addr = target.virtual_alloc(0x1000) + target.write_memory(addr, dll_name + "\x00") + t = target.create_thread(load_libraryA, addr) + t.wait() + windows.winproxy.VirtualFreeEx(target.handle, addr) + dbgprint("DLL Injected via (LoadLibray)", "DLLINJECT") + return True + # Hardcore mode + # We don't have k32 or PEB->Ldr + # Go inject a GetProcAddress(LoadLib) + LoadLib shellcode :D + if target.bitness == 32: + raise NotImplementedError("Manuel GetProcAddress 32bits") + + dll = "KERNEL32.DLL\x00".encode("utf-16-le") + api = "LoadLibraryA\x00" + dll_to_load = dll_name + "\x00" + + RemoteManualLoadLibray = x64.MultipleInstr() + code = RemoteManualLoadLibray + code += x64.Mov("R15", "RCX") + code += x64.Mov("RCX", x64.mem("[R15 + 0]")) + code += x64.Mov("RDX", x64.mem("[R15 + 8]")) + code += x64.Call(":FUNC_GETPROCADDRESS64") + code += x64.Mov("RCX", x64.mem("[R15 + 0x10]")) + code += x64.Push("RCX") + code += x64.Push("RCX") + code += x64.Push("RCX") + code += x64.Call("RAX") # LoadLibrary + code += x64.Pop("RCX") + code += x64.Pop("RCX") + code += x64.Pop("RCX") + code += x64.Ret() + + RemoteManualLoadLibray += GetProcAddress64 + + addr = target.virtual_alloc(0x1000) + addr2 = addr + len(dll) + addr3 = addr2 + len(api) + addr4 = addr3 + len(dll_to_load) + + target.write_memory(addr, dll) + target.write_memory(addr2, api) + target.write_memory(addr3, dll_to_load) + target.write_qword(addr4, addr) + target.write_qword(addr4 + 8, addr2) + target.write_qword(addr4 + 0x10, addr3) + + t = target.execute(RemoteManualLoadLibray.get_code(), addr4) + t.wait() + dbgprint("DLL Injected via manual GetProc(LoadLibray)", "DLLINJECT") + return True # 32 to 32 injection -def generate_python_exec_shellcode_32(target, PYDLL_addr, PyInit, PyRun, PYCODE_ADDR): - LoadLibraryA, GetProcAddress = get_loadlib_getproc(target) +def generate_python_exec_shellcode_32(target, PyInit, PyRun, PYCODE_ADDR): code = x86.MultipleInstr() - # Load python27.dll - code += x86.Push(PYDLL_addr) - code += x86.Mov('EAX', LoadLibraryA) - code += x86.Call('EAX') - # Get PyInit function into pythondll - code += x86.Push('EAX') - code += x86.Pop('EDI') - code += x86.Push(PyInit) - code += x86.Push('EDI') - code += x86.Mov('EBX', GetProcAddress) - code += x86.Call('EBX') # Call PyInit + + code += x86.Mov('EAX', PyInit) code += x86.Call('EAX') # Get PyRun function into pythondll - code += x86.Push(PyRun) - code += x86.Push('EDI') - code += x86.Call('EBX') # Call PyRun with python code to exec code += x86.Push(PYCODE_ADDR) + code += x86.Mov('EAX', PyRun) code += x86.Call('EAX') - code += x86.Pop('EDI') + code += x86.Pop("EDI") code += x86.Ret() return code.get_code() # 64 to 64 injection -def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_ADDR): - - LoadLibraryA, GetProcAddress = get_loadlib_getproc(target) - +def generate_python_exec_shellcode_64(target, PyInit, PyRun, PYCODE_ADDR): Reserve_space_for_call = x64.MultipleInstr([x64.Push('RDI')] * 4) Clean_space_for_call = x64.MultipleInstr([x64.Pop('RDI')] * 4) @@ -61,35 +110,16 @@ def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_ # Do stack alignement code += x64.Push('RCX') # Load python27.dll - code += x64.Mov('RCX', PYDLL_addr) - code += x64.Mov('RAX', LoadLibraryA) - code += Reserve_space_for_call - code += x64.Call('RAX') - code += Clean_space_for_call - code += x64.Push('RAX') - code += x64.Pop('RCX') - # Save RCX - code += x64.Push('RCX') - # Align stack - code += x64.Push('RDI') # Get PyInit function into pythondll code += Reserve_space_for_call - code += x64.Mov('RDX', PyInit) - code += x64.Mov('RBX', GetProcAddress) - code += x64.Call('RBX') + code += x64.Mov('RAX', PyInit) # Call PyInit code += x64.Call('RAX') code += Clean_space_for_call - # Remove Stack align - code += x64.Pop('RDI') - # Restore pythondll base into rcx - code += x64.Pop('RCX') - # Get PyRun function into pythondll - code += x64.Mov('RDX', PyRun) code += Reserve_space_for_call - code += x64.Call('RBX') - # Call PyInit with python code to exec + code += x64.Mov('RAX', PyRun) code += x64.Mov('RCX', PYCODE_ADDR) + # Call PyRun code += x64.Call('RAX') code += Clean_space_for_call # Remove stack alignement @@ -98,40 +128,32 @@ def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_ return code.get_code() -def inject_python_command(process, code_injected, PYDLL="python27.dll\x00"): - PyInitT = "Py_Initialize\x00" +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" - remote_addr_base = process.virtual_alloc(len(code_injected) + 0x100) - remote_addr = remote_addr_base - PYDLL_addr = remote_addr - process.write_memory(remote_addr, PYDLL) - remote_addr += len(PYDLL) + 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"] - PyInitT_ADDR = remote_addr - process.write_memory(remote_addr, PyInitT) - remote_addr += len(PyInitT) + remote_addr = target.virtual_alloc(len(PYCODE) + 0x100) + target.write_memory(remote_addr, PYCODE) + SHELLCODE_ADDR = remote_addr + len(PYCODE) - Pyrun_ADDR = remote_addr - process.write_memory(remote_addr, Pyrun) - remote_addr += len(Pyrun) - - PYCODE_ADDR = remote_addr - process.write_memory(remote_addr, PYCODE) - remote_addr += len(PYCODE) - - SHELLCODE_ADDR = remote_addr - if process.bitness == 32: + if target.bitness == 32: shellcode_generator = generate_python_exec_shellcode_32 else: shellcode_generator = generate_python_exec_shellcode_64 - shellcode = shellcode_generator(process, PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR) - process.write_memory(SHELLCODE_ADDR, shellcode) + + shellcode = shellcode_generator(target, PyInit, Pyrun, remote_addr) + target.write_memory(SHELLCODE_ADDR, shellcode) return SHELLCODE_ADDR -def validate_python_dll_presence(process): +def validate_python_dll_presence_on_disk(process): if windows.current_process.bitness == process.bitness: return True if windows.current_process.bitness == 32 and process.bitness == 64: @@ -146,9 +168,12 @@ def validate_python_dll_presence(process): raise NotImplementedError("Unknown bitness") def execute_python_code(process, code): - validate_python_dll_presence(process) - shellcode_remote_addr = inject_python_command(process, code) - return process.create_thread(shellcode_remote_addr, 0) + 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) + return t + retrieve_exc = r""" import traceback diff --git a/windows/winobject.py b/windows/winobject.py index 5b4bfb3..34b3457 100644 --- a/windows/winobject.py +++ b/windows/winobject.py @@ -5,6 +5,8 @@ import time import struct import itertools +from contextlib import contextmanager + import windows import windows.network import windows.registry @@ -321,14 +323,22 @@ class Process(AutoHandle): """ return self.exit_code != STILL_ACTIVE - def execute(self, code): + #@contextmanager + #def allocated_memory(self, size): + # addr = self.virtual_alloc(size) + # try: + # yield addr + # finally: + # windows.winproxy.VirtualFreeEx(self.handle, size) + + def execute(self, code, parameter=0): """Execute some native code in the context of the process :return: The return value of the native code :rtype: :class:`int`""" x = self.virtual_alloc(len(code)) self.write_memory(x, code) - return self.create_thread(x, 0) + return self.create_thread(x, parameter) def query_memory(self, addr): """Query the memory informations about page at ``addr`` @@ -366,6 +376,60 @@ class Process(AutoHandle): return addr += x.RegionSize + def read_char(self, addr): + sizeof_char = sizeof(CHAR) + return struct.unpack("Ldr is NULL: cannot walk the module list") list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value - current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) while current_dll.DllBase: res.append(current_dll) @@ -938,8 +971,9 @@ if CurrentProcess().bitness == 32: :type: [:class:`LoadedModule`] -- List of loaded modules """ res = [] + if not self.Ldr.value: + raise ValueError("PEB->Ldr is NULL: cannot walk the module list") list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value - current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) while current_dll.DllBase: res.append(current_dll) @@ -970,9 +1004,9 @@ if CurrentProcess().bitness == 64: :type: [:class:`LoadedModule`] -- List of loaded modules """ res = [] - #import pdb;pdb.set_trace() + if not self.Ldr.value: + raise ValueError("PEB->Ldr is NULL: cannot walk the module list") list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value - current_dll = self.ptr_flink_to_remote_module(list_entry_ptr) while current_dll.DllBase: res.append(current_dll)