|
13 | 13 | from qiling.const import QL_ARCH |
14 | 14 | from qiling.extensions import trace |
15 | 15 | from unicorn import UC_PROT_NONE, UC_PROT_READ, UC_PROT_WRITE, UC_PROT_EXEC, UC_PROT_ALL |
| 16 | +from .callstack import CallStack |
16 | 17 |
|
17 | 18 | if TYPE_CHECKING: |
18 | 19 | from qiling.core import Qiling |
@@ -268,6 +269,42 @@ def dis_nbytes(self, addr: int, size: int) -> List[Instruction]: |
268 | 269 | insts = [Instruction(**dic) for dic in self._cmdj(f"pDj {size} @ {addr}")] |
269 | 270 | return insts |
270 | 271 |
|
| 272 | + def dis_ninsts(self, addr: int, n: int=1) -> List[Instruction]: |
| 273 | + insts = [Instruction(**dic) for dic in self._cmdj(f"pdj {n} @ {addr}")] |
| 274 | + return insts |
| 275 | + |
| 276 | + def _backtrace_fuzzy(self, at: int = None, depth: int = 128) -> Optional[CallStack]: |
| 277 | + '''Fuzzy backtrace, see https://github.com/radareorg/radare2/blob/master/libr/debug/p/native/bt/fuzzy_all.c#L38 |
| 278 | + Args: |
| 279 | + at: address to start walking stack, default to current SP |
| 280 | + depth: limit of stack walking |
| 281 | + Returns: |
| 282 | + List of Frame |
| 283 | + ''' |
| 284 | + sp = at or self.ql.arch.regs.arch_sp |
| 285 | + wordsize = self.ql.arch.bits // 8 |
| 286 | + oldframe = None |
| 287 | + cursp = oldsp = sp |
| 288 | + for i in range(depth): |
| 289 | + addr = self.ql.stack_read(i * wordsize) |
| 290 | + inst = self.dis_ninsts(addr)[0] |
| 291 | + if inst.type.lower() == 'call': |
| 292 | + frame = CallStack(addr=addr, sp=cursp, bp=oldsp, name=self.at(addr), next=oldframe) |
| 293 | + oldframe = frame |
| 294 | + oldsp = cursp |
| 295 | + cursp += wordsize |
| 296 | + return oldframe |
| 297 | + |
| 298 | + def bt(self, target: int | str): |
| 299 | + '''Backtrace when reaching target''' |
| 300 | + def bt_hook(ql: 'Qiling', addr: int, size: int, target): |
| 301 | + if isinstance(target, str): |
| 302 | + target = self.where(target) |
| 303 | + if addr <= target and target <= addr + size: |
| 304 | + callstack = self._backtrace_fuzzy() |
| 305 | + print(callstack) |
| 306 | + self.ql.hook_code(bt_hook, target) |
| 307 | + |
271 | 308 | def disassembler(self, ql: 'Qiling', addr: int, size: int, filt: Pattern[str]=None) -> int: |
272 | 309 | '''A human-friendly monkey patch of QlArchUtils.disassembler powered by r2, can be used for hook_code |
273 | 310 | :param ql: Qiling instance |
|
0 commit comments