diff --git a/samples/local_debugger.py b/samples/local_debugger.py new file mode 100644 index 0000000..82f948c --- /dev/null +++ b/samples/local_debugger.py @@ -0,0 +1,50 @@ +import ctypes +import windows +import windows.debug +from windows.generated_def.winstructs import * + +ct = windows.current_thread +t = [t for t in windows.current_process.threads if t.tid == ct.tid][0] + + + +class YoloDebugger(windows.debug.LocalDebugger): + def __init__(self, single_step_count): + super(YoloDebugger, self).__init__() + self.single_step_count = single_step_count + + def on_exception(self, exc): + code = self.get_exception_code() + context = self.get_exception_context() + print("EXCEPTION !!!! Got a {0} at 0x{1:x}".format(code, context.pc)) + if self.single_step_count: + self.single_step_count -= 1 + return self.single_step() + + +class YoloHXBP(windows.debug.HXBreakpoint): + def trigger(self, dbg, exc): + context = dbg.get_exception_context() + print("GOT AN HXBP <3 at 0x{0:x}".format(context.pc)) + windows.current_process.write_memory(self.addr, "\x90\x90") + return dbg.single_step() + +print("Your main thread is {0}".format(windows.current_thread.tid)) + + +d = YoloDebugger(5) +# Infinite loop + nop + ret + +addr = windows.native_exec.native_function.allocator.write_code("\xeb\xfe\x90\x90\x90\x90\xc3") +func_type = ctypes.CFUNCTYPE(PVOID) +func = func_type(addr) + +print("Code addr = 0x{0:x}".format(addr)) + +t = windows.current_process.create_thread(addr, 0) + +d.add_bp(YoloHXBP(addr)) + +t.wait() + + diff --git a/samples/local_debugger_remote_process.py b/samples/local_debugger_remote_process.py new file mode 100644 index 0000000..43d897b --- /dev/null +++ b/samples/local_debugger_remote_process.py @@ -0,0 +1,40 @@ +import ctypes +import windows +import windows.test + +from windows.generated_def.winstructs import * + +remote_code = """ +import windows +from windows.generated_def.winstructs import * + +windows.utils.create_console() + +class YOLOHXBP(windows.debug.HXBreakpoint): + def trigger(self, dbg, exc): + p = windows.current_process + arg_pos = 2 + context = dbg.get_exception_context() + esp = context.Esp + unicode_string_addr = p.read_ptr(esp + (arg_pos + 1) * 4) + wstring_addr = p.read_ptr(unicode_string_addr + 4) + dll_loaded = p.read_wstring(wstring_addr) + print("I AM LOADING <{0}>".format(dll_loaded)) + +d = windows.debug.LocalDebugger() + +exp = windows.current_process.peb.modules[1].pe.exports +#windows.utils.FixedInteractiveConsole(locals()).interact() +ldr = exp["LdrLoadDll"] +d.add_bp(YOLOHXBP(ldr)) +print("By from {0}".format(windows.current_thread.tid)) + +""" + +c = windows.test.pop_calc_32(dwCreationFlags=CREATE_SUSPENDED) +c.execute_python(remote_code) +c.threads[0].resume() + +import time +time.sleep(2) +c.exit() diff --git a/windows/debug.py b/windows/debug.py index efe2163..3afbc21 100644 --- a/windows/debug.py +++ b/windows/debug.py @@ -423,8 +423,14 @@ class Debugger(object): # Public callback def on_exception(self, exception): - """Called on exception event other that known breakpoint""" - pass + """Called on exception event other that known breakpoint + + The default behaviour is to return ``DBG_CONTINUE`` for the known exception code + and ``DBG_EXCEPTION_NOT_HANDLED`` else + """ + if not exception.ExceptionRecord.ExceptionCode in windows.exception.exception_name_by_value: + return DBG_EXCEPTION_NOT_HANDLED + return DBG_CONTINUE def on_create_process(self, create_process): """Called on create_process event""" @@ -499,10 +505,16 @@ class LocalDebugger(object): self.breakpoints = {} self._memory_save = {} self._reput_breakpoint = {} + self._hxbp_breakpoint = defaultdict(dict) self.callback_vectored = VectoredException(self.callback) windows.winproxy.AddVectoredExceptionHandler(0, self.callback_vectored) + self.setup_hxbp_callback_vectored = VectoredException(self.setup_hxbp_callback) + self.hxbp_info = None + + self.code = windows.native_exec.create_function("\xcc\xc3", [PVOID]) + def get_exception_code(self): return self.current_exception[0].ExceptionRecord[0].ExceptionCode @@ -522,10 +534,11 @@ class LocalDebugger(object): def callback(self, exc): self.current_exception = exc exp_code = self.get_exception_code() - exp_addr = self.get_exception_context().get_pc() + context = self.get_exception_context() + exp_addr = context.pc if exp_code == EXCEPTION_BREAKPOINT and exp_addr in self.breakpoints: - continue_value = self.breakpoints[exp_addr].trigger(self, exc) + res = self.breakpoints[exp_addr].trigger(self, exc) single_step = self.get_exception_context().EEFlags.TF # single step activated by breakpoint return self._pass_breakpoint(exp_addr, single_step) @@ -538,17 +551,90 @@ class LocalDebugger(object): if single_step: return self.on_exception(exc) return windef.EXCEPTION_CONTINUE_EXECUTION - return self.on_exception(exc) + elif exp_code == EXCEPTION_SINGLE_STEP and exp_addr in self._hxbp_breakpoint[windows.current_thread.tid]: + res = self._hxbp_breakpoint[windows.current_thread.tid][exp_addr].trigger(self, exc) + context.EEFlags.RF = 1 + return EXCEPTION_CONTINUE_EXECUTION + res = self.on_exception(exc) + return EXCEPTION_CONTINUE_EXECUTION def on_exception(self, exc): + if not self.get_exception_code() in windows.exception.exception_name_by_value: + return windef.EXCEPTION_CONTINUE_SEARCH return windef.EXCEPTION_CONTINUE_EXECUTION - def add_bp(self, bp): + def add_bp(self, bp, targets=None): + if bp.type == HARDWARE_EXEC_BP: + return self.add_bp_hxbp(bp, targets) if bp.type != STANDARD_BP: - raise NotImplementedError("Add non standard-BP in LocalKernelDebugger") + raise NotImplementedError("Unknow BP type {0}".format(bp.type)) + if targets is not None: + raise ValueError("LocalDebugger: STANDARD_BP doest not support targets {0}".format(targets)) self.breakpoints[bp.addr] = bp self._memory_save[bp.addr] = windows.current_process.read_memory(bp.addr, 1) - with windows.utils.VirtualProtected(bp.addr, 1, PAGE_EXECUTE_READWRITE): windows.current_process.write_memory(bp.addr, "\xcc") - return \ No newline at end of file + return + + def add_bp_hxbp(self, bp, targets=None): + if bp.type != HARDWARE_EXEC_BP: + raise NotImplementedError("Add non standard-BP in LocalDebugger") + if targets is None: + targets = windows.current_process.threads + for thread in targets: + if thread.owner.pid != windows.current_process.pid: + raise ValueError("Cannot add HXBP to target in remote process {0}".format(thread)) + if thread.tid == windows.current_thread.tid: + self.setup_hxbp_self_thread(bp.addr) + else: + self.setup_hxbp_other_thread(bp.addr, thread) + self._hxbp_breakpoint[thread.tid][bp.addr] = bp + + def setup_hxbp_callback(self, exc): + self.current_exception = exc + + exp_code = self.get_exception_code() + context = self.get_exception_context() + exp_addr = context.pc + + hxbp_used = self.setup_hxbp_in_context(context, self.data) + + windows.current_process.write_memory(exp_addr, "\x90") + # Raising in the VEH is a bad idea.. + # So better give the information to triggerer.. + if hxbp_used is not None: + self.get_exception_context().Eax = exp_addr + else: + self.get_exception_context().Eax = 0 + return windef.EXCEPTION_CONTINUE_EXECUTION + + + def setup_hxbp_in_context(self, context, addr): + for i in range(4): + is_used = getattr(context.EDr7, "L" + str(i)) + empty_drx = str(i) + if not is_used: + context.EDr7.GE = 1 + context.EDr7.LE = 1 + setattr(context.EDr7, "L" + empty_drx, 1) + setattr(context, "Dr" + empty_drx, addr) + return i + return None + + def setup_hxbp_self_thread(self, addr): + self.data = addr + with windows.exception.VectoredExceptionHandler(1, self.setup_hxbp_callback): + x = self.code() + if x is None: + raise ValueError("Could not setup HXBP") + windows.current_process.write_memory(x, "\xcc") + return + + def setup_hxbp_other_thread(self, addr, thread): + thread.suspend() + ctx = thread.context + x = self.setup_hxbp_in_context(ctx, addr) + if x is None: + raise ValueError("Could not setup HXBP in {0}".format(thread)) + thread.set_context(ctx) + thread.resume() \ No newline at end of file diff --git a/windows/exception.py b/windows/exception.py index a5b5299..6e202cd 100644 --- a/windows/exception.py +++ b/windows/exception.py @@ -316,19 +316,20 @@ class VectoredException(object): return windef.EXCEPTION_CONTINUE_SEARCH -class WithExceptionHandler(object): - def __init__(self, handler): +class VectoredExceptionHandler(object): + def __init__(self, pos, handler): self.handler = VectoredException(handler) + self.pos = pos def __enter__(self): - self.value = windows.winproxy.AddVectoredExceptionHandler(0, self.handler) + self.value = windows.winproxy.AddVectoredExceptionHandler(self.pos, self.handler) return self def __exit__(self, exc_type, exc_value, traceback): windows.winproxy.RemoveVectoredExceptionHandler(self.value) return False -class DumpContextOnException(WithExceptionHandler): +class DumpContextOnException(VectoredExceptionHandler): def __init__(self, exit=False): self.exit = exit super(DumpContextOnException, self).__init__(self.print_context_result) diff --git a/windows/winobject.py b/windows/winobject.py index 8ba4904..b6b369f 100644 --- a/windows/winobject.py +++ b/windows/winobject.py @@ -475,7 +475,7 @@ class Process(AutoHandle): class CurrentThread(AutoHandle): """The current thread""" - @utils.fixedpropety + @property #It's not a fixedpropety because executing thread might change def tid(self): """Thread ID