diff options
author | Malfurious <m@lfurio.us> | 2024-02-20 12:44:35 -0500 |
---|---|---|
committer | Malfurious <m@lfurio.us> | 2025-01-02 02:50:40 -0500 |
commit | 778c0ca5c319a4dd2b34eae62062846e572a2411 (patch) | |
tree | 531bd880a24e877957ccd7027227ae9f3f9fb1a6 | |
parent | 70c7c16a157f0e2056d0b96b96f6e13c83841bc3 (diff) | |
download | nsploit-778c0ca5c319a4dd2b34eae62062846e572a2411.tar.gz nsploit-778c0ca5c319a4dd2b34eae62062846e572a2411.zip |
payload: Improve recursion performance
There is a small network of mutually-recursive helper functions which
produce the main outputs for Payload objects (the length, bytes, etc.).
The runtime performance of this code can suffer as a Payload grows to
contain more and more items.
These issues are heavily mitigated by implementing memoization within
one of these functions (which propagates the benefit to the rest of the
call tree). Memo dictionary is only used for a single operation
(lifetime) to avoid the possibility of bad cached results.
Signed-off-by: Malfurious <m@lfurio.us>
-rw-r--r-- | sploit/payload/payload.py | 36 |
1 files changed, 22 insertions, 14 deletions
diff --git a/sploit/payload/payload.py b/sploit/payload/payload.py index a59eed2..b88e323 100644 --- a/sploit/payload/payload.py +++ b/sploit/payload/payload.py @@ -84,6 +84,7 @@ class Payload(IndexTbl): """Return human-readable Payload.""" FMT = "\n{:<20} {:<20} {:<20}" s = f"{len(self.__entries__)} items, {len(self)} bytes @ {hex(self)}" + memo = {} if len(self.__entries__) > 0: s += FMT.format("ADDRESS", "SYMBOL", "DATA") @@ -93,8 +94,8 @@ class Payload(IndexTbl): key = "(unkeyed)" if key is None else str(key) key = f"[{key}]" if isinstance(value, IndexEntry) else key - addr = self.__addrof(i) - data = str(self.__bytesof(i)) + addr = self.__addrof(i, memo) + data = str(self.__bytesof(i, memo)) if len(data) > _REPR_DATA_LEN: data = data[:_REPR_DATA_LEN] + " ..." @@ -104,7 +105,8 @@ class Payload(IndexTbl): def __bytes__(self): """Return calculated payload bytes.""" - x = [ self.__bytesof(i) for i in range(len(self.__entries__)) ] + memo = {} + x = [ self.__bytesof(i, memo) for i in range(len(self.__entries__)) ] return b"".join(x) def __call__(self, *args): @@ -146,13 +148,13 @@ class Payload(IndexTbl): def __getindex__(self, index): """Return payload index value or address.""" - value, _ = self.__valueof(index) + value, _ = self.__valueof(index, {}) return value def __setindex__(self, index, value): """Set payload index value.""" try: - addr = self.__addrof(index) + addr = self.__addrof(index, {}) except KeyError: addr = self.end() value = self.__prep_insertion(value, addr) @@ -164,27 +166,33 @@ class Payload(IndexTbl): # Payload helpers - def __valueof(self, index): + def __valueof(self, index, memo): """Return a tuple: (addr of value, literal value) for index.""" value = self.__entries__[index] - addr = self.__addrof(index) + addr = self.__addrof(index, memo) if isinstance(value, IndexEntry): value @= addr return value, value return addr, value - def __addrof(self, index): + def __addrof(self, index, memo): """Return address (base + offset) for index.""" index = self.__entries__.key2idx(index) - sizes = [ len(self.__bytesof(i)) for i in range(index) ] + sizes = [ len(self.__bytesof(i, memo)) for i in range(index) ] return self.base + sum(sizes) - def __bytesof(self, index): + def __bytesof(self, index, memo): """Return byte output for index.""" - _, value = self.__valueof(index) - if isinstance(value, PayloadEntry): - return value.payload_bytes(self) - return bytes(value) + try: + return memo[index] + except KeyError: + _, value = self.__valueof(index, memo) + if isinstance(value, PayloadEntry): + data = value.payload_bytes(self) + else: + data = bytes(value) + memo[index] = data + return data def __prep_insertion(self, value, addr): """Initialize or type coerce input value for payload insert.""" |