diff --git a/qiling/debugger/qdb/branch_predictor/branch_predictor_x8664.py b/qiling/debugger/qdb/branch_predictor/branch_predictor_x8664.py index f50dad11a..eda3a5923 100644 --- a/qiling/debugger/qdb/branch_predictor/branch_predictor_x8664.py +++ b/qiling/debugger/qdb/branch_predictor/branch_predictor_x8664.py @@ -98,7 +98,7 @@ def predict(self): prophecy.going = jump_reg_table.get(line.mnemonic)(self.ql.arch.regs.ecx) if prophecy.going: - takeaway_list = ["ptr", "dword", "[", "]"] + takeaway_list = ["ptr", "dword", "qword", "[", "]"] if len(line.op_str.split()) > 1: new_line = line.op_str.replace(":", "+") @@ -114,7 +114,6 @@ def predict(self): if each_reg in new_line: new_line = re.sub(each_reg, hex(self.read_reg(each_reg)), new_line) - prophecy.where = check_and_eval(new_line) elif line.op_str in self.ql.arch.regs.register_mapping: diff --git a/qiling/debugger/qdb/qdb.py b/qiling/debugger/qdb/qdb.py index 4914af6ed..be572a004 100644 --- a/qiling/debugger/qdb/qdb.py +++ b/qiling/debugger/qdb/qdb.py @@ -6,9 +6,10 @@ import cmd from typing import Optional, Tuple, Union +from contextlib import contextmanager from qiling import Qiling -from qiling.const import QL_OS, QL_ARCH +from qiling.const import QL_OS, QL_ARCH, QL_ENDIAN from qiling.debugger import QlDebugger from .utils import setup_context_render, setup_branch_predictor, setup_address_marker, SnapshotManager, run_qdb_script @@ -119,6 +120,15 @@ def _run(self, address: int = 0, end: int = 0, count: int = 0) -> None: 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): + """ + helper function for fetching specific context by emulating instructions + """ + saved_states = self.ql.save(reg=reg, mem=mem) + yield self + self.ql.restore(saved_states) + def save_reg_dump(func) -> None: """ decorator function for saving register dump @@ -417,6 +427,94 @@ def do_mark(self, args=""): qdb_print(QDB_MSG.INFO, f"mark symbol '{sym_name}' at address: 0x{loc:08x} ...") + @parse_int + def do_show_args(self, argc: int = -1): + """ + show arguments of a function call + default argc is 2 since we don't know the function definition + """ + + if argc is None: + argc = -1 + + elif argc > 16: + qdb_print(QDB_MSG.ERROR, 'Maximum argc is 16.') + return + + prophecy = self.predictor.predict() + if not prophecy.going: + qdb_print(QDB_MSG.ERROR, 'Not on a braching instruction currently.') + return + + if argc == -1: + reg_n, stk_n = 2, 0 + else: + if argc > 4: + stk_n = argc - 4 + elif argc <= 4: + reg_n, stk_n = argc, 0 + + ptr_size = self.ql.arch.pointersize + + reg_args = [] + arch_type = self.ql.arch.type + if arch_type in (QL_ARCH.MIPS, QL_ARCH.ARM, QL_ARCH.CORTEX_M, QL_ARCH.X8664): + + reg_idx = None + if arch_type == QL_ARCH.MIPS: + slot_addr = self.cur_addr + ptr_size + + op_str = self.predictor.disasm(slot_addr).op_str + # register may be changed due to dealy slot + if '$a' in op_str.split(',')[0]: + dst_reg = op_str.split(',')[0].strip('$') + reg_idx = int(dst_reg.strip('a')) + + # fetch real value by emulating instruction in delay slot + with self._save() as qdb: + qdb._run(slot_addr, 0, count=1) + real_val = self.ql.arch.regs.read(dst_reg) + + reg_names = [f'a{d}'for d in range(reg_n)] + if reg_idx != None: + reg_names.pop(reg_idx) + + elif arch_type in (QL_ARCH.ARM, QL_ARCH.CORTEX_M): + reg_names = [f'r{d}'for d in range(reg_n)] + + elif arch_type == QL_ARCH.X8664: + reg_names = ('rdi', 'rsi', 'rdx', 'rcx', 'r8', 'r9')[:reg_n] + + reg_args = [self.ql.arch.regs.read(reg_name) for reg_name in reg_names] + if reg_idx != None: + reg_args.insert(reg_idx, real_val) + + reg_args = list(map(hex, reg_args)) + + elif arch_type == QL_ARCH.X86: + stk_n = 2 if argc == -1 else argc + + # read arguments on stack + if stk_n >= 0: + shadow_n = 0 + base_offset = self.ql.arch.regs.arch_sp + + if arch_type in (QL_ARCH.X86, QL_ARCH.X8664): + # shadow 1 pointer size for return address + shadow_n = 1 + + elif arch_type == QL_ARCH.MIPS: + # shadow 4 pointer size for mips + shadow_n = 4 + + base_offset = self.ql.arch.regs.arch_sp + shadow_n * ptr_size + stk_args = [self.ql.mem.read(base_offset+offset*ptr_size, ptr_size) for offset in range(stk_n)] + endian = 'little' if self.ql.arch.endian == QL_ENDIAN.EL else 'big' + stk_args = list(map(hex, map(lambda x: int.from_bytes(x, endian), stk_args))) + + args = reg_args + stk_args + qdb_print(QDB_MSG.INFO, f'args: {args}') + def do_show(self, *args) -> None: """ show some runtime information @@ -473,6 +571,7 @@ def do_EOF(self, *args) -> None: do_r = do_run do_s = do_step_in do_n = do_step_over + do_a = do_show_args do_j = do_jump do_m = do_mark do_q = do_quit