diff --git a/CHANGELOG b/CHANGELOG index 9e33f13..6da8d2e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -16,4 +16,7 @@ Since 0.2: * Added COMImplementation to com interface * fix x86.assemble + add x64.assemble | fix some enconding problem in x64 * Add test_code.py sample - * Remove OptionExport from winproxy \ No newline at end of file + * Remove OptionExport from winproxy + * MemoryBP + single_step for windows.debug.Debugger + * Possibility to delete breakpoints in windows.debug.Debugger + * Add Context.func_result as abstract register for EAX/RAX \ No newline at end of file diff --git a/TODO b/TODO index 1ed50a9..da8fbfe 100644 --- a/TODO +++ b/TODO @@ -8,16 +8,11 @@ TODO: - Verif multiple pending at same place - Test !! (bp, BP_HX, bp on only on process, bp_hx on only one thread..) - test breakpoint with specific target + - Add test for debugger with breakpoint that add another breakpoint on trigger - - Readme - - Debugger ? Veh ? + - remotectypes + - pretty sur I can get rid of PointerToStruct64/PointerToStruct32 - - TransparentApiProxy - double name (params and args) for same info.. - - - Parse .IDL file for more COM NAME->IID - - - Add test for debugger with breakpoint that add another breakpoint on trigger - Some test/doc on windows.system.handles @@ -28,6 +23,7 @@ TODO: Documentation * verif samples * COMImplementation (example in LKD) + * MemoryBP + single_step() in Debugger FIXME: - setup.py build seems to raise an error diff --git a/windows/debug/breakpoints.py b/windows/debug/breakpoints.py index ac642ec..f7664b8 100644 --- a/windows/debug/breakpoints.py +++ b/windows/debug/breakpoints.py @@ -1,6 +1,8 @@ +from collections import OrderedDict + +import windows from windows.generated_def.winstructs import * from windows.generated_def import windef - from windows.winobject.process import WinProcess, WinThread @@ -52,4 +54,90 @@ class MemoryBreakpoint(Breakpoint): def trigger(self, dbg, exception): """Called when breakpoint is hit""" + pass + + +## Arguments Helper (need to move this elsewhere) +class X86ArgumentRetriever(object): + def get_arg(self, nb, proc, thread): + return proc.read_dword(thread.context.sp + 4 + (4 * nb)) + +class X64ArgumentRetriever(object): + REG_ARGS = ["Rcx", "Rdx", "R8", "R9"] + def get_arg(self, nb, proc, thread): + if nb < len(self.REG_ARGS): + return getattr(thread.context, self.REG_ARGS[nb]) + return proc.read_dword(thread.context.sp + 8 + (8 * nb)) + +## Behaviour breakpoint ! +class ParamDumpBP(Breakpoint): + def __init__(self, addr, target): + super(ParamDumpBP, self).__init__(addr) + self.target = target + self.target_args = target.prototype._argtypes_ + + def extract_arguments_32bits(self, cproc, cthread): + x = windows.debug.X86ArgumentRetriever() + res = OrderedDict() + for i, (name, type) in enumerate(zip(self.target.params, self.target_args)): + value = x.get_arg(i, cproc, cthread) + rt = windows.remotectypes.transform_type_to_remote32bits(type) + if issubclass(rt, windows.remotectypes.RemoteValue): + t = rt(value, cproc) + else: + t = rt(value) + if not hasattr(t, "contents"): + try: + t = t.value + except AttributeError: + pass + res[name[1]] = t + return res + + def extract_arguments_64bits(self, cproc, cthread): + x = windows.debug.X64ArgumentRetriever() + res = OrderedDict() + for i, (name, type) in enumerate(zip(self.target.params, self.target_args)): + value = x.get_arg(i, cproc, cthread) + rt = windows.remotectypes.transform_type_to_remote64bits(type) + if issubclass(rt, windows.remotectypes.RemoteValue): + t = rt(value, cproc) + else: + t = rt(value) + if not hasattr(t, "contents"): + try: + t = t.value + except AttributeError: + pass + res[name[1]] = t + return res + + def extract_arguments(self, cproc, cthread): + if windows.current_process.bitness == 32: + return self.extract_arguments_32bits(cproc, cthread) + if cproc.bitness == 64: + return self.extract_arguments_64bits(cproc, cthread) + # SysWow process from a 64bits debugger, handle bitness with CS + if cthread.context.SegCs == windows.syswow64.CS_32bits: + return self.extract_arguments_32bits(cproc, cthread) + return self.extract_arguments_64bits(cproc, cthread) + + +class FunctionRetBP(Breakpoint): + def __init__(self, addr, initial_breakpoint): + super(FunctionRetBP, self).__init__(addr) + self.initial_breakpoint = initial_breakpoint + + def trigger(self, dbg, exc): + dbg.del_bp(self, targets=[dbg.current_process]) + return self.initial_breakpoint.ret_trigger(dbg, exc) + + +class FunctionCallBP(Breakpoint): + def trigger(self, dbg, exception): + cproc = dbg.current_process + return_addr = dbg.current_process.read_ptr(dbg.current_thread.context.sp) + dbg.add_bp(FunctionRetBP(return_addr, self), target=dbg.current_process) + + def ret_trigger(self, dbg, exception): pass \ No newline at end of file diff --git a/windows/remotectypes.py b/windows/remotectypes.py index 5af6db6..2f3fd73 100644 --- a/windows/remotectypes.py +++ b/windows/remotectypes.py @@ -188,7 +188,7 @@ class Remote_c_char_p64(c_char_p64, RemotePtr64, RemoteCCharP): class Remote_w_char_p64(c_wchar_p64, RemotePtr64, RemoteWCharP): def __repr__(self): - return "".format(self.raw_value) + return "".format(self.raw_value) class RemoteStructurePointer64(Remote_c_void_p64): @@ -246,7 +246,7 @@ class Remote_c_char_p32(c_char_p32, RemotePtr32, RemoteCCharP): class Remote_w_char_p32(c_wchar_p32, RemotePtr32, RemoteWCharP): def __repr__(self): - return "".format(self.raw_value) + return "".format(self.raw_value) class RemoteStructurePointer32(Remote_c_void_p32): @@ -375,6 +375,13 @@ remote_struct = RemoteStructure.from_structure def MakePtr64(type): class PointerToStruct64(Remote_c_void_p64): _sub_ctypes_ = (type) + + @property + def contents(self): + return RemoteStructurePointer64.from_buffer_with_target_and_ptr_type(bytearray(self), target=self.target, ptr_type=self).contents + + def __repr__(self): + return "".format(type.__name__) return PointerToStruct64 def transform_structure_to_remote64bits(structcls): @@ -410,6 +417,16 @@ def transform_type_to_remote64bits(ftype): def MakePtr32(type): class PointerToStruct32(Remote_c_void_p32): _sub_ctypes_ = (type) + + # Not sur about this code.. + # Logic problem: why do I have PointerToStruct32 and RemoteStructurePointer32... ? + @property + def contents(self): + return RemoteStructurePointer32.from_buffer_with_target_and_ptr_type(bytearray(self), target=self.target, ptr_type=self).contents + + def __repr__(self): + return "".format(type.__name__) + return PointerToStruct32 def transform_structure_to_remote32bits(structcls): diff --git a/windows/winobject/exception.py b/windows/winobject/exception.py index d1f7090..5c7efa4 100644 --- a/windows/winobject/exception.py +++ b/windows/winobject/exception.py @@ -184,6 +184,7 @@ class ECONTEXTBase(object): default_dump = () pc_reg = '' sp_reg = '' + func_result_reg = '' special_reg_type = {} @@ -220,8 +221,15 @@ class ECONTEXTBase(object): def set_sp(self, value): return setattr(self, self.sp_reg, value) + def get_func_result(self): + return getattr(self, self.func_result_reg) + + def set_func_result(self, value): + return setattr(self, self.func_result_reg, value) + pc = property(get_pc, set_pc, None, "Program Counter register (EIP or RIP)") sp = property(get_sp, set_sp, None, "Stack Pointer register (ESP or RSP)") + func_result = property(get_func_result, set_func_result, None, "Function Resultat register (EAX or RAX)") @property def EEFlags(self): @@ -249,6 +257,7 @@ class ECONTEXT32(ECONTEXTBase, CONTEXT32): default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags') pc_reg = 'Eip' sp_reg = 'Esp' + func_result_reg = 'Eax' fields = [f[0] for f in CONTEXT32._fields_] """The fields of the structure""" @@ -256,6 +265,7 @@ class ECONTEXTWOW64(ECONTEXTBase, WOW64_CONTEXT): default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags') pc_reg = 'Eip' sp_reg = 'Esp' + func_result_reg = 'Eax' fields = [f[0] for f in WOW64_CONTEXT._fields_] """The fields of the structure""" @@ -265,6 +275,7 @@ class ECONTEXT64(ECONTEXTBase, CONTEXT64): 'R8', 'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15', 'EFlags') pc_reg = 'Rip' sp_reg = 'Rsp' + func_result_reg = 'Rax' fields = [f[0] for f in CONTEXT64._fields_] """The fields of the structure"""