diff --git a/scripts/r2sptrace.py b/scripts/r2sptrace.py new file mode 100644 index 0000000000..93d6f68092 --- /dev/null +++ b/scripts/r2sptrace.py @@ -0,0 +1,54 @@ +# https://gist.github.com/thestr4ng3r/403fecffc081a899618b1500ba6d1156 +# https://github.com/radareorg/radare2/issues/12070 + +import r2pipe + +r = r2pipe.open() +ptr_size = int(r.cmd("e asm.bits").strip()) // 8 + +addr_stackptr = {} + +blocks = r.cmdj("afbj") + +def sp_delta_for_fcn_call(fcn): + cc = fcn["calltype"] + if cc == "stdcall": + nargs = fcn["nargs"] + return -nargs * ptr_size + return 0 + +def sp_delta_for_instr(instr): + if instr["type"] == "call" or instr["type"] == "ucall": + if "jump" in instr: + call_fcn = r.cmdj("afij@{}".format(instr["jump"])) + if len(call_fcn) > 0: + return sp_delta_for_fcn_call(call_fcn[0]) + else: + print("WARNING: Target not known for jump at {:#x}!".format(instr["addr"])) + return 0 if "stackptr" not in instr else instr["stackptr"] + +def block_at(addr): + for block in blocks: + if block["addr"] == addr: + return block + return None + +def trace_block(block, stackptr): + instrs = r.cmdj("aoj {}@{}".format(block["ninstr"], block["addr"])) + for instr in instrs: + addr = instr["addr"] + if addr in addr_stackptr: + if addr_stackptr[addr] != stackptr: + print("WARNING: mismatch at {:#x}: {} != {}".format(addr, stackptr, addr_stackptr[addr])) + return + addr_stackptr[addr] = stackptr + stackptr -= sp_delta_for_instr(instr) + if "jump" in block: + trace_block(block_at(block["jump"]), stackptr) + if "fail" in block: + trace_block(block_at(block["fail"]), stackptr) + +trace_block(blocks[0], 0) + +for addr, stackptr in addr_stackptr.items(): + r.cmd("CC sp: {}@{}".format(stackptr, addr))