From 42a97dd10cb04e836e0be372eaf08bf147f59dc4 Mon Sep 17 00:00:00 2001 From: Dave Lassalle Date: Tue, 14 Jul 2020 22:02:31 -0500 Subject: [PATCH] Registry: handle ints, multi strings, and binary data with StrLike --- volatility/cli/text_renderer.py | 6 ++++ .../plugins/windows/registry/printkey.py | 32 +++++++++++++------ .../framework/renderers/format_hints.py | 12 +++++-- .../symbols/windows/extensions/registry.py | 6 ++-- 4 files changed, 42 insertions(+), 14 deletions(-) diff --git a/volatility/cli/text_renderer.py b/volatility/cli/text_renderer.py index 2def90361..5e1d55b8f 100644 --- a/volatility/cli/text_renderer.py +++ b/volatility/cli/text_renderer.py @@ -58,7 +58,11 @@ def strlike_as_text(value: format_hints.StrLike) -> str: This attempts to convert the string based on its encoding and if no data's been lost due to the split on the null character, then it displays it as is """ + if value.show_hex: + return hex_bytes_as_text(value) string_representation = str(value, encoding = value.encoding, errors = 'replace') + if value.split_nulls and ((len(value) / 2 - 1) <= len(string_representation) <= (len(value) / 2)): + return "\n".join(string_representation.split("\x00")) if len(string_representation) - 1 <= len(string_representation.split("\x00")[0]) <= len(string_representation): return string_representation.split("\x00")[0] return hex_bytes_as_text(value) @@ -85,6 +89,8 @@ def quoted_optional(func): result = optional(func)(x) if result == "-" or result == "N/A": return "" + if isinstance(x, format_hints.StrLike) and x.converted_int: + return "{}".format(result) if isinstance(x, int) and not isinstance(x, (format_hints.Hex, format_hints.Bin)): return "{}".format(result) return "\"{}\"".format(result) diff --git a/volatility/framework/plugins/windows/registry/printkey.py b/volatility/framework/plugins/windows/registry/printkey.py index b416667b1..1e7d91516 100644 --- a/volatility/framework/plugins/windows/registry/printkey.py +++ b/volatility/framework/plugins/windows/registry/printkey.py @@ -66,7 +66,8 @@ class PrintKey(interfaces.plugins.PluginInterface): vollog.warning("Hive walker was not passed a valid node_path (or None)") return node = node_path[-1] - key_path = '\\'.join([k.get_name() for k in node_path]) + key_path_items = [hive] + node_path[1:] + key_path = '\\'.join([k.get_name() for k in key_path_items]) if node.vol.type_name.endswith(constants.BANG + '_CELL_DATA'): raise RegistryFormatException(hive.name, "Encountered _CELL_DATA instead of _CM_KEY_NODE") last_write_time = conversion.wintime_to_datetime(node.LastWriteTime.QuadPart) @@ -113,7 +114,7 @@ class PrintKey(interfaces.plugins.PluginInterface): key_node_name = renderers.UnreadableValue() yield (depth, (last_write_time, renderers.format_hints.Hex(hive.hive_offset), "Key", key_path, - key_node_name, "", volatile)) + key_node_name, renderers.NotApplicableValue(), volatile)) else: try: value_node_name = node.get_name() or "(Default)" @@ -121,20 +122,33 @@ class PrintKey(interfaces.plugins.PluginInterface): vollog.debug(excp) value_node_name = renderers.UnreadableValue() - try: - value_data = node.decode_data() # type: Union[interfaces.renderers.BaseAbsentValue, bytes] - except (ValueError, exceptions.InvalidAddressException, RegistryFormatException) as excp: - vollog.debug(excp) - value_data = renderers.UnreadableValue() - try: value_type = RegValueTypes.get(node.Type).name except (exceptions.InvalidAddressException, RegistryFormatException) as excp: vollog.debug(excp) value_type = renderers.UnreadableValue() + if isinstance(value_type, renderers.UnreadableValue): + vollog.debug("Couldn't read registry value type, so data is unreadable") + value_data = renderers.UnreadableValue() + else: + try: + value_data = node.decode_data() # type: Union[interfaces.renderers.BaseAbsentValue, bytes] + + if isinstance(value_data, int): + value_data = format_hints.StrLike(value_data, encoding='utf-8') + elif RegValueTypes.get(node.Type) == RegValueTypes.REG_BINARY: + value_data = format_hints.StrLike(value_data, show_hex=True) + elif RegValueTypes.get(node.Type) == RegValueTypes.REG_MULTI_SZ: + value_data = format_hints.StrLike(value_data, encoding='utf-16-le', split_nulls=True) + else: + value_data = format_hints.StrLike(value_data, encoding='utf-16-le') + except (ValueError, exceptions.InvalidAddressException, RegistryFormatException) as excp: + vollog.debug(excp) + value_data = renderers.UnreadableValue() + result = (depth, (last_write_time, renderers.format_hints.Hex(hive.hive_offset), value_type, key_path, - value_node_name, format_hints.StrLike(value_data, encoding = 'utf-16-le'), volatile)) + value_node_name, value_data, volatile)) yield result def _registry_walker(self, diff --git a/volatility/framework/renderers/format_hints.py b/volatility/framework/renderers/format_hints.py index ab5641a08..d592033c1 100644 --- a/volatility/framework/renderers/format_hints.py +++ b/volatility/framework/renderers/format_hints.py @@ -28,9 +28,17 @@ class HexBytes(bytes): class StrLike(bytes): """The contents are supposed to be a string, but may contain binary data.""" - def __new__(cls, original, encoding: str = 'utf-16-le'): + def __new__(cls, original, encoding: str = 'utf-16-le', split_nulls: bool = False, show_hex: bool = False): + if isinstance(original, int): + original = str(original).encode(encoding) return super().__new__(cls, original) - def __init__(self, original: bytes, encoding: str = 'utf-16-le'): + def __init__(self, original: bytes, encoding: str = 'utf-16-le', split_nulls: bool = False, show_hex: bool = False): + if isinstance(original, int): + self.converted_int = True + else: + self.converted_int = False self.encoding = encoding + self.split_nulls = split_nulls + self.show_hex = show_hex bytes.__init__(original) diff --git a/volatility/framework/symbols/windows/extensions/registry.py b/volatility/framework/symbols/windows/extensions/registry.py index 76e8c5ea6..cf15e6c18 100644 --- a/volatility/framework/symbols/windows/extensions/registry.py +++ b/volatility/framework/symbols/windows/extensions/registry.py @@ -5,7 +5,7 @@ import enum import logging import struct -from typing import Optional, Iterable +from typing import Optional, Iterable, Union from volatility.framework import constants, exceptions, objects, interfaces from volatility.framework.layers.registry import RegistryHive, RegistryInvalidIndex, RegistryFormatException @@ -239,7 +239,7 @@ class CM_KEY_VALUE(objects.StructType): self.Name.count = namelength return self.Name.cast("string", max_length = namelength, encoding = "latin-1") - def decode_data(self) -> bytes: + def decode_data(self) -> Union[int, bytes]: """Properly decodes the data associated with the value node""" # Determine if the data is stored inline datalen = self.DataLength @@ -298,4 +298,4 @@ class CM_KEY_VALUE(objects.StructType): # Fall back if it's something weird vollog.debug("Unknown registry value type encountered: {}".format(self.Type)) - return data.hex() + return data