import copy
from dataclasses import dataclass, field
from sploit.rev.gadget import Gadget
from sploit.types.index_entry import IndexEntry
@dataclass
class GadHint(IndexEntry):
"""
User-annotated gadget description object
base (Gadget|int): The gadget being annotated. May be a Gadget object or
an offset as an int.
pops (list[str]): The registers popped by this gadget, in order of
occurrence.
movs (dict{str:str}): The register-to-register moves made by this gadget.
Keys are destination register names, values are source register names. The
order given is insignificant.
imms (dict{str:int}): The immediate-to-register loads made by this gadget.
Keys are destination register names, values are immediate values. The order
given is insignificant.
writes (dict{str:str}): The register-to-memory stores made by this gadget.
Keys are the destination register names (which hold memory addresses),
values are source register names (which hold values to-be-stored). The
order given is insignificant.
requirements (dict{str:int}): The register state that is required before
this gadget should be executed. Keys are register names, values are the
required register values.
stack (list[int]): A list of words to append to the stack following this
gadget. The first element given is nearest to the top of the stack and the
rest follow in order.
align (bool): If True, this gadget expects the stack to be aligned prior
to entry.
syscall (bool): If True, this gadget contains a syscall instruction.
spm (int): "Stack pointer move" - The amount the stack pointer is adjusted
by this gadget. The effect of executing a terminating "return" instruction
should not be accounted for. A value of zero is taken as "unspecified".
"""
base: int = 0
pops: list = field(default_factory=list)
movs: dict = field(default_factory=dict)
imms: dict = field(default_factory=dict)
writes: dict = field(default_factory=dict)
requirements: dict = field(default_factory=dict)
stack: list = field(default_factory=list)
align: bool = False
syscall: bool = False
spm: int = 0
@property
def offset(self):
"""Return gadget offset as an integer."""
return int(self.base)
def with_requirements(self, reqs):
"""Return new object with additional requirements."""
for k, v in reqs.items():
if self.requirements.get(k, v) != v:
raise ValueError(
f"GadHint: Conflicting gadget requirements: "
f"{self.requirements}, {reqs}")
new = copy.deepcopy(self)
new.requirements |= reqs
return new
def __repr__(self):
"""Return human-readable GadHint."""
def fmt(name, prop):
if len(prop) > 0:
return f", {name}={prop}"
return ""
s = hex(self.base)
s = f"Gadget({s})" if isinstance(self.base, Gadget) else s
s += fmt("pops", self.pops)
s += fmt("movs", self.movs)
s += fmt("imms", self.imms)
s += fmt("writes", self.writes)
s += fmt("requirements", self.requirements)
s += fmt("stack", self.stack)
if self.align:
s += ", align"
if self.syscall:
s += ", syscall"
if self.spm > 0:
s += f", spm={self.spm}"
return f"GadHint({s})"