Windows: Refactor names of FileInterfaces

This commit is contained in:
Mike Auty
2020-10-29 09:43:16 +00:00
committed by ikelos
parent 3bd462720b
commit b703210024
14 changed files with 95 additions and 82 deletions
+6 -3
View File
@@ -465,6 +465,7 @@ class CommandLine:
return
self.seek(0)
if output_dir is None:
raise TypeError("Output directory is not a string")
os.makedirs(output_dir, exist_ok = True)
@@ -473,13 +474,15 @@ class CommandLine:
filename, extension = os.path.join(output_dir, '.'.join(pref_name_array[:-1])), pref_name_array[-1]
output_filename = "{}.{}".format(filename, extension)
if not os.path.exists(output_filename):
counter = 1
while os.path.exists(output_filename):
output_filename = "{}-{}.{}".format(filename, counter, extension)
counter += 1
with open(output_filename, "wb") as current_file:
current_file.write(self.read())
self._committed = True
vollog.log(logging.INFO, "Saved stored plugin file: {}".format(output_filename))
else:
vollog.warning("Refusing to overwrite an existing file: {}".format(output_filename))
super().close()
return CLIFileHandler
+19 -11
View File
@@ -20,11 +20,13 @@ vollog = logging.getLogger(__name__)
class FileHandlerInterface(IO[bytes]):
"""Class for storing Files in the plugin as a means to output a file or
files when necessary."""
"""Class for storing Files in the plugin as a means to output a file when necessary.
This can be used as ContextManager that will close/produce the file automatically when exiting the context block
"""
def __init__(self, filename: str) -> None:
"""Creates a FileTemplate
"""Creates a FileHandler
Args:
filename: The requested name of the filename for the data
@@ -35,19 +37,26 @@ class FileHandlerInterface(IO[bytes]):
@property
def preferred_filename(self):
"""The preferred filename to save the data to.
Until this file has been written, this value may not be the final filename the data is written to.
"""
return self._preferred_filename
@preferred_filename.setter
def preferred_filename(self, filename):
"""Sets the preferred filename"""
if self.closed:
raise IOError("FileTemplate name cannot be changed once closed")
raise IOError("FileHandler name cannot be changed once closed")
if not isinstance(filename, str):
raise TypeError("FileTemplateInterface preferred filenames must be strings")
raise TypeError("FileHandler preferred filenames must be strings")
if os.path.sep in filename:
raise ValueError("FileTemplateInterface filenames cannot contain path separators")
raise ValueError("FileHandler filenames cannot contain path separators")
self._preferred_filename = filename
@abstractmethod
def close(self):
"""Method that commits the file and fixes the final filename for use"""
def __enter__(self):
return self
@@ -115,11 +124,10 @@ class PluginInterface(interfaces.configuration.ConfigurableInterface,
framework.require_interface_version(*self._required_framework_version)
def open(self, preferred_filename: str) -> FileHandlerInterface:
"""Opens a file for output in bytes mode"""
if self._file_handler is not None:
return self._file_handler(preferred_filename)
raise IOError("FileTemplate not specified for this plugin")
@property
def open(self):
"""Returns a context manager and thus can be called like open"""
return self._file_handler
def set_file_handler(self, handler: Type[FileHandlerInterface]) -> None:
"""Sets the file handler to be used by this plugin."""
+4 -4
View File
@@ -39,10 +39,10 @@ class ConfigWriter(plugins.PluginInterface):
config = dict(self.context.config)
filename = "config.extra"
try:
with self.open(filename) as filedata:
filedata.write(bytes(json.dumps(config, sort_keys = True, indent = 2), 'raw_unicode_escape'))
except Exception:
vollog.warning("Unable to JSON encode configuration")
with self.open(filename) as file_data:
file_data.write(bytes(json.dumps(config, sort_keys = True, indent = 2), 'raw_unicode_escape'))
except Exception as excp:
vollog.warning("Unable to JSON encode configuration: {}".format(excp))
for k, v in config.items():
yield (0, (k, json.dumps(v)))
+3 -3
View File
@@ -47,7 +47,7 @@ class LayerWriter(plugins.PluginInterface):
chunk_size: Optional[int] = None,
progress_callback: Optional[constants.ProgressCallback] = None) -> Optional[
plugins.FileHandlerInterface]:
"""Produces a filedata from the named layer in the provided context
"""Produces a FileHandler from the named layer in the provided context or None on failure
Args:
context: the context from which to read the memory layer
@@ -65,11 +65,11 @@ class LayerWriter(plugins.PluginInterface):
chunk_size = cls.default_block_size
filehandler = file_handler(preferred_name)
with filehandler as filedata:
with filehandler as file_data:
for i in range(0, layer.maximum_address, chunk_size):
current_chunk_size = min(chunk_size, layer.maximum_address - i)
data = layer.read(i, current_chunk_size, pad = True)
filedata.write(data)
file_data.write(data)
if progress_callback:
progress_callback((i / layer.maximum_address) * 100, 'Writing layer {}'.format(layer_name))
return filehandler
+4 -4
View File
@@ -138,8 +138,8 @@ class Timeliner(interfaces.plugins.PluginInterface):
# Write out a body file if necessary
if self.config.get('create-bodyfile', True):
with self.open("volatility.body") as filedata:
with io.TextIOWrapper(filedata, write_through = True) as fp:
with self.open("volatility.body") as file_data:
with io.TextIOWrapper(file_data, write_through = True) as fp:
for (plugin_name, item) in self.timeline:
times = self.timeline[(plugin_name, item)]
# Body format is: MD5|name|inode|mode_as_string|UID|GID|size|atime|mtime|ctime|crtime
@@ -203,8 +203,8 @@ class Timeliner(interfaces.plugins.PluginInterface):
for entry in old_dict:
total_config[interfaces.configuration.path_join(plugin.__class__.__name__, entry)] = old_dict[entry]
with self.open("config.json") as filedata:
with io.TextIOWrapper(filedata, write_through = True) as fp:
with self.open("config.json") as file_data:
with io.TextIOWrapper(file_data, write_through = True) as fp:
json.dump(total_config, fp, sort_keys = True, indent = 2)
return renderers.TreeGrid(columns = [("Plugin", str), ("Description", str), ("Created Date", datetime.datetime),
+13 -13
View File
@@ -71,23 +71,23 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
if layer_name is None:
layer_name = dll_entry.vol.layer_name
filehandler = file_handler("{}{}.{:#x}.{:#x}.dmp".format(prefix,
ntpath.basename(name),
dll_entry.vol.offset,
dll_entry.DllBase))
file_handler = file_handler("{}{}.{:#x}.{:#x}.dmp".format(prefix,
ntpath.basename(name),
dll_entry.vol.offset,
dll_entry.DllBase))
dos_header = context.object(pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
offset = dll_entry.DllBase,
layer_name = layer_name)
with filehandler as filedata:
with file_handler as file_data:
for offset, data in dos_header.reconstruct():
filedata.seek(offset)
filedata.write(data)
except (IOError, exceptions.VolatilityException, OverflowError) as excp:
file_data.seek(offset)
file_data.write(data)
except (IOError, exceptions.VolatilityException, OverflowError, ValueError) as excp:
vollog.debug("Unable to dump dll at offset {}: {}".format(dll_entry.DllBase, excp))
return None
return filehandler
return file_handler
def _generator(self, procs):
pe_table_name = intermed.IntermediateSymbolTable.create(self.context,
@@ -128,11 +128,11 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
file_output = "Disabled"
if self.config['dump']:
filedata = self.dump_pe(self.context, pe_table_name, entry, self._file_handler,
proc_layer_name, prefix = "pid.{}.".format(proc_id))
file_handler = self.dump_pe(self.context, pe_table_name, entry, self._file_handler,
proc_layer_name, prefix = "pid.{}.".format(proc_id))
file_output = "Error outputting file"
if filedata:
file_output = filedata.preferred_filename
if file_handler:
file_output = file_handler.preferred_filename
yield (0, (proc.UniqueProcessId,
proc.ImageFileName.cast("string",
@@ -36,7 +36,7 @@ class Malfind(interfaces.plugins.PluginInterface):
default = False,
optional = True),
requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (2, 0, 0)),
requirements.VersionRequirement(name = 'vadinfo', component = vadinfo.VadInfo, version = (1, 1, 0))
requirements.VersionRequirement(name = 'vadinfo', component = vadinfo.VadInfo, version = (2, 0, 0))
]
@classmethod
@@ -135,8 +135,8 @@ class Malfind(interfaces.plugins.PluginInterface):
if self.config['dump']:
file_output = "Error outputting to file"
try:
filedata = vadinfo.VadInfo.vad_dump(self.context, proc, vad, self._file_handler)
file_output = filedata.preferred_filename
file_handler = vadinfo.VadInfo.vad_dump(self.context, proc, vad, self._file_handler)
file_output = file_handler.preferred_filename
except (exceptions.InvalidAddressException, OverflowError) as excp:
vollog.debug("Unable to dump PE with pid {0}.{1:#x}: {2}".format(proc.UniqueProcessId,
vad.get_start(), excp))
@@ -48,7 +48,8 @@ class Memmap(interfaces.plugins.PluginInterface):
excp.layer_name))
continue
with self.open("pid.{}.dmp".format(pid)) as filedata:
file_handler = self.open("pid.{}.dmp".format(pid))
with file_handler as file_data:
for mapval in proc_layer.mapping(0x0, proc_layer.maximum_address, ignore_errors = True):
offset, size, mapped_offset, mapped_size, maplayer = mapval
@@ -57,12 +58,12 @@ class Memmap(interfaces.plugins.PluginInterface):
if self.config['dump']:
try:
data = proc_layer.read(offset, size, pad = True)
filedata.write(data)
file_output = filedata.preferred_filename
file_data.write(data)
file_output = file_handler.preferred_filename
except exceptions.InvalidAddressException:
file_output = "Error outputting to file"
vollog.debug("Unable to write {}'s address {} to {}.dmp".format(proc_layer_name, offset,
filedata.preferred_filename))
vollog.debug("Unable to write {}'s address {} to {}".format(proc_layer_name, offset,
file_handler.preferred_filename))
yield (0, (
format_hints.Hex(offset),
@@ -79,10 +79,10 @@ class ModScan(interfaces.plugins.PluginInterface):
file_output = "Disabled"
if self.config['dump']:
filedata = dlllist.DllList.dump_pe(self.context, pe_table_name, mod, self._file_handler)
file_handler = dlllist.DllList.dump_pe(self.context, pe_table_name, mod, self.open)
file_output = "Error outputting file"
if filedata:
file_output = filedata.preferred_filename
if file_handler:
file_output = file_handler.preferred_filename
yield (0, (
format_hints.Hex(mod.vol.offset),
@@ -58,10 +58,10 @@ class Modules(interfaces.plugins.PluginInterface):
file_output = "Disabled"
if self.config['dump']:
filedata = dlllist.DllList.dump_pe(self.context, pe_table_name, mod, self._file_handler)
file_handler = dlllist.DllList.dump_pe(self.context, pe_table_name, mod, self._file_handler)
file_output = "Error outputting file"
if filedata:
file_output = filedata.preferred_filename
if file_handler:
file_output = file_handler.preferred_filename
yield (0, (
format_hints.Hex(mod.vol.offset),
@@ -74,15 +74,15 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
dos_header = context.object(pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
offset = peb.ImageBaseAddress,
layer_name = proc_layer_name)
filehandler = file_handler("pid.{0}.{1:#x}.dmp".format(proc.UniqueProcessId, peb.ImageBaseAddress))
with filehandler as filedata:
file_handler = file_handler("pid.{0}.{1:#x}.dmp".format(proc.UniqueProcessId, peb.ImageBaseAddress))
with file_handler as file_data:
for offset, data in dos_header.reconstruct():
filedata.seek(offset)
filedata.write(data)
file_data.seek(offset)
file_data.write(data)
except Exception as excp:
vollog.debug("Unable to dump PE with pid {}: {}".format(proc.UniqueProcessId, excp))
return filehandler
return file_handler
@classmethod
def create_pid_filter(cls, pid_list: List[int] = None) -> Callable[[interfaces.objects.ObjectInterface], bool]:
@@ -190,11 +190,11 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
file_output = "Disabled"
if self.config['dump']:
filedata = self.process_dump(self.context, self.config['nt_symbols'], pe_table_name, proc,
self._file_handler)
file_handler = self.process_dump(self.context, self.config['nt_symbols'], pe_table_name, proc,
self._file_handler)
file_output = "Error outputting file"
if filedata:
file_output = filedata.preferred_filename
if file_handler:
file_output = file_handler.preferred_filename
yield (0, (proc.UniqueProcessId, proc.InheritedFromUniqueProcessId,
proc.ImageFileName.cast("string", max_length = proc.ImageFileName.vol.count, errors = 'replace'),
@@ -82,20 +82,21 @@ class HiveList(interfaces.plugins.PluginInterface):
maxaddr = hive.hive.Storage[0].Length
hive_name = self._sanitize_hive_name(hive.get_name())
with self.open('registry.{}.{}.hive'.format(hive_name, hex(hive.hive_offset))) as filedata:
file_handler = self.open('registry.{}.{}.hive'.format(hive_name, hex(hive.hive_offset)))
with file_handler as file_data:
if hive._base_block:
hive_data = self.context.layers[hive.dependencies[0]].read(hive.hive.BaseBlock, 1 << 12)
else:
hive_data = '\x00' * (1 << 12)
filedata.write(hive_data)
file_data.write(hive_data)
for i in range(0, maxaddr, chunk_size):
current_chunk_size = min(chunk_size, maxaddr - i)
data = hive.read(i, current_chunk_size, pad = True)
filedata.write(data)
file_data.write(data)
# if self._progress_callback:
# self._progress_callback((i / maxaddr) * 100, 'Writing layer {}'.format(hive_name))
file_output = filedata.preferred_filename
file_output = file_handler.preferred_filename
yield (0, (format_hints.Hex(hive_object.vol.offset), hive_object.get_name() or "", file_output))
+15 -15
View File
@@ -34,7 +34,7 @@ class VadInfo(interfaces.plugins.PluginInterface):
"""Lists process memory ranges."""
_required_framework_version = (2, 0, 0)
_version = (1, 1, 0)
_version = (2, 0, 0)
MAXSIZE_DEFAULT = 0
def __init__(self, *args, **kwargs):
@@ -111,7 +111,7 @@ class VadInfo(interfaces.plugins.PluginInterface):
def vad_dump(cls,
context: interfaces.context.ContextInterface,
proc: interfaces.objects.ObjectInterface,
vad: interfaces.objects.ObjectInterface,
vad: interfaces.objects.ObjectInterface,
file_handler: Type[
interfaces.plugins.FileHandlerInterface]) -> Optional[interfaces.plugins.FileHandlerInterface]:
"""Extracts the complete data for Vad as a FileInterface.
@@ -150,16 +150,16 @@ class VadInfo(interfaces.plugins.PluginInterface):
file_name = "pid.{0}.vad.{1:#x}-{2:#x}.dmp".format(proc_id, vad_start, vad_end)
try:
file_handler = file_handler(file_name)
with file_handler as filedata:
chunk_size = 1024 * 1024 * 10
offset = vad_start
while offset < vad_end:
to_read = min(chunk_size, vad_end - offset)
data = proc_layer.read(offset, to_read, pad = True)
if not data:
break
filedata.write(data)
offset += to_read
with file_handler as file_data:
chunk_size = 1024 * 1024 * 10
offset = vad_start
while offset < vad_end:
to_read = min(chunk_size, vad_end - offset)
data = proc_layer.read(offset, to_read, pad = True)
if not data:
break
file_data.write(data)
offset += to_read
except Exception as excp:
vollog.debug("Unable to dump VAD {}: {}".format(file_name, excp))
@@ -188,10 +188,10 @@ class VadInfo(interfaces.plugins.PluginInterface):
file_output = "Disabled"
if self.config['dump']:
filedata = self.vad_dump(self.context, proc, vad, self._file_handler)
file_handler = self.vad_dump(self.context, proc, vad, self._file_handler)
file_output = "Error outputting file"
if filedata:
file_output = filedata.preferred_filename
if file_handler:
file_output = file_handler.preferred_filename
yield (0, (proc.UniqueProcessId, process_name, format_hints.Hex(vad.vol.offset),
format_hints.Hex(vad.get_start()), format_hints.Hex(vad.get_end()), vad.get_tag(),
@@ -58,8 +58,8 @@ class Certificates(interfaces.plugins.PluginInterface):
if not isinstance(certificate_data, interfaces.renderers.BaseAbsentValue):
with self.open("{} - {} - {}.crt".format(
hex(hive.hive_offset), reg_section, key_hash)) as filedata:
filedata.write(certificate_data)
hex(hive.hive_offset), reg_section, key_hash)) as file_data:
file_data.write(certificate_data)
yield (0, (top_key, reg_section, key_hash, name))
except KeyError:
# Key wasn't found in this hive, carry on