diff options
Diffstat (limited to 'sploit')
| -rw-r--r-- | sploit/symtbl.py | 112 | ||||
| -rw-r--r-- | sploit/types/__init__.py | 2 | ||||
| -rw-r--r-- | sploit/types/index_entry.py | 44 | ||||
| -rw-r--r-- | sploit/types/indextbl.py | 103 | 
4 files changed, 193 insertions, 68 deletions
| diff --git a/sploit/symtbl.py b/sploit/symtbl.py index a471958..86800f5 100644 --- a/sploit/symtbl.py +++ b/sploit/symtbl.py @@ -73,112 +73,88 @@ with the Symtbl base address.      print(s.a, s.b, s.c, s.d)   # "998 999 1000 1001"  """ +from sploit.types.indextbl import IndexTbl +from sploit.types.index_entry import IndexEntry +  def Symtbl(*, base=0, **symbols):      """      Create a new Symtbl object.      Return an empty Symtbl or, optionally, one initialized with the given -    symbol values.  Arguments _must_ be keyword arguments. +    symbol values.  Arguments must be keyword arguments.      Users should call this function instead of attempting to construct the      Symtbl class.  Construction is implemented via a normal function to prevent      any argument name from conflicting with __init__'s bound instance parameter.      """ -    self = SymtblImpl({}, 0, base) +    self = SymtblImpl(base, 0, dict())      for k, v in symbols.items():          self[k] = v      return self -class SymtblImpl: +class SymtblImpl(IndexTbl):      """Symtbl implementation class""" -    def __init__(self, entries, adjust, base): +    def __init__(self, base, adjust, entries):          """Construct Symtbl from instance data.""" +        super().__init__(base) +        object.__setattr__(self, "__adjust__", adjust)          object.__setattr__(self, "__entries__", entries) -        object.__setattr__(self, "__adjust__",  adjust) -        object.__setattr__(self, "base", base) -    def __index__(self): -        """Convert object to integer using base value.""" -        return self.base +    def __repr__(self): +        """Return human-readable Symtbl.""" +        FMT = "\n{:<20} {:<20}" +        s = f"{len(self)} symbols @ {hex(self)}" -    def __matmul__(self, base): -        """Create remapped version of object at absolute base.""" -        return SymtblImpl(self.__entries__, self.__adjust__, int(base)) +        if len(self) > 0: +            s += FMT.format("ADDRESS", "SYMBOL") -    def __add__(self, offset): -        """Create remapped version of object at relative base.""" -        return self @ (self.base + offset) +        for key, value in self: +            key = f"[{key}]" if isinstance(value, IndexEntry) else key +            s += FMT.format(hex(value), key) -    def __sub__(self, offset): -        """Create remapped version of object at relative base.""" -        return self @ (self.base - offset) +        return s      def __rshift__(self, offset): -        """Create symbol adjusted version of object.""" -        return SymtblImpl(self.__entries__, self.__adjust__ + int(offset), self.base) +        """Return symbol-adjusted version of object.""" +        adjust = self.__adjust__ + int(offset) +        return SymtblImpl(self.base, adjust, self.__entries__)      def __lshift__(self, offset): -        """Create symbol adjusted version of object.""" +        """Return symbol-adjusted version of object."""          return self >> (-offset)      def __mod__(self, offset): -        """Create symbol rebased version of object.""" +        """Return symbol-rebased version of object."""          return self >> (self.base - offset) -    def __getattr__(self, symbol): -        """Return symbol offset or subtable via pseudo-attribute.""" -        return self[symbol] +    # IndexTbl abstract methods -    def __setattr__(self, symbol, value): -        """Set symbol offset or subtable via pseudo-attribute.""" -        self[symbol] = value +    def __copy__(self): +        """Return copy of object with shared symbol entries.""" +        return SymtblImpl(self.base, self.__adjust__, self.__entries__) -    def __delattr__(self, symbol): -        """Unset symbol via pseudo-attribute.""" -        del self[symbol] +    def __iter__(self): +        """Iterate over table items, sorted by offsets.""" +        it = { k: self[k] for k in self.__entries__}.items() +        return iter(sorted(it, key=lambda x: int(x[1])))      def __len__(self):          """Return number of defined symbols."""          return len(self.__entries__) -    def __getitem__(self, symbol): -        """Return symbol offset, subtable, or translated offset via subscript.""" -        if symbol == "base": -            return self.base -        offset = self.__entries__[symbol] if type(symbol) is str else symbol +    def __getindex__(self, index): +        """Return symbol value or translated offset.""" +        if isinstance(index, (int, IndexEntry)): offset = index +        else: offset = self.__entries__[index]          return offset + (self.base + self.__adjust__) -    def __setitem__(self, symbol, value): -        """Set symbol offset or subtable via subscript.""" -        if symbol == "base": -            object.__setattr__(self, "base", int(value)) -        elif symbol in dir(self): -            raise KeyError(f"Symtbl: key is reserved: {symbol}") -        elif type(symbol) is not str: -            raise TypeError(f"Symtbl: key must be a string: {symbol}") -        else: -            self.__entries__[symbol] = value - (self.base + self.__adjust__) - -    def __delitem__(self, symbol): -        """Unset symbol via subscript.""" -        del self.__entries__[symbol] +    def __setindex__(self, index, value): +        """Set symbol value.""" +        if isinstance(index, (int, IndexEntry)): +            raise TypeError(f"Symtbl: Unsupported key type: {type(index)}") +        self.__entries__[index] = value - (self.base + self.__adjust__) -    def __iter__(self): -        """Iterate over table entries as key:value tuples, like dict.items().""" -        return iter(sorted({ k: self[k] for k in self.__entries__ }.items(), key=lambda v: int(v[1]))) - -    def __contains__(self, symbol): -        """Test symbol name membership in table.""" -        return symbol in self.__entries__ - -    def __repr__(self): -        """Return human-readable Symtbl.""" -        FMT = "\n{:<20} {:<20}" -        s = f"{len(self)} symbols @ {hex(self)}" -        if len(self) > 0: -            s += FMT.format("ADDRESS", "SYMBOL") -        for symbol, offset in self: -            disp = f"[{symbol}]" if type(offset) is not int else symbol -            s += FMT.format(hex(offset), disp) -        return s +    def __delindex__(self, index): +        """Delete symbol.""" +        del self.__entries__[index] diff --git a/sploit/types/__init__.py b/sploit/types/__init__.py new file mode 100644 index 0000000..1316dad --- /dev/null +++ b/sploit/types/__init__.py @@ -0,0 +1,2 @@ +from .indextbl import * +from .index_entry import * diff --git a/sploit/types/index_entry.py b/sploit/types/index_entry.py new file mode 100644 index 0000000..a03ab92 --- /dev/null +++ b/sploit/types/index_entry.py @@ -0,0 +1,44 @@ +import copy + +class IndexEntry: +    """ +    Generic IndexTbl entry object + +    IndexEntry is intended to be subclassed to create interesting types that are +    compatible with IndexTbl directories.  IndexEntry gives objects a baseline +    int-like personality. + +    IndexEntry objects are convertable to int via int(), bin(), hex(), etc.  This +    integer value is manipulated via the object's "base" property, and a few +    operators are implemented to provide nicer syntax for this as well.  The use +    of operators generally yield distinct copies of the original object. + +    The property name "base" is used since it has semantic meaning for the +    IndexTbl class, which is itself an extension of this class. + +    base (int): Index integer value +    """ + +    base = 0 + +    def __init__(self, base=0): +        """Construct index with the given base value.""" +        self.base = base + +    def __index__(self): +        """Convert index to base integer value.""" +        return int(self.base) + +    def __matmul__(self, base): +        """Create new object with the given base value.""" +        new = copy.copy(self) +        new.base = base +        return new + +    def __add__(self, add): +        """Create new object with the given relative base value.""" +        return self @ (self.base + add) + +    def __sub__(self, sub): +        """Create new object with the given relative base value.""" +        return self @ (self.base - sub) diff --git a/sploit/types/indextbl.py b/sploit/types/indextbl.py new file mode 100644 index 0000000..4f57a59 --- /dev/null +++ b/sploit/types/indextbl.py @@ -0,0 +1,103 @@ +from abc import abstractmethod +from collections.abc import Collection + +from sploit.types.index_entry import IndexEntry + +class IndexTbl(IndexEntry, Collection): +    """ +    Abstract Index Table + +    IndexTbl is a common interface to an abstracted key-value store.  The +    storage mechanism as well as lookup semantics are defined by concrete +    implementations.  "Index" in this case is more akin to a directory index or +    book index than a strictly numeric array index. + +    In general, concrete tables may store values of any or multiple different +    types.  In particular, tables should give special accommodation for values +    of type IndexEntry.  These objects usually represent "rich" versions of the +    nominal data types the table expects to contain.  Implementation repr() +    methods usually also annotate which members are IndexEntries. + +    IndexTbl extends from IndexEntry, and so has a base value which represents +    the "base index" of the table.  The meaning of this depends on the +    implementation.  This inheritance also means that tables are generally +    expected to be nestable. + +    IndexTbls allow indices to be accessed via attribute or subscript notation. +    This is probably the key characteristic feature of the class.  The class +    namespace is kept as clean as possible to make for the fewest collisions +    between index names and other (real) class attributes.  Note that there are +    abstract methods, required to be overridden, which implement the index +    access.  These methods are only called for normal indices, not the table +    base. + +    Because this class overrides attribute access, normal automatic object +    copying is broken.  Because of this, implementations must also provide a +    definition for the __copy__() method as well. +    """ + +    @abstractmethod +    def __getindex__(self, index): +        """Lookup and retrieve index value.""" +        raise NotImplementedError + +    @abstractmethod +    def __setindex__(self, index, value): +        """Lookup and set index value.""" +        raise NotImplementedError + +    @abstractmethod +    def __delindex__(self, index): +        """Lookup and delete index value.""" +        raise NotImplementedError + +    @abstractmethod +    def __copy__(self): +        """Create a copy of this IndexTbl object.""" +        raise NotImplementedError + +    def __contains__(self, index): +        """Test the existence of the given index.""" +        try: +            self.__getindex__(index) +        except KeyError: +            return False +        else: +            return True + +    # Attribute access methods + +    def __getattr__(self, index): +        """Get value via attribute.""" +        return self[index] + +    def __setattr__(self, index, value): +        """Set value via attribute.""" +        self[index] = value + +    def __delattr__(self, index): +        """Delete value via attribute.""" +        del self[index] + +    # Subscript/item access methods + +    def __getitem__(self, index): +        """Get value via subscript.""" +        if index == "base": +            return self.base +        return self.__getindex__(index) + +    def __setitem__(self, index, value): +        """Set value via subscript.""" +        if index == "base": +            object.__setattr__(self, "base", value) +        elif index in dir(self): +            raise KeyError(f"IndexTbl: Index is reserved: {index}") +        else: +            self.__setindex__(index, value) + +    def __delitem__(self, index): +        """Delete value via subscript.""" +        if index == "base": +            raise KeyError("IndexTbl: May not delete index: base") +        self.__delindex__(index) | 
