summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sploit/payload.py6
-rw-r--r--sploit/symtbl.py233
2 files changed, 185 insertions, 54 deletions
diff --git a/sploit/payload.py b/sploit/payload.py
index 1110e76..1775ceb 100644
--- a/sploit/payload.py
+++ b/sploit/payload.py
@@ -26,13 +26,13 @@ class Payload:
return f'{kind}_{ctr}'
def _append(self, value, sym):
- setattr(self.sym.map(0), sym, len(self))
+ (self.sym @ 0)[sym] = len(self)
self.payload += value
return self
def _prepend(self, value, sym):
- self.sym.adjust(len(value))
- setattr(self.sym.map(0), sym, 0)
+ self.sym >>= len(value)
+ (self.sym @ 0)[sym] = 0
self.payload = value + self.payload
return self
diff --git a/sploit/symtbl.py b/sploit/symtbl.py
index 3a3e697..05021f7 100644
--- a/sploit/symtbl.py
+++ b/sploit/symtbl.py
@@ -1,53 +1,184 @@
-import types
-
-class Symtbl:
- def __init__(self, *, base=0, **kwargs):
- object.__setattr__(self, '_namesp', types.SimpleNamespace(base=base,sym={},sub={}))
- for k, v in {**kwargs}.items():
- setattr(self, k, v)
-
- def __getattr__(self, ident):
- self = self._namesp
- if ident == 'base': return self.base
- off = self.base + self.sym[ident]
- if ident in self.sub: return self.sub[ident].map(off)
- return off
-
- def __setattr__(self, ident, value):
- if ident in dir(self): raise Exception(f'Symtbl: assignment would shadow non-symbol "{ident}"')
- self = self._namesp
- if ident == 'base':
- self.base = value
+"""
+Symtbl data structure
+
+A Symtbl (symbol table) is an associative data container intended to model
+arbitrary memory layouts, such as structure definitions or memory-mapped
+objects. Elements may be accessed via subscript or attribute notation.
+
+A Symtbl is essentially a dictionary, in which each key (symbol name string)
+is associated with an offset value. A special key "base" represents the
+base or starting address of the overall table in memory. Whenever offset
+values are accessed, they are adjusted relative to the table's base value.
+This enables the primary function of Symtbl objects: the ability to resolve
+mapped, or absolute, addresses of objects in memory.
+
+Therefore, even though a Symtbl internally tracks symbol offsets, the apparent
+value of any symbol will always be its offset plus the table's base address.
+The table's base address will also be subtracted from values being stored in
+the table, as the provided value is assumed to be mapped in the same manner as
+the table itself.
+
+ s = Symtbl()
+ s.a = 10
+ s.b = 20
+ print(s.a, s.b) # "10 20"
+ s.base = 100
+ print(s.a, s.b) # "110 120"
+ s.c = 150
+ s.base = 10
+ print(s.a, s.b, s.c) # "20 30 60"
+
+A Symtbl's base value may be changed at any time, and this will affect the
+interpretation of offsets as described above. However, one may also create a
+remapped version of a Symtbl (without modifying the original) using the '@'
+operator. This new object will have the base value given on the right hand
+side of the '@' and its collection of symbols is referentially linked to the
+source object, meaning changes to symbol entries will be visible in both
+objects.
+
+ s1 = Symtbl()
+ s1.a = 10
+ s2 = s1 @ 1000
+ print(s1.a, s2.a) # "10 1010"
+ s2.b = 1234
+ print(s1.b, s2.b) # "234 1234"
+
+Symtbl's are also nestable, to support modeling composite memory layouts. If
+a symbol's value is assigned to another Symtbl object, rather than an integer
+offset, the child object's base value serves as its offset in the parent
+Symtbl. Symbols on the child object may then be accessed recursively from the
+parent's scope. If the parent has a non-zero base, it adjusts the offsets
+interpreted in the child.
+
+ child = Symtbl()
+ child.a = 1
+ child.b = 2
+ parent = Symtbl()
+ parent.nested = child @ 70
+ print(parent.nested.a, parent.nested.b) # "71 72"
+
+A Symtbl will allow you to uniformly adjust all offsets contained, while leaving
+the base value the same, using the '<<' and '>>' operators. A custom
+"rebase" operation is also available via the "%" operator. A rebase applies
+a uniform shift, such that the right hand side offset operand ends up coinciding
+with the Symtbl base address.
+
+ s = Symtbl()
+ s.a = 1
+ s.b = 2
+ s.c = 3
+ s.d = 4
+ s.base = 1000
+ s %= s.c # rebase at symbol 'c'
+ print(s.a, s.b, s.c, s.d) # "998 999 1000 1001"
+"""
+
+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.
+
+ 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)
+ for k, v in symbols.items():
+ self[k] = v
+ return self
+
+class SymtblImpl:
+ """Symtbl implementation class"""
+
+ def __init__(self, entries, adjust, base):
+ """Construct Symtbl from instance data"""
+ 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 __matmul__(self, base):
+ """Create remapped version of object at absolute base"""
+ return SymtblImpl(self.__entries__, self.__adjust__, int(base))
+
+ def __add__(self, offset):
+ """Create remapped version of object at relative base"""
+ return self @ (self.base + offset)
+
+ def __sub__(self, offset):
+ """Create remapped version of object at relative base"""
+ return self @ (self.base - offset)
+
+ def __rshift__(self, offset):
+ """Create symbol adjusted version of object"""
+ return SymtblImpl(self.__entries__, self.__adjust__ + int(offset), self.base)
+
+ def __lshift__(self, offset):
+ """Create symbol adjusted version of object"""
+ return self >> (-offset)
+
+ def __mod__(self, offset):
+ """Create 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]
+
+ def __setattr__(self, symbol, value):
+ """Set symbol offset or subtable via pseudo-attribute"""
+ self[symbol] = value
+
+ def __delattr__(self, symbol):
+ """Unset symbol via pseudo-attribute"""
+ del self[symbol]
+
+ def __len__(self):
+ """Return number of defined symbols"""
+ return len(self.__entries__)
+
+ def __getitem__(self, symbol):
+ """Return symbol offset or subtable via subscript"""
+ if symbol == "base":
+ return self.base
+ return self.__entries__[symbol] + (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: name '{symbol}' is reserved")
else:
- if type(value) is tuple: self.sub[ident], off = value
- else: off = value
- self.sym[ident] = off - self.base
-
- def map(self, addr, off=0):
- self = self._namesp
- mm = Symtbl()
- mm._namesp.sym, mm._namesp.sub = self.sym, self.sub
- mm._namesp.base = addr - off
- return mm
-
- def adjust(self, off):
- self = self._namesp
- for k, v in self.sym.items():
- self.sym[k] = v + off
-
- def rebase(self, off):
- self.adjust(self.base - off)
-
- def __str__(_self):
- FMT = '\n{:<20} {:<20}'
- self = _self._namesp
-
- s = f'{len(self.sym)} symbols @ {hex(_self.base)}'
- s += FMT.format('ADDRESS', 'SYMBOL')
- for sym, _ in sorted(self.sym.items(), key=lambda x:x[1]):
- addr = getattr(_self, sym)
- if type(addr) is Symtbl:
- s += FMT.format(hex(addr.base), f'[{sym}]')
- else:
- s += FMT.format(hex(addr), sym)
+ self.__entries__[symbol] = value - (self.base + self.__adjust__)
+
+ def __delitem__(self, symbol):
+ """Unset symbol via subscript"""
+ del self.__entries__[symbol]
+
+ def __iter__(self):
+ """Iterate over table entries as key:value tuples, like dict.items()"""
+ return iter({ k: self[k] for k in self.__entries__ }.items())
+
+ def __contains__(self, symbol):
+ """Test symbol name membership in table"""
+ return symbol in self.__entries__
+
+ def __repr__(self):
+ """Return string representation of Symtbl"""
+ return str(self)
+
+ def __str__(self):
+ """Return string representation of Symtbl"""
+ FMT = "\n{:<20} {:<20}"
+ s = f"{len(self)} symbols @ {hex(self)}"
+ s += FMT.format("ADDRESS", "SYMBOL")
+ for symbol, offset in sorted(self, key=lambda v: int(v[1])):
+ disp = f"[{symbol}]" if type(offset) is SymtblImpl else symbol
+ s += FMT.format(hex(offset), disp)
return s