From dab852c22cee8370eb7673164361d44ece185a2b Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 16 Jul 2020 22:35:58 +0100 Subject: [PATCH] CLI: Support hiding data that may be inaccurate --- volatility/cli/__init__.py | 6 +++++- volatility/cli/text_renderer.py | 34 +++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/volatility/cli/__init__.py b/volatility/cli/__init__.py index 2b83fc94c..533cf3c8c 100644 --- a/volatility/cli/__init__.py +++ b/volatility/cli/__init__.py @@ -157,6 +157,10 @@ class CommandLine(interfaces.plugins.FileConsumerInterface): help = "Clears out all short-term cached items", default = False, action = 'store_true') + parser.add_argument("--safe", + help = "Does not display data that is unreadable or unparsable", + default = False, + action = 'store_true') # We have to filter out help, otherwise parse_known_args will trigger the help message before having # processed the plugin choice or had the plugin subparser added. @@ -305,7 +309,7 @@ class CommandLine(interfaces.plugins.FileConsumerInterface): try: # Construct and run the plugin if constructed: - renderers[args.renderer]().render(constructed.run()) + renderers[args.renderer](safe = args.safe).render(constructed.run()) except (exceptions.VolatilityException) as excp: self.process_exceptions(excp) diff --git a/volatility/cli/text_renderer.py b/volatility/cli/text_renderer.py index e81cbedea..8e3687b9e 100644 --- a/volatility/cli/text_renderer.py +++ b/volatility/cli/text_renderer.py @@ -111,6 +111,10 @@ class CLIRenderer(interfaces.renderers.Renderer): name = "unnamed" structured_output = False + def __init__(self, safe: bool = False, *args, **kwargs): + super().__init__(*args, **kwargs) + self._safe = safe + class QuickTextRenderer(CLIRenderer): _type_renderers = { @@ -148,14 +152,21 @@ class QuickTextRenderer(CLIRenderer): def visitor(node, accumulator): accumulator.write("\n") - # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case accumulator.write("*" * max(0, node.path_depth - 1) + ("" if (node.path_depth <= 1) else " ")) + # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case line = [] + skip = False for column_index in range(len(grid.columns)): column = grid.columns[column_index] renderer = self._type_renderers.get(column.type, self._type_renderers['default']) + if isinstance(node.values[column_index], + (renderers.UnparsableValue, renderers.UnreadableValue)) and self._safe: + skip = True line.append(renderer(node.values[column_index])) - accumulator.write("{}".format("\t".join(line))) + if not skip: + accumulator.write("{}".format("\t".join(line))) + else: + accumulator.write("--- Unsafe data ---") accumulator.flush() return accumulator @@ -190,8 +201,10 @@ class CSVRenderer(CLIRenderer): Args: grid: The TreeGrid object to render """ - outfd = sys.stdout + if self._safe: + vollog.info("CSV does not support safe rendering, all records will be returned") + outfd = sys.stdout line = ['"TreeDepth"'] for column in grid.columns: # Ignore the type because namedtuples don't realize they have accessible attributes @@ -250,15 +263,22 @@ class PrettyTextRenderer(CLIRenderer): node, accumulator: List[Tuple[int, Dict[interfaces.renderers.Column, bytes]]] ) -> List[Tuple[int, Dict[interfaces.renderers.Column, bytes]]]: # Nodes always have a path value, giving them a path_depth of at least 1, we use max just in case + skip = False max_column_widths[tree_indent_column] = max(max_column_widths.get(tree_indent_column, 0), node.path_depth) line = {} + skip = False for column_index in range(len(grid.columns)): column = grid.columns[column_index] renderer = self._type_renderers.get(column.type, self._type_renderers['default']) + data = renderer(node.values[column_index]) + if isinstance(node.values[column_index], + (renderers.UnparsableValue, renderers.UnreadableValue)) and self._safe: + skip = True max_column_widths[column.name] = max(max_column_widths.get(column.name, len(column.name)), len("{}".format(data))) line[column] = data + line['__skip'] = skip accumulator.append((node.path_depth, line)) return accumulator @@ -280,7 +300,10 @@ class PrettyTextRenderer(CLIRenderer): column_titles = [""] + [column.name for column in grid.columns] outfd.write(format_string.format(*column_titles)) for (depth, line) in final_output: - outfd.write(format_string.format("*" * depth, *[line[column] for column in grid.columns])) + if line['__skip']: + outfd.write("{}".format("*" * depth, "--- Unsafe data ---")) + else: + outfd.write(format_string.format("*" * depth, *[line[column] for column in grid.columns])) class JsonRenderer(CLIRenderer): @@ -302,6 +325,9 @@ class JsonRenderer(CLIRenderer): outfd.write(json.dumps(result, indent = 2, sort_keys = True)) def render(self, grid: interfaces.renderers.TreeGrid): + if self._safe: + vollog.info("JSON output does not support safe rendering, all records will be returned") + outfd = sys.stdout outfd.write("\n")