|
6 | 6 | import cmd |
7 | 7 |
|
8 | 8 | from typing import Optional, Tuple, Union |
| 9 | +from contextlib import contextmanager |
9 | 10 |
|
10 | 11 | from qiling import Qiling |
11 | | -from qiling.const import QL_OS, QL_ARCH |
| 12 | +from qiling.const import QL_OS, QL_ARCH, QL_ENDIAN |
12 | 13 | from qiling.debugger import QlDebugger |
13 | 14 |
|
14 | 15 | 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: |
119 | 120 |
|
120 | 121 | self.ql.emu_start(begin=address, end=end, count=count) |
121 | 122 |
|
| 123 | + @contextmanager |
| 124 | + def _save(self, reg=True, mem=True, hw=False, fd=False, cpu_context=False, os=False, loader=False): |
| 125 | + """ |
| 126 | + helper function for fetching specific context by emulating instructions |
| 127 | + """ |
| 128 | + saved_states = self.ql.save(reg=reg, mem=mem) |
| 129 | + yield self |
| 130 | + self.ql.restore(saved_states) |
| 131 | + |
122 | 132 | def save_reg_dump(func) -> None: |
123 | 133 | """ |
124 | 134 | decorator function for saving register dump |
@@ -417,6 +427,94 @@ def do_mark(self, args=""): |
417 | 427 |
|
418 | 428 | qdb_print(QDB_MSG.INFO, f"mark symbol '{sym_name}' at address: 0x{loc:08x} ...") |
419 | 429 |
|
| 430 | + @parse_int |
| 431 | + def do_show_args(self, argc: int = -1): |
| 432 | + """ |
| 433 | + show arguments of a function call |
| 434 | + default argc is 2 since we don't know the function definition |
| 435 | + """ |
| 436 | + |
| 437 | + if argc is None: |
| 438 | + argc = -1 |
| 439 | + |
| 440 | + elif argc > 16: |
| 441 | + qdb_print(QDB_MSG.ERROR, 'Maximum argc is 16.') |
| 442 | + return |
| 443 | + |
| 444 | + prophecy = self.predictor.predict() |
| 445 | + if not prophecy.going: |
| 446 | + qdb_print(QDB_MSG.ERROR, 'Not on a braching instruction currently.') |
| 447 | + return |
| 448 | + |
| 449 | + if argc == -1: |
| 450 | + reg_n, stk_n = 2, 0 |
| 451 | + else: |
| 452 | + if argc > 4: |
| 453 | + stk_n = argc - 4 |
| 454 | + elif argc <= 4: |
| 455 | + reg_n, stk_n = argc, 0 |
| 456 | + |
| 457 | + ptr_size = self.ql.arch.pointersize |
| 458 | + |
| 459 | + reg_args = [] |
| 460 | + arch_type = self.ql.arch.type |
| 461 | + if arch_type in (QL_ARCH.MIPS, QL_ARCH.ARM, QL_ARCH.CORTEX_M, QL_ARCH.X8664): |
| 462 | + |
| 463 | + reg_idx = None |
| 464 | + if arch_type == QL_ARCH.MIPS: |
| 465 | + slot_addr = self.cur_addr + ptr_size |
| 466 | + |
| 467 | + op_str = self.predictor.disasm(slot_addr).op_str |
| 468 | + # register may be changed due to dealy slot |
| 469 | + if '$a' in op_str.split(',')[0]: |
| 470 | + dst_reg = op_str.split(',')[0].strip('$') |
| 471 | + reg_idx = int(dst_reg.strip('a')) |
| 472 | + |
| 473 | + # fetch real value by emulating instruction in delay slot |
| 474 | + with self._save() as qdb: |
| 475 | + qdb._run(slot_addr, 0, count=1) |
| 476 | + real_val = self.ql.arch.regs.read(dst_reg) |
| 477 | + |
| 478 | + reg_names = [f'a{d}'for d in range(reg_n)] |
| 479 | + if reg_idx != None: |
| 480 | + reg_names.pop(reg_idx) |
| 481 | + |
| 482 | + elif arch_type in (QL_ARCH.ARM, QL_ARCH.CORTEX_M): |
| 483 | + reg_names = [f'r{d}'for d in range(reg_n)] |
| 484 | + |
| 485 | + elif arch_type == QL_ARCH.X8664: |
| 486 | + reg_names = ('rdi', 'rsi', 'rdx', 'rcx', 'r8', 'r9')[:reg_n] |
| 487 | + |
| 488 | + reg_args = [self.ql.arch.regs.read(reg_name) for reg_name in reg_names] |
| 489 | + if reg_idx != None: |
| 490 | + reg_args.insert(reg_idx, real_val) |
| 491 | + |
| 492 | + reg_args = list(map(hex, reg_args)) |
| 493 | + |
| 494 | + elif arch_type == QL_ARCH.X86: |
| 495 | + stk_n = 2 if argc == -1 else argc |
| 496 | + |
| 497 | + # read arguments on stack |
| 498 | + if stk_n >= 0: |
| 499 | + shadow_n = 0 |
| 500 | + base_offset = self.ql.arch.regs.arch_sp |
| 501 | + |
| 502 | + if arch_type in (QL_ARCH.X86, QL_ARCH.X8664): |
| 503 | + # shadow 1 pointer size for return address |
| 504 | + shadow_n = 1 |
| 505 | + |
| 506 | + elif arch_type == QL_ARCH.MIPS: |
| 507 | + # shadow 4 pointer size for mips |
| 508 | + shadow_n = 4 |
| 509 | + |
| 510 | + base_offset = self.ql.arch.regs.arch_sp + shadow_n * ptr_size |
| 511 | + stk_args = [self.ql.mem.read(base_offset+offset*ptr_size, ptr_size) for offset in range(stk_n)] |
| 512 | + endian = 'little' if self.ql.arch.endian == QL_ENDIAN.EL else 'big' |
| 513 | + stk_args = list(map(hex, map(lambda x: int.from_bytes(x, endian), stk_args))) |
| 514 | + |
| 515 | + args = reg_args + stk_args |
| 516 | + qdb_print(QDB_MSG.INFO, f'args: {args}') |
| 517 | + |
420 | 518 | def do_show(self, *args) -> None: |
421 | 519 | """ |
422 | 520 | show some runtime information |
@@ -473,6 +571,7 @@ def do_EOF(self, *args) -> None: |
473 | 571 | do_r = do_run |
474 | 572 | do_s = do_step_in |
475 | 573 | do_n = do_step_over |
| 574 | + do_a = do_show_args |
476 | 575 | do_j = do_jump |
477 | 576 | do_m = do_mark |
478 | 577 | do_q = do_quit |
|
0 commit comments