From 9218e0e07f92ffb61b17f14336f36440092c1083 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 31 Jan 2025 18:47:11 +1100 Subject: [PATCH 1/3] fix double null-termination search --- volatility3/framework/objects/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/objects/__init__.py b/volatility3/framework/objects/__init__.py index 869d4dae6..39ce6f59f 100644 --- a/volatility3/framework/objects/__init__.py +++ b/volatility3/framework/objects/__init__.py @@ -356,8 +356,9 @@ class String(PrimitiveObject, str): ), **params, ) - if value.find("\x00") >= 0: - value = value[: value.find("\x00")] + index = value.find("\x00") + if index >= 0: + value = value[:index] return value class VolTemplateProxy(interfaces.objects.ObjectInterface.VolTemplateProxy): From b91724c3ef7a5fa22f639a6c495afdce736ee5d9 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 31 Jan 2025 18:48:52 +1100 Subject: [PATCH 2/3] Replace *_to_string() for a block reader implementation for better performance. Add new address_to_string() helper --- volatility3/framework/objects/utility.py | 102 +++++++++++++++++++++-- 1 file changed, 94 insertions(+), 8 deletions(-) diff --git a/volatility3/framework/objects/utility.py b/volatility3/framework/objects/utility.py index 0bc285517..dc450c0b3 100644 --- a/volatility3/framework/objects/utility.py +++ b/volatility3/framework/objects/utility.py @@ -29,9 +29,23 @@ def bswap_64(value: int) -> int: def array_to_string( - array: "objects.Array", count: Optional[int] = None, errors: str = "replace" -) -> interfaces.objects.ObjectInterface: - """Takes a volatility Array of characters and returns a string.""" + array: "objects.Array", + count: Optional[int] = None, + errors: str = "replace", + block_size=32, +) -> str: + """Takes a Volatility 'Array' of characters and returns a Python string. + + Args: + array: The Volatility `Array` object containing character elements. + count: Optional maximum number of characters to convert. If None, the function + processes the entire array. + errors: Specifies error handling behavior for decoding, defaulting to "replace". + block_size: Reading block size. Defaults to 32 + + Returns: + A decoded string representation of the character array. + """ # TODO: Consider checking the Array's target is a native char if not isinstance(array, objects.Array): raise TypeError("Array_to_string takes an Array of char") @@ -39,19 +53,91 @@ def array_to_string( if count is None: count = array.vol.count - return array.cast("string", max_length=count, errors=errors) + return address_to_string( + context=array._context, + layer_name=array.vol.layer_name, + address=array.vol.offset, + count=count, + errors=errors, + block_size=block_size, + ) -def pointer_to_string(pointer: "objects.Pointer", count: int, errors: str = "replace"): - """Takes a volatility Pointer to characters and returns a string.""" +def pointer_to_string( + pointer: "objects.Pointer", + count: int, + errors: str = "replace", + block_size=32, +) -> str: + """Takes a Volatility 'Pointer' to characters and returns a Python string. + + Args: + pointer: A `Pointer` object containing character elements. + count: Optional maximum number of characters to convert. If None, the function + processes the entire array. + errors: Specifies error handling behavior for decoding, defaulting to "replace". + block_size: Reading block size. Defaults to 32 + + Returns: + A decoded string representation of the data referenced by the pointer. + """ if not isinstance(pointer, objects.Pointer): raise TypeError("pointer_to_string takes a Pointer") if count < 1: raise ValueError("pointer_to_string requires a positive count") - char = pointer.dereference() - return char.cast("string", max_length=count, errors=errors) + return address_to_string( + context=pointer._context, + layer_name=pointer.vol.layer_name, + address=pointer, + count=count, + errors=errors, + block_size=block_size, + ) + + +def address_to_string( + context: interfaces.context.ContextInterface, + layer_name: str, + address: int, + count: int, + errors: str = "replace", + block_size=32, +) -> str: + """Reads a null-terminated string from a given specified memory address, processing + it in blocks for efficiency. + + Args: + context: The context used to retrieve memory layers and symbol tables + layer_name: The name of the memory layer to read from + address: The address where the string is located in memory + count: The number of bytes to read + errors: The error handling scheme to use for encoding errors. Defaults to "replace" + block_size: Reading block size. Defaults to 32 + + Returns: + The decoded string extracted from memory. + """ + if not isinstance(address, int): + raise TypeError("It takes an int") + + if count < 1: + raise ValueError("It requires a positive count") + + layer = context.layers[layer_name] + text = b"" + while len(text) <= count: + current_block_size = min(count - len(text), block_size) + temp_text = layer.read(address + len(text), current_block_size) + idx = temp_text.find(b"\x00") + if idx != -1: + temp_text = temp_text[:idx] + text += temp_text + break + text += temp_text + + return text.decode(errors=errors) def array_of_pointers( From 815b2fe918241ab2847512cfc7e9980b0a9e50ed Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 31 Jan 2025 19:49:04 +1100 Subject: [PATCH 3/3] Fix bug in address_to_string() helper --- volatility3/framework/objects/utility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/objects/utility.py b/volatility3/framework/objects/utility.py index dc450c0b3..93216743c 100644 --- a/volatility3/framework/objects/utility.py +++ b/volatility3/framework/objects/utility.py @@ -127,7 +127,7 @@ def address_to_string( layer = context.layers[layer_name] text = b"" - while len(text) <= count: + while len(text) < count: current_block_size = min(count - len(text), block_size) temp_text = layer.read(address + len(text), current_block_size) idx = temp_text.find(b"\x00")