diff options
-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) |