summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2022-03-16 04:11:11 -0400
committerMalfurious <m@lfurio.us>2022-03-17 03:58:41 -0400
commit6a617f6dea973862fc88fdbdbbf9c7afed44de62 (patch)
tree9e954ae6e7e5cde322e418473c6280b06bf16607 /tools
parentc557d154b49eb76ddc955e9fa023bf4d7f3bb5b5 (diff)
downloadlib-des-gnux-6a617f6dea973862fc88fdbdbbf9c7afed44de62.tar.gz
lib-des-gnux-6a617f6dea973862fc88fdbdbbf9c7afed44de62.zip
sploit: Rework payload builder
The design/implementation of class Payload is updated for better compatibility with Symtbl, and to address some usability issues that have come up so far: No more automatically fixed-up stack alignment by default: In fact, alignment as a concept is fully removed from the tool, in preparation for another upcoming ROP-centric addon to Payload. Therefore, insertion of return addresses (via .ret()) are now equivalent to any other integer value. No instance size value: Each call to .pad() uses an independent size passed as a parameter, but functions in the same manor as before. Padding can also now be inserted at the beginning of the payload: .pad_front() prepends the necessary amount of data, and updates the tracked offsets of values that were already inserted to the payload. Payload now directly extends Symtbl: Instead of possessing a Symtbl member, payload objects can directly be treated as symbol tables for things like mounting them as subtables, or mapping them to access absolute addresses. New call syntax to access binary data: As a shorthand, users may now use the call syntax to get the bytes string built by the tool. If an argument is passed, it is another byte string containing illegal bytes that we check the built payload for. Unfortunately, the __str__ magic func doesn't like returning bytes string; plus, that overload is already in use for formatting the symbol table content (worth not hiding). New semantic insertion functions: .bin(), .str() [C-style strings], .int(), .ret(), and more. Some of these functions are direct synonyms, however their use can provide more clarity in Sploit scripts. Smarter default element symbol names: Instead of just using '_' as a placeholder if no symbol name is ever given, we now uniquely name each inserted element according to the API function that was used, then slap on an incrementing number. An explicit name still bypasses this scheme. Insertion functions can now be chained together: Functions previously returned the offset/address of the inserted value. However, this feature was seldom used, and there is now the possibility of .pad_front() invalidating previously-returned offsets. Instead, functional-style chaining is enabled to reduce boilerplate, and help with quick oneliners. Signed-off-by: Malfurious <m@lfurio.us> Signed-off-by: dusoleil <howcansocksbereal@gmail.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/sploit/sploit/payload.py125
1 files changed, 68 insertions, 57 deletions
diff --git a/tools/sploit/sploit/payload.py b/tools/sploit/sploit/payload.py
index 49e0c04..9fab65e 100644
--- a/tools/sploit/sploit/payload.py
+++ b/tools/sploit/sploit/payload.py
@@ -1,64 +1,75 @@
from sploit.arch import arch, itob
from sploit.mem import Symtbl
-# Users can set this to the (absolute) address of a 'ret' ROP gadget. Some
-# features may require it.
-RETGADGET : int = None
+class Payload(Symtbl):
+ MAGIC = b'\xef'
-class Placeholder(bytearray):
- def __init__(self, text='_unnamed_'):
- self += bytearray(itob(0))
- self.text = text
-
-class Payload:
- def __init__(self, size=0, base=0, **kwargs):
+ def __init__(self, **kwargs):
+ super().__init__(**kwargs)
+ self = self._namesp
self.payload = b''
- self.size = size
- self.alignstart = None
- self.tab = Symtbl(base=base, **kwargs)
+ self.ctrs = {}
def __len__(self):
- return len(self.payload)
-
- def __getattr__(self, sym):
- return getattr(self.tab, sym)
-
- def data(self, x, sym='_'):
- off = len(self)
- self.payload += x
- setattr(self.tab, sym, off)
- return getattr(self.tab, sym)
-
- def value(self, x, sym='_', signed=False):
- return self.data(itob(x, signed=signed), sym=sym)
-
- def ret(self, x, sym='_'):
- self.align()
- return self.value(x, sym=sym)
-
- def stuff(self, x, size, sym='_', *, explain=''):
- if size >= 0:
- if (size := size / len(x)) == int(size):
- if size == 0 or not isinstance(x, Placeholder):
- return self.data(x * int(size), sym=sym)
-
- raise Exception(explain+"Can not stuff payload: "
- f"Placeholder for {x.text} detected")
- raise Exception(explain+"Can not stuff payload: "
- "Element does not divide the space evenly")
- raise Exception(explain+"Can not stuff payload: "
- "Available space is negative")
-
- def pad(self, x=None, sym='_'):
- size = self.size - len(self)
- return self.stuff((x or arch.nopcode), size, sym=sym,
- explain='Error padding payload: ')
-
- def align(self, x=None, sym='_'):
- if self.alignstart is None:
- self.alignstart = len(self)
-
- retgad = (itob(RETGADGET) if RETGADGET else Placeholder('ret gadget'))
- size = (self.alignstart - len(self)) % arch.alignment
- return self.stuff((x or retgad), size, sym=sym,
- explain='Error aligning payload: ')
+ return len(self._namesp.payload)
+
+ def __call__(self, badbytes=b''):
+ self = self._namesp
+ found = [ hex(x) for x in set(self.payload).intersection(badbytes) ]
+ if len(found) > 0:
+ raise Exception(f'Payload: bad bytes in content: {found}')
+ return self.payload
+
+ def __name(self, kind):
+ self = self._namesp
+ try: ctr = self.ctrs[kind]
+ except: ctr = 0
+ self.ctrs[kind] = ctr + 1
+ return f'{kind}_{ctr}'
+
+ def __append(self, value, sym):
+ setattr(self, sym, len(self))
+ self._namesp.payload += value
+ return self
+
+ def __prepend(self, value, sym):
+ self.adjust(len(value))
+ setattr(self, sym, 0)
+ self._namesp.payload = value + self._namesp.payload
+ return self
+
+ def bin(self, value, sym=None):
+ return self.__append(value, sym or self.__name('bin'))
+
+ def str(self, value, sym=None):
+ return self.bin(value.encode()+b'\x00', sym or self.__name('str'))
+
+ def int(self, value, sym=None, signed=False):
+ return self.bin(itob(value, signed=signed), sym or self.__name('int'))
+
+ def ret(self, value, sym=None):
+ return self.int(value, sym or self.__name('ret'))
+
+ def sbp(self, value=None, sym=None):
+ if value is None:
+ return self.rep(self.MAGIC, arch.wordsize, sym or self.__name('sbp'))
+ return self.int(value, sym or self.__name('sbp'))
+
+ def rep(self, value, size, sym=None):
+ return self.bin(self.__rep_helper(value, size), sym or self.__name('rep'))
+
+ def pad(self, size, value=None, sym=None):
+ return self.bin(self.__pad_helper(size, value), sym or self.__name('pad'))
+
+ def pad_front(self, size, value=None, sym=None):
+ return self.__prepend(self.__pad_helper(size, value), sym or self.__name('pad'))
+
+ def __rep_helper(self, value, size, *, explain=''):
+ if size < 0:
+ raise Exception(f'Payload: {explain}rep: available space is negative')
+ if (size := size / len(value)) != int(size):
+ raise Exception(f'Payload: {explain}rep: element does not divide the space evenly')
+ return value * int(size)
+
+ def __pad_helper(self, size, value):
+ return self.__rep_helper(value or arch.nopcode, size - len(self), explain='pad: ')