diff --git a/qiling/debugger/qdb/arch/arch.py b/qiling/debugger/qdb/arch/arch.py index f84bdcd74..cbe6489a7 100644 --- a/qiling/debugger/qdb/arch/arch.py +++ b/qiling/debugger/qdb/arch/arch.py @@ -5,6 +5,9 @@ from qiling.const import QL_ARCH +from unicorn import UC_ERR_READ_UNMAPPED +import unicorn + class Arch: """ @@ -23,4 +26,9 @@ def archbit(self): return 4 def read_insn(self, address: int): - return self.read_mem(address, self.arch_insn_size) + try: + result = self.read_mem(address, self.arch_insn_size) + except unicorn.unicorn.UcError as err: + result = None + + return result diff --git a/qiling/debugger/qdb/context.py b/qiling/debugger/qdb/context.py index cd9fbc210..a3c71ef86 100644 --- a/qiling/debugger/qdb/context.py +++ b/qiling/debugger/qdb/context.py @@ -48,7 +48,9 @@ def disasm(self, address: int, detail: bool = False) -> Optional[CsInsn]: md = self.ql.arch.disassembler md.detail = detail - return next(md.disasm(self.read_insn(address), address), None) + if (addr := self.read_insn(address)): + return next(md.disasm(addr, address), None) + return None def try_read(self, address: int, size: int) -> Optional[bytes]: """ diff --git a/qiling/debugger/qdb/misc.py b/qiling/debugger/qdb/misc.py index bfff5aa8e..d9bf67362 100644 --- a/qiling/debugger/qdb/misc.py +++ b/qiling/debugger/qdb/misc.py @@ -3,7 +3,7 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # -from typing import Callable, Optional +from typing import AnyStr, Callable, Optional import ast @@ -50,17 +50,27 @@ def read_int(s: str) -> int: return int(s, 0) +def try_read_int(s: AnyStr) -> Optional[int]: + """ + try to read string as integer is possible + """ + try: + ret = read_int(s) + except: + ret = None + + return ret + + def parse_int(func: Callable) -> Callable: """ function dectorator for parsing argument as integer """ def wrap(qdb, s: str = "") -> int: assert type(s) is str - try: - ret = read_int(s) - except: - ret = None + ret = try_read_int(s) return func(qdb, ret) + return wrap diff --git a/qiling/debugger/qdb/qdb.py b/qiling/debugger/qdb/qdb.py index d8810ff29..4dd516e7c 100644 --- a/qiling/debugger/qdb/qdb.py +++ b/qiling/debugger/qdb/qdb.py @@ -8,12 +8,12 @@ import cmd from qiling import Qiling -from qiling.const import QL_ARCH, QL_VERBOSE +from qiling.const import QL_OS, QL_ARCH, QL_VERBOSE from qiling.debugger import QlDebugger -from .utils import setup_context_render, setup_branch_predictor, SnapshotManager, run_qdb_script +from .utils import setup_context_render, setup_branch_predictor, setup_address_marker, SnapshotManager, run_qdb_script from .memory import setup_memory_Manager -from .misc import parse_int, Breakpoint, TempBreakpoint +from .misc import parse_int, Breakpoint, TempBreakpoint, try_read_int from .const import color from .utils import QDB_MSG, qdb_print @@ -34,6 +34,7 @@ def __init__(self, ql: Qiling, init_hook: str = "", rr: bool = False, script: st self._saved_reg_dump = None self._script = script self.bp_list = {} + self.marker = setup_address_marker() self.rr = SnapshotManager(ql) if rr else None self.mm = setup_memory_Manager(ql) @@ -72,7 +73,10 @@ def bp_handler(ql, address, size, bp_list): self.ql.hook_code(bp_handler, self.bp_list) - if init_hook and self.ql.loader.entry_point != init_hook: + 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 != init_hook: self.do_breakpoint(init_hook) self.cur_addr = self.ql.loader.entry_point @@ -319,6 +323,7 @@ def do_disassemble(self, address: Optional[int] = None) -> None: qdb_print(QDB_MSG.ERROR) def do_examine(self, line: str) -> None: + """ Examine memory: x/FMT ADDRESS. format letter: o(octal), x(hex), d(decimal), u(unsigned decimal), t(binary), f(float), a(address), i(instruction), c(char), s(string) and z(hex, zero padded on the left) @@ -328,6 +333,29 @@ def do_examine(self, line: str) -> None: if type(err_msg := self.mm.parse(line)) is str: qdb_print(QDB_MSG.ERROR, err_msg) + + + def do_set(self, line: str) -> None: + """ + set register value of current context + """ + # set $a = b + + reg, val = line.split("=") + reg_name = reg.strip().strip("$") + reg_val = try_read_int(val.strip()) + + if reg_name in self.ql.arch.regs.save().keys(): + if reg_val: + setattr(self.ql.arch.regs, reg_name, reg_val) + self.do_context() + qdb_print(QDB_MSG.INFO, f"set register {reg_name} to 0x{(reg_val & 0xfffffff):08x}") + + else: + qdb_print(QDB_MSG.ERROR, f"error parsing input: {reg_val} as integer value") + + else: + qdb_print(QDB_MSG.ERROR, f"invalid register: {reg_name}") def do_start(self, *args) -> None: """ @@ -348,6 +376,61 @@ def do_context(self, *args) -> None: self.render.context_stack() self.render.context_asm() + def do_jump(self, loc: str, *args) -> None: + """ + seek to where ever valid location you want + """ + + sym = self.marker.get_symbol(loc) + addr = sym if sym is not None else try_read_int(loc) + + # check validation of the address to be seeked + if self.ql.mem.is_mapped(addr, 4): + if sym: + qdb_print(QDB_MSG.INFO, f"seek to {loc} @ 0x{addr:08x} ...") + else: + qdb_print(QDB_MSG.INFO, f"seek to 0x{addr:08x} ...") + + self.cur_addr = addr + self.do_context() + + else: + qdb_print(QDB_MSG.ERROR, f"the address to be seeked isn't mapped") + + def do_mark(self, args=""): + """ + mark a user specified address as a symbol + """ + + args = args.split() + if len(args) == 0: + loc = self.cur_addr + sym_name = self.marker.mark_only_loc(loc) + + elif len(args) == 1: + if (loc := try_read_int(args[0])): + sym_name = self.marker.mark_only_loc(loc) + + else: + loc = self.cur_addr + sym_name = args[0] + if (err := self.marker.mark(sym_name, loc)): + qdb_print(QDB_MSG.ERROR, err) + return + + elif len(args) == 2: + sym_name, addr = args + if (loc := try_read_int(addr)): + self.marker.mark(sym_name, loc) + else: + qdb_print(QDB_MSG.ERROR, f"unable to mark symbol at address: '{addr}'") + return + else: + qdb_print(QDB_MSG.ERROR, "symbol should not be empty ...") + return + + qdb_print(QDB_MSG.INFO, f"mark symbol '{sym_name}' at address: 0x{loc:08x} ...") + def do_show(self, *args) -> None: """ show some runtime information @@ -357,6 +440,7 @@ def do_show(self, *args) -> None: self.ql.log.info(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]}") if self.rr: qdb_print(QDB_MSG.INFO, f"Snapshots: {len([st for st in self.rr.layers if isinstance(st, self.rr.DiffedState)])}") @@ -403,6 +487,8 @@ def do_EOF(self, *args) -> None: do_r = do_run do_s = do_step_in do_n = do_step_over + do_j = do_jump + do_m = do_mark do_q = do_quit do_x = do_examine do_p = do_backward diff --git a/qiling/debugger/qdb/utils.py b/qiling/debugger/qdb/utils.py index fdbde9d85..ec960c435 100644 --- a/qiling/debugger/qdb/utils.py +++ b/qiling/debugger/qdb/utils.py @@ -12,7 +12,6 @@ from qiling.const import QL_ARCH from .context import Context -from .misc import read_int from .render import ( ContextRenderX86, @@ -50,6 +49,65 @@ def print_info(msg): print(color_coated) +""" + + class Marker provide the ability for marking an address as a more easier rememberable alias + +""" + +def setup_address_marker(): + + class Marker: + def __init__(self): + self._mark_list = {} + + def get_symbol(self, sym): + """ + get the mapped address to a symbol if it's in the mark_list + """ + + return self._mark_list.get(sym, None) + + @property + def mark_list(self): + """ + get a list about what we marked + """ + + return self._mark_list.items() + + def gen_sym_name(self): + """ + generating symbol name automatically + """ + + sym_name, idx = "sym0", 0 + while sym_name in self._mark_list: + idx += 1 + sym_name = f"sym{idx}" + + return sym_name + + def mark_only_loc(self, loc): + """ + mark when location provided only + """ + + sym_name = self.gen_sym_name() + self.mark(sym_name, loc) + return sym_name + + def mark(self, sym: str, loc: int): + """ + mark loc as sym + """ + + if sym not in self.mark_list: + self._mark_list.update({sym: loc}) + else: + return f"dumplicated symbol name: {sym} at address: 0x{loc:08x}" + + return Marker() """