From 47ff37b07b79a44a30249a243b095a0edfc1845a Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 31 Oct 2020 23:30:17 +0000 Subject: [PATCH] Plugins: Make layerwiter more configurable This adds support for dumping multiple layers at once, listing available layers and selects the highest non-mapped layer. Mapping is specified in the metadata and currently is only applied to intel layers. --- volatility/framework/layers/intel.py | 1 + volatility/framework/plugins/layerwriter.py | 69 ++++++++++++++------- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/volatility/framework/layers/intel.py b/volatility/framework/layers/intel.py index 25d7d4822..544e6259c 100644 --- a/volatility/framework/layers/intel.py +++ b/volatility/framework/layers/intel.py @@ -28,6 +28,7 @@ class Intel(linear.LinearlyMappedLayer): _maxvirtaddr = _maxphyaddr _structure = [('page directory', 10, False), ('page table', 10, True)] _direct_metadata = collections.ChainMap({'architecture': 'Intel32'}, + {'mapped': True}, interfaces.layers.TranslationLayerInterface._direct_metadata) def __init__(self, diff --git a/volatility/framework/plugins/layerwriter.py b/volatility/framework/plugins/layerwriter.py index 5e3fa04b2..05d2c403b 100644 --- a/volatility/framework/plugins/layerwriter.py +++ b/volatility/framework/plugins/layerwriter.py @@ -3,7 +3,6 @@ # import logging -import os from typing import List, Optional, Type from volatility.framework import renderers, interfaces, constants, exceptions @@ -16,7 +15,6 @@ vollog = logging.getLogger(__name__) class LayerWriter(plugins.PluginInterface): """Runs the automagics and writes out the primary layer produced by the stacker.""" - default_output_name = "output.raw" default_block_size = 0x500000 _required_framework_version = (2, 0, 0) @@ -28,14 +26,19 @@ class LayerWriter(plugins.PluginInterface): requirements.TranslationLayerRequirement(name = 'primary', description = 'Memory layer for the kernel', architectures = ["Intel32", "Intel64"]), - requirements.StringRequirement(name = 'output', - description = 'Filename to output the chosen layer', - optional = True, - default = cls.default_output_name), requirements.IntRequirement(name = 'block_size', description = "Size of blocks to copy over", default = cls.default_block_size, - optional = True) + optional = True), + requirements.BooleanRequirement(name = 'list', + description = 'List available layers', + default = False, + optional = True), + requirements.ListRequirement(name = 'layers', + element_type = str, + description = 'Names of layer to write', + default = None, + optional = True) ] @classmethod @@ -75,24 +78,44 @@ class LayerWriter(plugins.PluginInterface): return file_handle def _generator(self): - if self.config['primary'] not in self.context.layers: - yield 0, ('Layer Name does not exist',) - elif os.path.exists(self.config.get('output', self.default_output_name)): - yield 0, ('Refusing to overwrite existing output file',) + if self.config['list']: + for name in self.context.layers: + yield 0, (name,) else: - output_name = self.config.get('output', self.default_output_name) - try: - file_handle = self.write_layer(self.context, - self.config['primary'], - output_name, - self.open, - self.config.get('block_size', self.default_block_size), - progress_callback = self._progress_callback) - file_handle.close() - except IOError as excp: - yield 0, ('Layer cannot be written to {}: {}'.format(self.config['output_name'], excp),) + import pdb + pdb.set_trace() + # Choose the most recently added layer that isn't virtual + if self.config['layers'] is None: + self.config['layers'] = [] + for name in self.context.layers: + if not self.context.layers[name].metadata.get('mapped', False): + self.config['layers'] = [name] - yield 0, ('Layer has been written to {}'.format(output_name),) + for name in self.config['layers']: + # Check the layer exists and validate the output file + if name not in self.context.layers: + yield 0, ('Layer Name {} does not exist'.format(name),) + else: + output_name = self.config.get('output', ".".join([name, "raw"])) + try: + file_handle = self.write_layer(self.context, + name, + output_name, + self.open, + self.config.get('block_size', self.default_block_size), + progress_callback = self._progress_callback) + file_handle.close() + except IOError as excp: + yield 0, ('Layer cannot be written to {}: {}'.format(self.config['output_name'], excp),) + + yield 0, ('Layer has been written to {}'.format(output_name),) + + def _generate_layers(self): + """List layer names from this run""" + for name in self.context.layers: + yield (0, (name,)) def run(self): + if self.config['list']: + return renderers.TreeGrid([("Layer name", str)], self._generate_layers()) return renderers.TreeGrid([("Status", str)], self._generator())