diff --git a/doc/source/debug.rst b/doc/source/debug.rst index 6f7fb94..4c5c05a 100644 --- a/doc/source/debug.rst +++ b/doc/source/debug.rst @@ -44,8 +44,6 @@ There is not much documentation for now as the code might change soon. - - :class:`Breakpoint` """"""""""""""""""" @@ -77,8 +75,14 @@ When a breakpoint is hit, its ``trigger`` function is called with the debugger a :special-members: __init__ + .. note:: - MemoryBreakpoint are triggered based on the fault address only (as I don't know a way to get the size of the read/write causing the fault without embeding a disassembler). + MemoryBreakpoint are triggered based on the fault address only (as I don't know a way to get the size of the read/write causing the fault without embedding a disassembler). - This means that a MEMBP at address ``X`` won't be triggered by a write of size 4 at address ``X - 1`` (it's sad I know :( ) \ No newline at end of file + This means that a MEMBP at address ``X`` won't be triggered by a write of size 4 at address ``X - 1``. it's sad I know. + +.. autoclass:: FunctionBP + :members: + :inherited-members: + :special-members: __init__ \ No newline at end of file diff --git a/doc/source/sample.rst b/doc/source/sample.rst index 4488242..eb683da 100644 --- a/doc/source/sample.rst +++ b/doc/source/sample.rst @@ -362,6 +362,9 @@ Ouput:: Ask to load : exiting process +Single stepping +~~~~~~~~~~~~~~~ + .. literalinclude:: ..\..\samples\debugger_membp_singlestep.py Ouput:: @@ -379,8 +382,27 @@ Ouput:: No more single step: exiting +:class:`windows.debug.FunctionBP` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. literalinclude:: ..\..\samples\debug_functionbp.py + +Ouput:: + + NtCreateFile of <\??\C:\Windows\syswow64\en-US\calc.exe.mui>: handle = 0xac + Handle manually found! typename=, name=<\Device\HarddiskVolume2\Windows\SysWOW64\en-US\calc.exe.mui> + + NtCreateFile of <\Device\DeviceApi\CMApi>: handle = 0x108 + Handle manually found! typename=, name=<\Device\DeviceApi> + + NtCreateFile of <\??\C:\Windows\Fonts\staticcache.dat>: handle = 0x154 + Handle manually found! typename=, name=<\Device\HarddiskVolume2\Windows\Fonts\StaticCache.dat> + + Exiting process + .. _sample_local_debugger: + :class:`LocalDebugger` '''''''''''''''''''''' diff --git a/samples/debugger_membp_singlestep.py b/samples/debugger_membp_singlestep.py index 7440d49..7bf80b6 100644 --- a/samples/debugger_membp_singlestep.py +++ b/samples/debugger_membp_singlestep.py @@ -37,7 +37,6 @@ class SingleStepOnWrite(windows.debug.MemoryBreakpoint): """Check that BP/dbg can trigger single step and that instruction follows""" def trigger(self, dbg, exc): fault_addr = exc.ExceptionRecord.ExceptionInformation[1] - import pdb;pdb.set_trace() eip = dbg.current_thread.context.pc print("Instruction at <{0:#x}> wrote at <{1:#x}>".format(eip, fault_addr)) dbg.single_step_counter = 4 diff --git a/windows/com.py b/windows/com.py index 6829558..ca54c4e 100644 --- a/windows/com.py +++ b/windows/com.py @@ -12,6 +12,8 @@ from windows.generated_def import interfaces from windows.generated_def.interfaces import generate_IID, IID +# Simple raw -> UUID +# "-".join("{:02X}".format(c) for c in struct.unpack(" Python) def create_c_callable(func, types, keepalive=[]): diff --git a/windows/debug/breakpoints.py b/windows/debug/breakpoints.py index 4455450..b4ed3cb 100644 --- a/windows/debug/breakpoints.py +++ b/windows/debug/breakpoints.py @@ -73,9 +73,11 @@ class X64ArgumentRetriever(object): 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) +class FunctionParamDumpBP(Breakpoint): + def __init__(self, target, addr=None): + if addr is None: + addr = "{0}!{1}".format(target.target_dll, target.target_func) + super(FunctionParamDumpBP, self).__init__(addr) self.target = target self.target_args = target.prototype._argtypes_ @@ -116,6 +118,7 @@ class ParamDumpBP(Breakpoint): return res def extract_arguments(self, cproc, cthread): + """Extracts the functions parameters in an :class:`OrderedDict`""" if windows.current_process.bitness == 32: return self.extract_arguments_32bits(cproc, cthread) if cproc.bitness == 64: @@ -137,10 +140,20 @@ class FunctionRetBP(Breakpoint): class FunctionCallBP(Breakpoint): - def trigger(self, dbg, exception): + def break_on_ret(self, dbg, exception): + """Setup a breakpoint at the return address of the function, this breakpoint will call :func:`ret_trigger`""" 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 + """Called at the return of the function if :func:`break_on_ret` was called""" + raise NotImplementedError("ret_trigger") + + +class FunctionBP(FunctionCallBP, FunctionParamDumpBP): + """A breakpoint that accepts a function from :mod:`windows.winproxy` and able to: + + - Extract the arguments of the functions + - Break at the return of the function + """ \ No newline at end of file diff --git a/windows/debug/debugger.py b/windows/debug/debugger.py index 4f8aabb..7fa9b06 100644 --- a/windows/debug/debugger.py +++ b/windows/debug/debugger.py @@ -83,6 +83,7 @@ class Debugger(object): return cls(target) def detach(self, target=None): + """Detach from all debugged processes or process ``target``""" if target is None: for proc in self.processes.values(): self.detach(proc) @@ -116,7 +117,7 @@ class Debugger(object): windows.winproxy.DebugActiveProcessStop(target.pid) def _killed_in_action(self): - """Return True if current process have been detached by user callback""" + """Return ``True`` if current process have been detached by user callback""" return self.current_process.pid not in self.processes @@ -385,7 +386,7 @@ class Debugger(object): def _setup_pending_breakpoints_load_dll(self, dll_name): for bp in self._pending_breakpoints_new[None]: if isinstance(bp.addr, basestring): - target_dll = bp.addr.split("!")[0] + target_dll = bp.addr.lower().split("!")[0] if target_dll == dll_name: _setup_method = getattr(self, "_setup_breakpoint_" + bp.type) if bp.apply_to_target(self.current_process): @@ -710,7 +711,7 @@ class Debugger(object): ## Public API def loop(self): - """Debugging loop: handle event / dispatch to breakpoint. Returns when all targets are dead""" + """Debugging loop: handle event / dispatch to breakpoint. Returns when all targets are dead/detached""" for debug_event in self._debug_event_generator(): self.REMOVE_ME_debug_event = debug_event dbg_continue_flag = self._dispatch_debug_event(debug_event) @@ -861,7 +862,7 @@ class Debugger(object): return DBG_CONTINUE def on_single_step(self, exception): - """Called on requested single step``exception`` is one of the following type: + """Called on requested single step ``exception`` is one of the following type: * :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO32` * :class:`windows.winobject.exception.EEXCEPTION_DEBUG_INFO64` diff --git a/windows/debug/localdbg.py b/windows/debug/localdbg.py index ef443a8..7c7fe18 100644 --- a/windows/debug/localdbg.py +++ b/windows/debug/localdbg.py @@ -110,14 +110,14 @@ class LocalDebugger(object): del self.breakpoints[bp.addr] return if bp.type == HARDWARE_EXEC_BP: + threads_by_tid = {t.tid: t for t in windows.current_process.threads} for tid in self._hxbp_breakpoint: if bp.addr in self._hxbp_breakpoint[tid] and self._hxbp_breakpoint[tid][bp.addr] == bp: if tid == windows.current_thread.tid: self.remove_hxbp_self_thread(bp.addr) else: - self.remove_hxbp_other_thread(bp.addr) + self.remove_hxbp_other_thread(bp.addr, threads_by_tid[tid]) del self._hxbp_breakpoint[tid][bp.addr] - #print("Need to remove {0} in {1}".format(self._hxbp_breakpoint[tid][bp.addr], tid)) return raise NotImplementedError("Unknow BP type {0}".format(bp.type)) diff --git a/windows/winobject/wmi.py b/windows/winobject/wmi.py index 9f72573..3c6d644 100644 --- a/windows/winobject/wmi.py +++ b/windows/winobject/wmi.py @@ -14,20 +14,21 @@ class WmiRequester(object): r"""An object to perform wmi request to ``root\cimv2``""" INSTANCE = None - def __new__(cls): + def __new__(cls, *args, **kwargs): if cls.INSTANCE is not None: return cls.INSTANCE - cls.INSTANCE = super(cls, cls).__new__(cls) + cls.INSTANCE = super(cls, cls).__new__(cls, *args, **kwargs) return cls.INSTANCE - def __init__(self): + def __init__(self, target="root\\cimv2", user=None, password=None): locator = IWbemLocator() service = IWbemServices() - CLSID_WbemAdministrativeLocator_IID = windows.com.IID.from_string('CB8555CC-9128-11D1-AD9B-00C04FD8FDFF') + #CLSID_WbemAdministrativeLocator_IID = windows.com.IID.from_string('CB8555CC-9128-11D1-AD9B-00C04FD8FDFF') + WbemLocator_CLSID = windows.com.IID.from_string('4590F811-1D3A-11D0-891F-00AA004B2E24') windows.com.init() - windows.com.create_instance(CLSID_WbemAdministrativeLocator_IID, locator) - locator.ConnectServer("root\\cimv2", None, None , None, 0x80, None, None, ctypes.byref(service)) + windows.com.create_instance(WbemLocator_CLSID, locator) + locator.ConnectServer(target, user, password , None, 0x80, None, None, ctypes.byref(service)) self.service = service def select(self, frm, attrs="*"): diff --git a/windows/winproxy.py b/windows/winproxy.py index 04a834a..d05dae1 100644 --- a/windows/winproxy.py +++ b/windows/winproxy.py @@ -114,6 +114,8 @@ class ApiProxy(object): python_proxy.prototype = prototype python_proxy.params = params python_proxy.errcheck = self.error_check + python_proxy.target_dll = self.APIDLL + python_proxy.target_func = self.func_name params_name = [param[1] for param in params] if (self.error_check.__doc__): doc = python_proxy.__doc__