diff options
-rw-r--r-- | sploit/rev/elf.py | 11 | ||||
-rw-r--r-- | sploit/rev/r2.py | 82 |
2 files changed, 61 insertions, 32 deletions
diff --git a/sploit/rev/elf.py b/sploit/rev/elf.py index 78f4e47..d173897 100644 --- a/sploit/rev/elf.py +++ b/sploit/rev/elf.py @@ -44,11 +44,8 @@ class ELF: def retaddr(self, caller, callee): return [c.ret_addr for c in r2.get_call_returns(self.path, caller, callee)] - def retgad(self): - return r2.ret_gadget(self.path) + def gadgets(self, *regexes, cont=False): + return r2.rop_gadgets(self.path, *regexes, cont=cont) - def gad(self, gad): - return [g.addr for g in r2.rop_gadget(self.path, gad)] - - def egad(self, gad): - return r2.rop_gadget_exact(self.path, gad).addr + def gadget(self, *regexes): + return r2.rop_gadget(self.path, *regexes) diff --git a/sploit/rev/r2.py b/sploit/rev/r2.py index 7fe57d8..f2650da 100644 --- a/sploit/rev/r2.py +++ b/sploit/rev/r2.py @@ -1,10 +1,13 @@ from sploit.arch import arch from sploit.log import ilog +from sploit.rev.gadget import Gadget from sploit.symtbl import Symtbl from sploit.util import run_cmd_cached -import re from collections import namedtuple as nt +from functools import cache +import json +import re def run_cmd(binary,cmd): return run_cmd_cached(['r2','-q','-c',cmd,'-e','scr.color=false',binary]) @@ -56,30 +59,59 @@ def get_locals(binary,func): out = {var[1]:-(int(var[0],0)-arch.wordsize) for var in out} return Symtbl(sbp=0, **out) -def ret_gadget(binary): - ilog(f'Searching for a ret gadget in {binary} with r2...') - - cmd_ret = '/R/ ret~ret' - out = run_cmd(binary,cmd_ret) - out = out[0] - out = re.split(r'\s+',out) - out = out[1] - return int(out,0) - -def rop_gadget(binary,gad): - ilog(f'Searching for "{gad}" gadgets in {binary} with r2...') - - cmd_gad = f'"/R/q {gad}"' - out = run_cmd(binary,cmd_gad) - Gad = nt("Gad", "addr asm") - out = [Gad(int(gad[:gad.find(':')],0),gad[gad.find(':')+2:]) for gad in out] - return out - -def rop_gadget_exact(binary,gad): - gads = rop_gadget(binary,gad) - for g in gads: - if g.asm[:-1].replace('; ',';') == gad: - return g +def rop_json(binary): + # Gadget JSON schema: + # [ + # { + # retaddr: int + # size: int + # opcodes: [ + # { + # offset: int + # size: int + # opcode: string + # type: string + # } + # ] + # } + # ] + return json.loads("\n".join(run_cmd(binary, "/Rj"))) + +@cache +def rop_gadgets(binary, *regexes, cont=False): + ilog(f"Searching {binary} for {'; '.join(regexes)} gadgets with r2...") + gadgets = rop_json(binary) + results = [] + + for gadget in gadgets: + opcodes = gadget['opcodes'] + end_idx = len(opcodes) - len(regexes) + + for start_idx in range(end_idx + 1): + idx = start_idx + size = end_idx - idx + regexes_use = (regexes + (".*",) * size) if cont else regexes + + offset = opcodes[idx]['offset'] + matches = [] + + for regex in regexes_use: + match = re.fullmatch(regex, opcodes[idx]['opcode']) + if not match: + break + matches.append(match) + idx += 1 + + if len(matches) == len(regexes_use): + results.append(Gadget(offset, matches)) + + return results + +def rop_gadget(binary, *regexes): + results = rop_gadgets(binary, *regexes) + if len(results) == 0: + raise LookupError(f"Could not find gadget for: {'; '.join(regexes)}") + return results[0] def get_call_returns(binary,xref_from,xref_to): ilog(f'Getting return addresses of calls from {hex(xref_from)} to {hex(xref_to)} in {binary} with r2...') |