summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sploit/rev/elf.py11
-rw-r--r--sploit/rev/r2.py82
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...')