From 42ab380423553720a2a80d03dee68957e6f3b4ff Mon Sep 17 00:00:00 2001 From: dusoleil Date: Thu, 16 Mar 2023 18:57:11 -0400 Subject: elf: Add docstrings Signed-off-by: dusoleil --- sploit/rev/elf.py | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/sploit/rev/elf.py b/sploit/rev/elf.py index 5833bf5..990cfde 100644 --- a/sploit/rev/elf.py +++ b/sploit/rev/elf.py @@ -1,9 +1,95 @@ +""" +Definition of the ELF class +""" + from sploit.rev import ldd, r2 from sploit.arch import lookup_arch from itertools import zip_longest class ELF: + """ + Representation of an ELF binary file. + + This class is effectively a front-end for the r2 module. Through ELF, you + can get information about an ELF binary in a convenient, object-oriented + interface and automate some of your static analysis reverse engineering. + + Because much of the functionality of r2 is cached, much of the functionality + of ELF is also implicitly cached. Longer operations like retrieving the + symbol table or looking up gadgets will be faster on subsequent attempts. + This is mostly useful when sploit is run from the REPL or in Pipes mode + where this cache is preserved across script runs. + + Some of the behavior of this class is done upfront while other operations + are performed lazily. Retrieving symbols, binary info, security info, and + the list of library dependencies are all done upfront when the object is + constructed. + + path (str): Absolute file path to the underlying ELF file. + + sym (Symtbl): A collection of named address offsets exposed through the ELF. + + libs (dict{str:ELF}): A dictionary of ELFs representing linked library + dependencies of the current ELF. They are indexed by their base filename + (i.e. elf.libs["libc.so.6"]). The actual ELF is lazily constructed when it + is requested. Pretty printing of the libs dict is implemented. + + locals (->Symtbl): A psuedo-namespace to access Symtbls for the local + variables of functions. i.e. If a function existed in the elf called foo(), + you could get a Symtbl of its local variables with elf.locals.foo + + info (->str|int): A psuedo-namespace to access various info about the ELF + file. Printing elf.info will pretty-print this info in a tabulated form. + + info.type (str): The type of file. + + info.os (str): The os the binary was compiled for. + + info.baddr (int): The virtual base address of the binary. + + info.arch_string (str): A string given by r2 iI that helps identify the + architecture the binary was compiled for. + + info.wordsize (int): The natual width of an int on the architecture the + binary was compiled for. + + info.endianness (str): The byte order of an int on the architecture the + binary was compiled for. + + security (->bool|str): A psuedo-namespace to access security info about the + binary. Printing elf.security will pretty-print this info in a tabulated + form. + + security.stripped (bool): True if the binary was stripped of debugging + information, symbols, and strings. + + security.pic (bool): True if the binary's code is position independent. + + security.relro (str): The level of "Relocation Read-Only" that the binary + was compiled with. Pertains to if the Global Offset Table is read-only. + This is often "partial" or "full". + + security.relocs (bool): True if the binary uses dynamic runtime relocation. + + security.canary (bool): True if the binary uses stack canaries. + + security.nx (bool): True if the binary does not have stack execution + privileges. + + security.rpath (str): Runtime library lookup path. If there isn't one, this + will say "NONE". + + arch (Arch): On Construction, an ELF will automatically try to figure out if + it was compiled for one of sploit's predefined Arch's. If so, it will set it + here. Otherwise, this is None. + """ + def __init__(self, path): + """ + Construct an ELF. + + path (str): The filepath to the ELF binary file. + """ self.path = path self.sym = r2.get_elf_symbols(self.path) try: @@ -18,6 +104,7 @@ class ELF: self.arch = lookup_arch(self.info.arch_string, self.info.wordsize, self.info.endianness) def __repr__(self): + """Pretty-print a summary of the ELF.""" s = 'ELF: ' s += self.path s += f'\n{len(self.sym)} symbols @ {hex(self.sym)}' @@ -36,6 +123,7 @@ class ELF: return s class __LIBS__(dict): + # Fancy magic dict of {filename:ELF} which will lazy load the ELF def __init__(self, libs): super().__init__({lib.name:lib.path for lib in libs.values() if lib.path}) def __getitem__(self, lib): @@ -49,6 +137,7 @@ class ELF: return s.strip() class __LOCALS__: + # Fancy magic class that provides a psuedo-namespace to lookup locals for functions def __init__(self, elf): self.elf = elf def __getattr__(self, sym): @@ -89,10 +178,28 @@ class ELF: } def retaddr(self, caller, callee): + """ + Returns a list of addresses where a function returns into another + function at. + + caller (int): Address of the calling function to be returned into. + + callee (int): Address of the function that was called and will return. + """ return [c.ret_addr for c in r2.get_call_returns(self.path, caller, callee)] def gadgets(self, *regexes, cont=False): + """ + Returns a list of gadgets that match the given regex list. + + *regexes (str): All positional arguments are treated as regex strings + for the gadget search. + + cont (bool): If true, this function will return all of the assembly past + the found gadget up to the next return point. + """ return r2.rop_gadgets(self.path, *regexes, cont=cont) def gadget(self, *regexes): + """Returns the first gadget found that matches the given regex list.""" return r2.rop_gadget(self.path, *regexes) -- cgit v1.2.3