From d673d458922b640ca3f384288356a33a308cdc9b Mon Sep 17 00:00:00 2001 From: Malfurious Date: Sat, 28 May 2022 01:10:05 -0400 Subject: sploit: payload: Allow variadic insertions Often times, users of the Payload module wish to push a list of integers to a payload buffer. Currently, the best (and intended) way to do this is to make several calls to .int(). However, as part of the ROP effort, I am planning to add function 'gadget(addr, *params)' to the Payload class. Per the design of this function, calling it with an expanded list of values would be equivalent to passing each to .int() individually. In order to discourage the use of .gadget(), as a shortcut to a series of .int()s, .int(), and most other insertion functions, now accept arbitrarily many value arguments. Functions that support additional options (such as .int()'s 'signed' parameter) will apply such options to all values. If a symbol name is defined, it will reference the beginning of the block of values. Keep in mind, this will also allow inserting zero values. For example, obj.bin(sym='end') will tag the end of the payload without extending its content. This use-case is not intended to be particularly useful, but exists as a consequence of the change. Payload.rep() and the pad functions are not affected by this commit, as I don't think changing their semantics in this way makes sense. Signed-off-by: Malfurious Signed-off-by: dusoleil --- tools/sploit/sploit/payload.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/tools/sploit/sploit/payload.py b/tools/sploit/sploit/payload.py index 9fab65e..43f58c6 100644 --- a/tools/sploit/sploit/payload.py +++ b/tools/sploit/sploit/payload.py @@ -38,28 +38,30 @@ class Payload(Symtbl): 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 bin(self, *values, sym=None): + return self.__append(b''.join(values), sym or self.__name('bin')) - def str(self, value, sym=None): - return self.bin(value.encode()+b'\x00', sym or self.__name('str')) + def str(self, *values, sym=None): + values = [ v.encode() + b'\x00' for v in values ] + return self.bin(*values, sym=(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 int(self, *values, sym=None, signed=False): + values = [ itob(v, signed=signed) for v in values ] + return self.bin(*values, sym=(sym or self.__name('int'))) - def ret(self, value, sym=None): - return self.int(value, sym or self.__name('ret')) + def ret(self, *values, sym=None): + return self.int(*values, sym=(sym or self.__name('ret'))) - def sbp(self, value=None, sym=None): - if value is None: + def sbp(self, *values, sym=None): + if len(values) == 0: return self.rep(self.MAGIC, arch.wordsize, sym or self.__name('sbp')) - return self.int(value, sym or self.__name('sbp')) + return self.int(*values, sym=(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')) + return self.bin(self.__rep_helper(value, size), sym=(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')) + return self.bin(self.__pad_helper(size, value), sym=(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')) -- cgit v1.2.3 From 9759d731da4787ea0679ebe9700d72397ec788b2 Mon Sep 17 00:00:00 2001 From: Malfurious Date: Thu, 7 Jul 2022 23:56:25 -0400 Subject: sploit: payload: Class no longer extends Symtbl Given the current design of Symtbl, creating subclasses of it gets more tedious the further one goes down a potential class hierarchy. As I am planning to introduce new features in the future that explicitly extend Payload, make this change now to minimize the impact. Additionally, switching Payload's relationship with Symtbl from "is-a" to "has-a" makes it more consistent with rev.ELF, the other major user of Symtbl. (And in both cases, the member is named 'sym') Signed-off-by: Malfurious Signed-off-by: dusoleil --- tools/sploit/sploit/payload.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tools/sploit/sploit/payload.py b/tools/sploit/sploit/payload.py index 43f58c6..0bbf463 100644 --- a/tools/sploit/sploit/payload.py +++ b/tools/sploit/sploit/payload.py @@ -1,41 +1,38 @@ from sploit.arch import arch, itob from sploit.mem import Symtbl -class Payload(Symtbl): +class Payload: MAGIC = b'\xef' def __init__(self, **kwargs): - super().__init__(**kwargs) - self = self._namesp self.payload = b'' + self.sym = Symtbl(**kwargs) self.ctrs = {} def __len__(self): - return len(self._namesp.payload) + return len(self.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 + setattr(self.sym.map(0), sym, len(self)) + self.payload += value return self def __prepend(self, value, sym): - self.adjust(len(value)) - setattr(self, sym, 0) - self._namesp.payload = value + self._namesp.payload + self.sym.adjust(len(value)) + setattr(self.sym.map(0), sym, 0) + self.payload = value + self.payload return self def bin(self, *values, sym=None): -- cgit v1.2.3 From 51d92be1f5eb0ec188b635366d588f61f5f3bca4 Mon Sep 17 00:00:00 2001 From: Malfurious Date: Fri, 8 Jul 2022 00:04:02 -0400 Subject: sploit: payload: Clean up automatic symbol naming This is just a slight code reduction, but will make any future code simpler as well. Explicit comparision to None is more correct as well; centralizing this for reuse better justifies the wordier if statement. Signed-off-by: Malfurious Signed-off-by: dusoleil --- tools/sploit/sploit/payload.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tools/sploit/sploit/payload.py b/tools/sploit/sploit/payload.py index 0bbf463..0e32975 100644 --- a/tools/sploit/sploit/payload.py +++ b/tools/sploit/sploit/payload.py @@ -18,7 +18,8 @@ class Payload: raise Exception(f'Payload: bad bytes in content: {found}') return self.payload - def __name(self, kind): + def __name(self, kind, sym): + if sym is not None: return sym try: ctr = self.ctrs[kind] except: ctr = 0 self.ctrs[kind] = ctr + 1 @@ -36,32 +37,32 @@ class Payload: return self def bin(self, *values, sym=None): - return self.__append(b''.join(values), sym or self.__name('bin')) + return self.__append(b''.join(values), sym=self.__name('bin', sym)) def str(self, *values, sym=None): values = [ v.encode() + b'\x00' for v in values ] - return self.bin(*values, sym=(sym or self.__name('str'))) + return self.bin(*values, sym=self.__name('str', sym)) def int(self, *values, sym=None, signed=False): values = [ itob(v, signed=signed) for v in values ] - return self.bin(*values, sym=(sym or self.__name('int'))) + return self.bin(*values, sym=self.__name('int', sym)) def ret(self, *values, sym=None): - return self.int(*values, sym=(sym or self.__name('ret'))) + return self.int(*values, sym=self.__name('ret', sym)) def sbp(self, *values, sym=None): if len(values) == 0: - return self.rep(self.MAGIC, arch.wordsize, sym or self.__name('sbp')) - return self.int(*values, sym=(sym or self.__name('sbp'))) + return self.rep(self.MAGIC, arch.wordsize, sym=self.__name('sbp', sym)) + return self.int(*values, sym=self.__name('sbp', sym)) def rep(self, value, size, sym=None): - return self.bin(self.__rep_helper(value, size), sym=(sym or self.__name('rep'))) + return self.bin(self.__rep_helper(value, size), sym=self.__name('rep', sym)) def pad(self, size, value=None, sym=None): - return self.bin(self.__pad_helper(size, value), sym=(sym or self.__name('pad'))) + return self.bin(self.__pad_helper(size, value), sym=self.__name('pad', sym)) def pad_front(self, size, value=None, sym=None): - return self.__prepend(self.__pad_helper(size, value), sym or self.__name('pad')) + return self.__prepend(self.__pad_helper(size, value), sym=self.__name('pad', sym)) def __rep_helper(self, value, size, *, explain=''): if size < 0: -- cgit v1.2.3 From 85320ac3a6f58483c01d52e39ee67241db6c165f Mon Sep 17 00:00:00 2001 From: Malfurious Date: Fri, 8 Jul 2022 00:49:35 -0400 Subject: sploit: payload: Promote private methods to "protected" access Lift restriction (name mangling) to Payload helper functions, as their use will be useful in Payload subclasses. Signed-off-by: Malfurious Signed-off-by: dusoleil --- tools/sploit/sploit/payload.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tools/sploit/sploit/payload.py b/tools/sploit/sploit/payload.py index 0e32975..c916514 100644 --- a/tools/sploit/sploit/payload.py +++ b/tools/sploit/sploit/payload.py @@ -18,58 +18,58 @@ class Payload: raise Exception(f'Payload: bad bytes in content: {found}') return self.payload - def __name(self, kind, sym): + def _name(self, kind, sym): if sym is not None: return sym try: ctr = self.ctrs[kind] except: ctr = 0 self.ctrs[kind] = ctr + 1 return f'{kind}_{ctr}' - def __append(self, value, sym): + def _append(self, value, sym): setattr(self.sym.map(0), sym, len(self)) self.payload += value return self - def __prepend(self, value, sym): + def _prepend(self, value, sym): self.sym.adjust(len(value)) setattr(self.sym.map(0), sym, 0) self.payload = value + self.payload return self def bin(self, *values, sym=None): - return self.__append(b''.join(values), sym=self.__name('bin', sym)) + return self._append(b''.join(values), sym=self._name('bin', sym)) def str(self, *values, sym=None): values = [ v.encode() + b'\x00' for v in values ] - return self.bin(*values, sym=self.__name('str', sym)) + return self.bin(*values, sym=self._name('str', sym)) def int(self, *values, sym=None, signed=False): values = [ itob(v, signed=signed) for v in values ] - return self.bin(*values, sym=self.__name('int', sym)) + return self.bin(*values, sym=self._name('int', sym)) def ret(self, *values, sym=None): - return self.int(*values, sym=self.__name('ret', sym)) + return self.int(*values, sym=self._name('ret', sym)) def sbp(self, *values, sym=None): if len(values) == 0: - return self.rep(self.MAGIC, arch.wordsize, sym=self.__name('sbp', sym)) - return self.int(*values, sym=self.__name('sbp', sym)) + return self.rep(self.MAGIC, arch.wordsize, sym=self._name('sbp', sym)) + return self.int(*values, sym=self._name('sbp', sym)) def rep(self, value, size, sym=None): - return self.bin(self.__rep_helper(value, size), sym=self.__name('rep', sym)) + return self.bin(self._rep_helper(value, size), sym=self._name('rep', sym)) def pad(self, size, value=None, sym=None): - return self.bin(self.__pad_helper(size, value), sym=self.__name('pad', sym)) + return self.bin(self._pad_helper(size, value), sym=self._name('pad', sym)) def pad_front(self, size, value=None, sym=None): - return self.__prepend(self.__pad_helper(size, value), sym=self.__name('pad', sym)) + return self._prepend(self._pad_helper(size, value), sym=self._name('pad', sym)) - def __rep_helper(self, value, size, *, explain=''): + 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: ') + def _pad_helper(self, size, value): + return self._rep_helper(value or arch.nopcode, size - len(self), explain='pad: ') -- cgit v1.2.3