summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2024-02-20 12:44:35 -0500
committerMalfurious <m@lfurio.us>2025-01-02 02:50:40 -0500
commit778c0ca5c319a4dd2b34eae62062846e572a2411 (patch)
tree531bd880a24e877957ccd7027227ae9f3f9fb1a6
parent70c7c16a157f0e2056d0b96b96f6e13c83841bc3 (diff)
downloadnsploit-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.py36
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."""