diff --git a/qiling/debugger/qdb/qdb.py b/qiling/debugger/qdb/qdb.py index 09f589d1f..a24940ae4 100644 --- a/qiling/debugger/qdb/qdb.py +++ b/qiling/debugger/qdb/qdb.py @@ -5,11 +5,11 @@ import cmd -from typing import Optional, Tuple, Union +from typing import Optional, Tuple, Union, List from contextlib import contextmanager from qiling import Qiling -from qiling.const import QL_OS, QL_ARCH, QL_ENDIAN +from qiling.const import QL_OS, QL_ARCH, QL_ENDIAN, QL_STATE, QL_VERBOSE from qiling.debugger import QlDebugger from .utils import setup_context_render, setup_branch_predictor, setup_address_marker, SnapshotManager, run_qdb_script @@ -25,7 +25,7 @@ class QlQdb(cmd.Cmd, QlDebugger): The built-in debugger of Qiling Framework """ - def __init__(self, ql: Qiling, init_hook: str = "", rr: bool = False, script: str = "") -> None: + def __init__(self, ql: Qiling, init_hook: List[str] = [], rr: bool = False, script: str = "") -> None: """ @init_hook: the entry to be paused at @rr: record/replay debugging @@ -45,9 +45,10 @@ def __init__(self, ql: Qiling, init_hook: str = "", rr: bool = False, script: st super().__init__() - self.dbg_hook(init_hook) + # filter out entry_point of loader if presented + self.dbg_hook(list(filter(lambda d: int(d, 0) != self.ql.loader.entry_point, init_hook))) - def dbg_hook(self, init_hook: str): + def dbg_hook(self, init_hook: List[str]): """ initial hook to prepare everything we need """ @@ -67,7 +68,7 @@ def bp_handler(ql, address, size, bp_list): if bp.hitted: return - qdb_print(QDB_MSG.INFO, f"hit breakpoint at 0x{self.cur_addr:08x}") + qdb_print(QDB_MSG.INFO, f"hit breakpoint at {self.cur_addr:#x}") bp.hitted = True ql.stop() @@ -75,12 +76,6 @@ def bp_handler(ql, address, size, bp_list): self.ql.hook_code(bp_handler, self.bp_list) - if self.ql.os.type == QL_OS.BLOB: - self.ql.loader.entry_point = self.ql.loader.load_address - - elif init_hook and self.ql.loader.entry_point != int(init_hook, 0): - self.do_breakpoint(init_hook) - if self.ql.entry_point: self.cur_addr = self.ql.entry_point else: @@ -88,6 +83,39 @@ def bp_handler(ql, address, size, bp_list): self.init_state = self.ql.save() + # stop emulator once interp. have been done emulating + if addr_elf_entry := getattr(self.ql.loader, 'elf_entry'): + handler = self.ql.hook_address(lambda ql: ql.stop(), addr_elf_entry) + else: + handler = self.ql.hook_address(lambda ql: ql.stop(), self.ql.loader.entry_point) + + # suppress logging temporary + _verbose = self.ql.verbose + self.ql.verbose = QL_VERBOSE.DISABLED + + # init os for integrity of hooks and patches, + self.ql.os.run() + + handler.remove() + + # ignore the memory unmap error for now, due to the MIPS memory layout issue + try: + self.ql.mem.unmap_all() + except: + pass + + self.ql.restore(self.init_state) + + # resotre logging verbose + self.ql.verbose = _verbose + + if self.ql.os.type is QL_OS.BLOB: + self.ql.loader.entry_point = self.ql.loader.load_address + + elif init_hook: + for each_hook in init_hook: + self.do_breakpoint(each_hook) + if self._script: run_qdb_script(self, self._script) else: @@ -121,14 +149,7 @@ def _run(self, address: int = 0, end: int = 0, count: int = 0) -> None: if getattr(self.ql.arch, 'is_thumb', False): address |= 0b1 - # assume we're running PE if on Windows - if self.ql.os.type == QL_OS.WINDOWS: - self.ql.count = count - self.ql.entry_point = address - self.ql.os.run() - - else: - self.ql.emu_start(begin=address, end=end, count=count) + self.ql.emu_start(begin=address, end=end, count=count) @contextmanager def _save(self, reg=True, mem=True, hw=False, fd=False, cpu_context=False, os=False, loader=False): @@ -372,7 +393,6 @@ def do_start(self, *args) -> None: """ if self.ql.arch != QL_ARCH.CORTEX_M: - self.ql.restore(self.init_state) self.do_context() @@ -528,13 +548,29 @@ def do_show_args(self, argc: int = -1): args = reg_args + stk_args qdb_print(QDB_MSG.INFO, f'args: {args}') - def do_show(self, *args) -> None: + def do_show(self, keyword: Optional[str] = None, *args) -> None: """ show some runtime information """ - for info_line in self.ql.mem.get_formatted_mapinfo(): - qdb_print(QDB_MSG.INFO, info_line) + qdb_print(QDB_MSG.INFO, f"Entry point: {self.ql.loader.entry_point:#x}") + + if addr_elf_entry := getattr(self.ql.loader, 'elf_entry'): + qdb_print(QDB_MSG.INFO, f"ELF entry: {addr_elf_entry:#x}") + + info_lines = iter(self.ql.mem.get_formatted_mapinfo()) + + # print filed name first + qdb_print(QDB_MSG.INFO, next(info_lines)) + + # keyword filtering + if keyword: + lines = filter(lambda line: keyword in line, info_lines) + else: + lines = info_lines + + for line in lines: + qdb_print(QDB_MSG.INFO, line) qdb_print(QDB_MSG.INFO, f"Breakpoints: {[hex(addr) for addr in self.bp_list.keys()]}") qdb_print(QDB_MSG.INFO, f"Marked symbol: {[{key:hex(val)} for key,val in self.marker.mark_list]}") diff --git a/qiling/utils.py b/qiling/utils.py index f76e9610b..f4ca25a85 100644 --- a/qiling/utils.py +++ b/qiling/utils.py @@ -350,15 +350,18 @@ def __int_nothrow(v: str, /) -> Optional[int]: return None # qdb init args are independent and may include any combination of: rr enable, init hook and script - for a in args: - if a == 'rr': + arg_init_hook = [] + for arg in args: + if arg == 'rr': kwargs['rr'] = True - elif __int_nothrow(a) is not None: - kwargs['init_hook'] = a + elif __int_nothrow(arg) is not None: + arg_init_hook.append(arg) else: - kwargs['script'] = a + kwargs['script'] = arg + else: + kwargs['init_hook'] = arg_init_hook else: raise QlErrorOutput('Debugger not supported')