summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sploit/symtbl.py112
-rw-r--r--sploit/types/__init__.py2
-rw-r--r--sploit/types/index_entry.py44
-rw-r--r--sploit/types/indextbl.py103
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)