From 8ecd7e2ddddb018164d3ef734899b91a93899d09 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Thu, 28 Nov 2024 15:17:22 +1100 Subject: [PATCH 1/3] Linux/Mac: Log producer information --- .../framework/automagic/symbol_finder.py | 31 ++++++++++++++++--- volatility3/framework/symbols/intermed.py | 15 ++++++--- volatility3/framework/symbols/metadata.py | 13 +++++++- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/volatility3/framework/automagic/symbol_finder.py b/volatility3/framework/automagic/symbol_finder.py index 21e594549..55e2ad6f5 100644 --- a/volatility3/framework/automagic/symbol_finder.py +++ b/volatility3/framework/automagic/symbol_finder.py @@ -142,11 +142,11 @@ class SymbolFinder(interfaces.automagic.AutomagicInterface): ) for _, banner in banner_list: - vollog.debug(f"Identified banner: {repr(banner)}") - symbol_files = self.banners.get(banner, None) - if symbol_files: - isf_path = symbol_files - vollog.debug(f"Using symbol library: {symbol_files}") + vollog.debug(f"Identified banner: {banner!r}") + symbols_file = self.banners.get(banner, None) + if symbols_file: + isf_path = symbols_file + vollog.debug(f"Using symbol library: {symbols_file}") clazz = self.symbol_class # Set the discovered options path_join = interfaces.configuration.path_join @@ -160,8 +160,29 @@ class SymbolFinder(interfaces.automagic.AutomagicInterface): path_join(config_path, requirement.name, "symbol_mask") ] = layer.address_mask + # Keep track of the existing table names so we know which ones were added + old_table_names = set(context.symbol_space._dict) + # Construct the appropriate symbol table requirement.construct(context, config_path) + + new_table_names = context.symbol_space._dict.keys() - old_table_names + # It should add only one symbol table. Ignore the next steps if it doesn't + if len(new_table_names) == 1: + new_table_name = new_table_names.pop() + symbol_table = context.symbol_space._dict[new_table_name] + producer = symbol_table.producer + vollog.debug( + f"producer_name: {producer.name}, producer_version: {producer.version_string}" + ) + for category in symbol_table.metadata._json_data: + vollog.debug(f"{category}:") + for subkey in symbol_table.metadata._json_data[category]: + subkey_item = ", ".join( + f"{key}: '{value}'" for key, value in subkey.items() + ) + vollog.debug(f"\t{subkey_item}") + break else: vollog.debug(f"Symbol library path not found for: {banner}") diff --git a/volatility3/framework/symbols/intermed.py b/volatility3/framework/symbols/intermed.py index 751f88e39..5f558bf12 100644 --- a/volatility3/framework/symbols/intermed.py +++ b/volatility3/framework/symbols/intermed.py @@ -738,10 +738,17 @@ class Version6Format(Version5Format): @property def metadata(self) -> Optional[interfaces.symbols.MetadataInterface]: """Returns a MetadataInterface object.""" - if self._json_object.get("metadata", {}).get("windows"): - return metadata.WindowsMetadata(self._json_object["metadata"]["windows"]) - if self._json_object.get("metadata", {}).get("linux"): - return metadata.LinuxMetadata(self._json_object["metadata"]["linux"]) + if "metadata" not in self._json_object: + return None + + json_metadata = self._json_object["metadata"] + if "windows" in json_metadata: + return metadata.WindowsMetadata(json_metadata["windows"]) + if "linux" in json_metadata: + return metadata.LinuxMetadata(json_metadata["linux"]) + if "mac" in json_metadata: + return metadata.MacMetadata(json_metadata["mac"]) + return None diff --git a/volatility3/framework/symbols/metadata.py b/volatility3/framework/symbols/metadata.py index 95f542f07..39ddd6544 100644 --- a/volatility3/framework/symbols/metadata.py +++ b/volatility3/framework/symbols/metadata.py @@ -18,10 +18,17 @@ class ProducerMetadata(interfaces.symbols.MetadataInterface): def name(self) -> Optional[str]: return self._json_data.get("name", None) + @property + def version_string(self) -> str: + """Returns the ISF file producer's version as a string. + If no version is present, an empty string is returned. + """ + return self._json_data.get("version", "") + @property def version(self) -> Optional[Tuple[int]]: """Returns the version of the ISF file producer""" - version = self._json_data.get("version", None) + version = self.version_string() if not version: return None if all(x in "0123456789." for x in version): @@ -81,3 +88,7 @@ class WindowsMetadata(interfaces.symbols.MetadataInterface): class LinuxMetadata(interfaces.symbols.MetadataInterface): """Class to handle the metadata from a Linux symbol table.""" + + +class MacMetadata(interfaces.symbols.MetadataInterface): + """Class to handle the metadata from a Mac symbol table.""" From b8023f0c97ae97253ab9b8eae99e1dc4cba1eb79 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 29 Nov 2024 19:05:27 +1100 Subject: [PATCH 2/3] Linux/Mac: Address code review suggestions - Add getters for Linux/Mac ISF sources - Avoid using internal attributes - Use the dict repr instead of walking the dict to simplify code --- .../framework/automagic/symbol_finder.py | 26 ++++++++++--------- volatility3/framework/symbols/metadata.py | 19 +++++++++++--- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/volatility3/framework/automagic/symbol_finder.py b/volatility3/framework/automagic/symbol_finder.py index 55e2ad6f5..6d689e194 100644 --- a/volatility3/framework/automagic/symbol_finder.py +++ b/volatility3/framework/automagic/symbol_finder.py @@ -161,27 +161,29 @@ class SymbolFinder(interfaces.automagic.AutomagicInterface): ] = layer.address_mask # Keep track of the existing table names so we know which ones were added - old_table_names = set(context.symbol_space._dict) + old_table_names = set(context.symbol_space) # Construct the appropriate symbol table requirement.construct(context, config_path) - new_table_names = context.symbol_space._dict.keys() - old_table_names + new_table_names = set(context.symbol_space) - old_table_names # It should add only one symbol table. Ignore the next steps if it doesn't if len(new_table_names) == 1: new_table_name = new_table_names.pop() - symbol_table = context.symbol_space._dict[new_table_name] - producer = symbol_table.producer + symbol_table = context.symbol_space[new_table_name] + producer_metadata = symbol_table.producer vollog.debug( - f"producer_name: {producer.name}, producer_version: {producer.version_string}" + f"producer_name: {producer_metadata.name}, producer_version: {producer_metadata.version_string}" ) - for category in symbol_table.metadata._json_data: - vollog.debug(f"{category}:") - for subkey in symbol_table.metadata._json_data[category]: - subkey_item = ", ".join( - f"{key}: '{value}'" for key, value in subkey.items() - ) - vollog.debug(f"\t{subkey_item}") + + symbol_metadata = symbol_table.metadata + vollog.debug("Types:") + for types_source_dict in symbol_metadata.get_types_sources(): + vollog.debug(f"\t{types_source_dict}") + + vollog.debug("Symbols:") + for symbol_source_dict in symbol_metadata.get_symbols_sources(): + vollog.debug(f"\t{symbol_source_dict}") break else: diff --git a/volatility3/framework/symbols/metadata.py b/volatility3/framework/symbols/metadata.py index 39ddd6544..02e9cc489 100644 --- a/volatility3/framework/symbols/metadata.py +++ b/volatility3/framework/symbols/metadata.py @@ -4,8 +4,7 @@ import datetime import logging -from typing import Optional, Tuple, Union - +from typing import Optional, Tuple, Union, List, Dict from volatility3.framework import constants, interfaces vollog = logging.getLogger(__name__) @@ -86,9 +85,21 @@ class WindowsMetadata(interfaces.symbols.MetadataInterface): return self._json_data.get("pdb", {}).get("age", None) -class LinuxMetadata(interfaces.symbols.MetadataInterface): +class DwarfMetadata(interfaces.symbols.MetadataInterface): + """Base class to handle metadata of DWARF-based ISF sources""" + + def get_types_sources(self) -> List[Optional[Dict]]: + """Returns the types sources metadata""" + return self._json_data.get("types", []) + + def get_symbols_sources(self) -> List[Optional[Dict]]: + """Returns the symbols sources metadata""" + return self._json_data.get("symbols", []) + + +class LinuxMetadata(DwarfMetadata): """Class to handle the metadata from a Linux symbol table.""" -class MacMetadata(interfaces.symbols.MetadataInterface): +class MacMetadata(DwarfMetadata): """Class to handle the metadata from a Mac symbol table.""" From 77778ee6f6cfaa9a9af1d73a225c67a8836727c7 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 29 Nov 2024 19:35:18 +1100 Subject: [PATCH 3/3] Linux/Mac: ISF metadata: Rename s/DWARF/POSIX/, as I'm not happy with the generic name. BTF source could potentially generate the same keys --- volatility3/framework/symbols/metadata.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/symbols/metadata.py b/volatility3/framework/symbols/metadata.py index 02e9cc489..73ad2cf21 100644 --- a/volatility3/framework/symbols/metadata.py +++ b/volatility3/framework/symbols/metadata.py @@ -85,8 +85,8 @@ class WindowsMetadata(interfaces.symbols.MetadataInterface): return self._json_data.get("pdb", {}).get("age", None) -class DwarfMetadata(interfaces.symbols.MetadataInterface): - """Base class to handle metadata of DWARF-based ISF sources""" +class PosixMetadata(interfaces.symbols.MetadataInterface): + """Base class to handle metadata of Posix-based ISF sources""" def get_types_sources(self) -> List[Optional[Dict]]: """Returns the types sources metadata""" @@ -97,9 +97,9 @@ class DwarfMetadata(interfaces.symbols.MetadataInterface): return self._json_data.get("symbols", []) -class LinuxMetadata(DwarfMetadata): +class LinuxMetadata(PosixMetadata): """Class to handle the metadata from a Linux symbol table.""" -class MacMetadata(DwarfMetadata): +class MacMetadata(PosixMetadata): """Class to handle the metadata from a Mac symbol table."""