mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-28 20:44:52 +02:00
Plugins: Introduce FileHandlerInterface
This commit is contained in:
@@ -168,7 +168,7 @@ be called whenever a plugin produces an auxiliary file.
|
||||
::
|
||||
|
||||
constructed = plugin(context, plugin_config_path, progress_callback = progress_callback)
|
||||
constructed.set_file_consumer(file_consumer)
|
||||
constructed.set_file_handler(file_consumer)
|
||||
|
||||
The file_consumer must adhere to the :py:class:`~volatility.framework.interfaces.plugins.FileConsumerInterface`,
|
||||
which has a `consume_file` method that takes a :py:class:`~volatility.framework.interfaces.plugins.FileInterface`
|
||||
|
||||
+26
-11
@@ -12,6 +12,7 @@ User interfaces make use of the framework to:
|
||||
"""
|
||||
import argparse
|
||||
import inspect
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
@@ -66,7 +67,7 @@ class MuteProgress(PrintedProgress):
|
||||
pass
|
||||
|
||||
|
||||
class CommandLine(interfaces.plugins.FileConsumerInterface):
|
||||
class CommandLine:
|
||||
"""Constructs a command-line interface object for users to run plugins."""
|
||||
|
||||
CLI_NAME = 'volatility'
|
||||
@@ -85,7 +86,7 @@ class CommandLine(interfaces.plugins.FileConsumerInterface):
|
||||
"""Executes the command line module, taking the system arguments,
|
||||
determining the plugin to run and then running it."""
|
||||
|
||||
volatility.framework.require_interface_version(1, 0, 0)
|
||||
volatility.framework.require_interface_version(2, 0, 0)
|
||||
|
||||
renderers = dict([(x.name.lower(), x) for x in framework.class_subclasses(text_renderer.CLIRenderer)])
|
||||
|
||||
@@ -296,7 +297,8 @@ class CommandLine(interfaces.plugins.FileConsumerInterface):
|
||||
if args.quiet:
|
||||
progress_callback = MuteProgress()
|
||||
|
||||
constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback, self)
|
||||
constructed = plugins.construct_plugin(ctx, automagics, plugin, base_config_path, progress_callback,
|
||||
self.file_handler_class_factory())
|
||||
|
||||
if args.write_config:
|
||||
vollog.debug("Writing out configuration data to config.json")
|
||||
@@ -449,22 +451,35 @@ class CommandLine(interfaces.plugins.FileConsumerInterface):
|
||||
extended_path = interfaces.configuration.path_join(config_path, requirement.name)
|
||||
context.config[extended_path] = value
|
||||
|
||||
def consume_file(self, filedata: interfaces.plugins.FileInterface):
|
||||
"""Consumes a file as produced by a plugin."""
|
||||
if self.output_dir is None:
|
||||
raise TypeError("Output directory is not a string")
|
||||
os.makedirs(self.output_dir, exist_ok = True)
|
||||
def file_handler_class_factory(self):
|
||||
output_dir = self.output_dir
|
||||
|
||||
pref_name_array = filedata.preferred_filename.split('.')
|
||||
filename, extension = os.path.join(self.output_dir, '.'.join(pref_name_array[:-1])), pref_name_array[-1]
|
||||
class CLIFileHandler(io.BytesIO, interfaces.plugins.FileHandlerInterface):
|
||||
def __init__(self, filename: str, immediate_commit: bool = False):
|
||||
io.BytesIO.__init__(self)
|
||||
interfaces.plugins.FileHandlerInterface.__init__(self, filename, immediate_commit)
|
||||
|
||||
def close(self):
|
||||
if self.closed:
|
||||
return
|
||||
|
||||
if output_dir is None:
|
||||
raise TypeError("Output directory is not a string")
|
||||
os.makedirs(output_dir, exist_ok = True)
|
||||
|
||||
pref_name_array = self.preferred_filename.split('.')
|
||||
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):
|
||||
with open(output_filename, "wb") as current_file:
|
||||
current_file.write(filedata.data.getvalue())
|
||||
current_file.write(self.read())
|
||||
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
|
||||
|
||||
def populate_requirements_argparse(self, parser: Union[argparse.ArgumentParser, argparse._ArgumentGroup],
|
||||
configurable: Type[interfaces.configuration.ConfigurableInterface]):
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
import binascii
|
||||
import code
|
||||
import io
|
||||
import random
|
||||
import string
|
||||
import struct
|
||||
@@ -263,10 +264,6 @@ class Volshell(interfaces.plugins.PluginInterface):
|
||||
else:
|
||||
return hex(value.vol.offset)
|
||||
|
||||
def consume_file(self, file: interfaces.plugins.FileInterface) -> None:
|
||||
"""Dummy file consumer to satisfy the interface"""
|
||||
pass
|
||||
|
||||
def generate_treegrid(self, plugin: Type[interfaces.plugins.PluginInterface],
|
||||
**kwargs) -> Optional[interfaces.renderers.TreeGrid]:
|
||||
"""Generates a TreeGrid based on a specific plugin passing in kwarg configuration values"""
|
||||
@@ -281,7 +278,7 @@ class Volshell(interfaces.plugins.PluginInterface):
|
||||
self.config[path_join(plugin_config_suffix, plugin.__name__, name)] = value
|
||||
|
||||
try:
|
||||
constructed = plugins.construct_plugin(self.context, [], plugin, plugin_path, None, NullFileConsumer())
|
||||
constructed = plugins.construct_plugin(self.context, [], plugin, plugin_path, None, NullFileHandler())
|
||||
return constructed.run()
|
||||
except exceptions.UnsatisfiedException as excp:
|
||||
print("Unable to validate the plugin requirements: {}\n".format([x for x in excp.unsatisfied]))
|
||||
@@ -320,9 +317,17 @@ class Volshell(interfaces.plugins.PluginInterface):
|
||||
print(" " * (longest_offset - len_offset), hex(symbol.address), " ", symbol.name)
|
||||
|
||||
|
||||
class NullFileConsumer(interfaces.plugins.FileConsumerInterface):
|
||||
"""Null FileConsumer that swallows files whole"""
|
||||
class NullFileHandler(io.BytesIO, interfaces.plugins.FileHandlerInterface):
|
||||
"""Null FileHandler that swallows files whole without consuming memory"""
|
||||
|
||||
def consume_file(self, file: interfaces.plugins.FileInterface) -> None:
|
||||
"""Dummy file consumer to satisfy the FileConsumerInterface"""
|
||||
def __init__(self, preferred_name: str, immediate_commit: bool = False):
|
||||
interfaces.plugins.FileHandlerInterface.__init__(self, preferred_name, immediate_commit)
|
||||
super().__init__()
|
||||
|
||||
def writelines(self, lines):
|
||||
"""Dummy method"""
|
||||
pass
|
||||
|
||||
def write(self, data):
|
||||
"""Dummy method"""
|
||||
return len(data)
|
||||
|
||||
@@ -44,7 +44,7 @@ def available(context: interfaces.context.ContextInterface) -> List[interfaces.a
|
||||
clazz(context, interfaces.configuration.path_join(config_path, clazz.__name__))
|
||||
for clazz in class_subclasses(interfaces.automagic.AutomagicInterface)
|
||||
],
|
||||
key = lambda x: x.priority)
|
||||
key = lambda x: x.priority)
|
||||
|
||||
|
||||
def choose_automagic(
|
||||
|
||||
@@ -8,10 +8,10 @@ using objects constructed from symbols.
|
||||
"""
|
||||
|
||||
# Configuration interfaces must be imported separately, since we're part of interfaces and can't import ourselves
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from typing import List, Optional, Tuple
|
||||
from typing import List, Tuple, Type, IO
|
||||
|
||||
from volatility import framework
|
||||
from volatility.framework import exceptions, constants, interfaces
|
||||
@@ -19,38 +19,61 @@ from volatility.framework import exceptions, constants, interfaces
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FileInterface(metaclass = ABCMeta):
|
||||
class FileHandlerInterface(IO[bytes]):
|
||||
"""Class for storing Files in the plugin as a means to output a file or
|
||||
files when necessary."""
|
||||
|
||||
def __init__(self, filename: str, data: bytes = None) -> None:
|
||||
"""
|
||||
def __init__(self, filename: str, immediate_commit: bool = False) -> None:
|
||||
"""Creates a FileTemplate
|
||||
|
||||
Args:
|
||||
filename: The requested name of the filename for the data
|
||||
data: The data to be stored in a file
|
||||
"""
|
||||
self._immediate_commit = immediate_commit
|
||||
self._committed = False
|
||||
self._preferred_filename = None
|
||||
self.preferred_filename = filename
|
||||
if data is None:
|
||||
data = b''
|
||||
self.data = io.BytesIO(data)
|
||||
super().__init__()
|
||||
|
||||
@property
|
||||
def committed(self):
|
||||
return self._committed
|
||||
|
||||
class FileConsumerInterface(object):
|
||||
"""Class for consuming files potentially produced by plugins.
|
||||
@property
|
||||
def preferred_filename(self):
|
||||
return self._preferred_filename
|
||||
|
||||
We use the producer/consumer model to ensure we can avoid running
|
||||
out of memory by storing every file produced. The downside is, we
|
||||
can't provide much feedback to the producer about what happened to
|
||||
their file (other than exceptions).
|
||||
"""
|
||||
@preferred_filename.setter
|
||||
def preferred_filename(self, filename):
|
||||
"""Sets the preferred filename"""
|
||||
if self._committed:
|
||||
raise IOError
|
||||
if not isinstance(filename, str):
|
||||
raise TypeError("FileTemplateInterface preferred filenames must be strings")
|
||||
if os.path.sep in filename:
|
||||
raise ValueError("FileTemplateInterface filenames cannot contain path separators")
|
||||
self._preferred_filename = filename
|
||||
|
||||
def consume_file(self, file: FileInterface) -> None:
|
||||
"""Consumes a file as passed back to a UI by a plugin.
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
Args:
|
||||
file: A FileInterface object with the data to write to a file
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
if exc_type is None and exc_value is None and traceback is None:
|
||||
self.close()
|
||||
if self._immediate_commit:
|
||||
self.commit()
|
||||
else:
|
||||
vollog.warning("File {} could not be written: {}".format(self._preferred_filename, str(exc_value)))
|
||||
self.close()
|
||||
|
||||
def commit(self):
|
||||
"""Commits the file to whatever medium is necessary, the file cannot be altered after this point
|
||||
nor can its preferred_name be chaned
|
||||
|
||||
This also ensures that a UI can determine when a file is fully complete rather than partially written
|
||||
"""
|
||||
self.close()
|
||||
self._committed = True
|
||||
|
||||
|
||||
#
|
||||
@@ -105,21 +128,21 @@ class PluginInterface(interfaces.configuration.ConfigurableInterface,
|
||||
if requirement.name not in self.config:
|
||||
self.config[requirement.name] = requirement.default
|
||||
|
||||
self._file_consumer = None # type: Optional[FileConsumerInterface]
|
||||
self._file_handler = FileHandlerInterface # type: Type[FileHandlerInterface]
|
||||
|
||||
framework.require_interface_version(*self._required_framework_version)
|
||||
|
||||
def set_file_consumer(self, consumer: FileConsumerInterface) -> None:
|
||||
"""Sets the file consumer to be used by this plugin."""
|
||||
self._file_consumer = consumer
|
||||
def open(self, preferred_filename: str) -> FileHandlerInterface:
|
||||
"""Opens a file for output"""
|
||||
if self._file_handler is not None:
|
||||
return self._file_handler(preferred_filename)
|
||||
raise IOError("FileTemplate not specified for this plugin")
|
||||
|
||||
def produce_file(self, filedata: FileInterface) -> None:
|
||||
"""Adds a file to the plugin's file store and returns the chosen
|
||||
filename for the file."""
|
||||
if self._file_consumer:
|
||||
self._file_consumer.consume_file(filedata)
|
||||
else:
|
||||
vollog.debug("No file consumer specified to consume: {}".format(filedata.preferred_filename))
|
||||
def set_file_handler(self, handler: Type[FileHandlerInterface]) -> None:
|
||||
"""Sets the file handler to be used by this plugin."""
|
||||
if not issubclass(handler, FileHandlerInterface):
|
||||
raise ValueError("FileHandler must be a subclass of FileHandlerInterface")
|
||||
self._file_handler = handler
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
|
||||
@@ -19,7 +19,7 @@ def construct_plugin(context: interfaces.context.ContextInterface,
|
||||
automagics: List[interfaces.automagic.AutomagicInterface],
|
||||
plugin: Type[interfaces.plugins.PluginInterface], base_config_path: str,
|
||||
progress_callback: constants.ProgressCallback,
|
||||
file_consumer: interfaces.plugins.FileConsumerInterface) -> interfaces.plugins.PluginInterface:
|
||||
file_handler: Type[interfaces.plugins.FileHandlerInterface]) -> interfaces.plugins.PluginInterface:
|
||||
"""Constructs a plugin object based on the parameters.
|
||||
|
||||
Clever magic figures out how to fulfill each requirement that might not be fulfilled
|
||||
@@ -30,7 +30,7 @@ def construct_plugin(context: interfaces.context.ContextInterface,
|
||||
plugin: The plugin to run
|
||||
base_config_path: The path within the context's config containing the plugin's configuration
|
||||
progress_callback: Callback function to provide feedback for ongoing processes
|
||||
file_consumer: Object to pass any generated files to
|
||||
file_handler: Object to pass any generated files to
|
||||
|
||||
Returns:
|
||||
The constructed plugin object
|
||||
@@ -49,6 +49,6 @@ def construct_plugin(context: interfaces.context.ContextInterface,
|
||||
raise exceptions.UnsatisfiedException(unsatisfied)
|
||||
|
||||
constructed = plugin(context, plugin_config_path, progress_callback = progress_callback)
|
||||
if file_consumer:
|
||||
constructed.set_file_consumer(file_consumer)
|
||||
if file_handler:
|
||||
constructed.set_file_handler(file_handler)
|
||||
return constructed
|
||||
|
||||
@@ -39,9 +39,8 @@ class ConfigWriter(plugins.PluginInterface):
|
||||
config = dict(self.context.config)
|
||||
filename = "config.extra"
|
||||
try:
|
||||
filedata = plugins.FileInterface(filename)
|
||||
filedata.data.write(bytes(json.dumps(config, sort_keys = True, indent = 2), 'raw_unicode_escape'))
|
||||
self.produce_file(filedata)
|
||||
with self._file_handler(filename, True) 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")
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Type
|
||||
|
||||
from volatility.framework import renderers, interfaces, constants, exceptions
|
||||
from volatility.framework.configuration import requirements
|
||||
@@ -43,8 +43,10 @@ class LayerWriter(plugins.PluginInterface):
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
preferred_name: str,
|
||||
file_handler: Type[plugins.FileHandlerInterface],
|
||||
chunk_size: Optional[int] = None,
|
||||
progress_callback: Optional[constants.ProgressCallback] = None) -> Optional[plugins.FileInterface]:
|
||||
progress_callback: Optional[constants.ProgressCallback] = None) -> Optional[
|
||||
plugins.FileHandlerInterface]:
|
||||
"""Produces a filedata from the named layer in the provided context
|
||||
|
||||
Args:
|
||||
@@ -62,11 +64,11 @@ class LayerWriter(plugins.PluginInterface):
|
||||
if chunk_size is None:
|
||||
chunk_size = cls.default_block_size
|
||||
|
||||
filedata = plugins.FileInterface(preferred_name)
|
||||
filedata = file_handler(preferred_name)
|
||||
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.data.write(data)
|
||||
filedata.write(data)
|
||||
if progress_callback:
|
||||
progress_callback((i / layer.maximum_address) * 100, 'Writing layer {}'.format(layer_name))
|
||||
return filedata
|
||||
@@ -80,9 +82,10 @@ class LayerWriter(plugins.PluginInterface):
|
||||
output_name = self.config.get('output', self.default_output_name)
|
||||
try:
|
||||
filedata = self.write_layer(self.context, self.config['primary'], output_name,
|
||||
self._file_handler,
|
||||
self.config.get('block_size', self.default_block_size),
|
||||
self._progress_callback)
|
||||
self.produce_file(filedata)
|
||||
progress_callback = self._progress_callback)
|
||||
filedata.commit()
|
||||
except IOError as excp:
|
||||
yield 0, ('Layer cannot be written to {}: {}'.format(self.config['output_name'], excp),)
|
||||
|
||||
|
||||
@@ -138,8 +138,8 @@ class Timeliner(interfaces.plugins.PluginInterface):
|
||||
|
||||
# Write out a body file if necessary
|
||||
if self.config.get('create-bodyfile', True):
|
||||
filedata = interfaces.plugins.FileInterface("volatility.body")
|
||||
with io.TextIOWrapper(filedata.data, write_through = True) as fp:
|
||||
filedata = self._file_handler("volatility.body", True)
|
||||
with io.TextIOWrapper(filedata, 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
|
||||
@@ -151,7 +151,6 @@ class Timeliner(interfaces.plugins.PluginInterface):
|
||||
self._text_format(times.get(TimeLinerType.MODIFIED, "")),
|
||||
self._text_format(times.get(TimeLinerType.CHANGED, "")),
|
||||
self._text_format(times.get(TimeLinerType.CREATED, ""))))
|
||||
self.produce_file(filedata)
|
||||
|
||||
def _sanitize_body_format(self, value):
|
||||
return value.replace("|", "_")
|
||||
@@ -185,7 +184,7 @@ class Timeliner(interfaces.plugins.PluginInterface):
|
||||
automagics = automagic.choose_automagic(self.automagics, plugin_class)
|
||||
|
||||
plugin = plugins.construct_plugin(self.context, automagics, plugin_class, self.config_path,
|
||||
self._progress_callback, self._file_template)
|
||||
self._progress_callback, self._file_handler)
|
||||
|
||||
if isinstance(plugin, TimeLinerInterface):
|
||||
if not len(filter_list) or any(
|
||||
@@ -203,10 +202,9 @@ class Timeliner(interfaces.plugins.PluginInterface):
|
||||
for entry in old_dict:
|
||||
total_config[interfaces.configuration.path_join(plugin.__class__.__name__, entry)] = old_dict[entry]
|
||||
|
||||
filedata = interfaces.plugins.FileInterface("config.json")
|
||||
with io.TextIOWrapper(filedata.data, write_through = True) as fp:
|
||||
filedata = self._file_handler("config.json", True)
|
||||
with io.TextIOWrapper(filedata, write_through = True) as fp:
|
||||
json.dump(total_config, fp, sort_keys = True, indent = 2)
|
||||
self.produce_file(filedata)
|
||||
|
||||
return renderers.TreeGrid(columns = [("Plugin", str), ("Description", str), ("Created Date", datetime.datetime),
|
||||
("Modified Date", datetime.datetime), ("Accessed Date", datetime.datetime),
|
||||
|
||||
@@ -26,7 +26,7 @@ class CmdLine(interfaces.plugins.PluginInterface):
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)),
|
||||
requirements.ListRequirement(name = 'pid',
|
||||
element_type = int,
|
||||
description = "Process IDs to include (all other processes are excluded)",
|
||||
|
||||
@@ -21,7 +21,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Lists the loaded modules in a particular windows memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 0, 0)
|
||||
_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -31,7 +31,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"),
|
||||
requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (1, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (2, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'info', component = info.Info, version = (1, 0, 0)),
|
||||
requirements.ListRequirement(name = 'pid',
|
||||
element_type = int,
|
||||
@@ -48,7 +48,8 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
context: interfaces.context.ContextInterface,
|
||||
pe_table_name: str,
|
||||
dll_entry: interfaces.objects.ObjectInterface,
|
||||
layer_name: str = None) -> interfaces.plugins.FileInterface:
|
||||
file_handler: Type[interfaces.plugins.FileHandlerInterface],
|
||||
layer_name: str = None) -> interfaces.plugins.FileHandlerInterface:
|
||||
"""Extracts the complete data for a process as a FileInterface
|
||||
|
||||
Args:
|
||||
@@ -56,6 +57,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
pe_table_name: the name for the symbol table containing the PE format symbols
|
||||
dll_entry: the object representing the module
|
||||
layer_name: the layer that the DLL lives within
|
||||
file_handler: class for constructing output files
|
||||
|
||||
Returns:
|
||||
A FileInterface object containing the complete data for the DLL or None in the case of failure"""
|
||||
@@ -69,17 +71,21 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
if layer_name is None:
|
||||
layer_name = dll_entry.vol.layer_name
|
||||
|
||||
filedata = interfaces.plugins.FileInterface("{0}.{1:#x}.{2:#x}.dmp".format(
|
||||
ntpath.basename(name), dll_entry.vol.offset, dll_entry.DllBase))
|
||||
filedata = file_handler(
|
||||
"{0}.{1}.{2:#x}.{3:#x}.dmp".format(layer_name, ntpath.basename(name), dll_entry.vol.offset,
|
||||
dll_entry.DllBase), True)
|
||||
|
||||
dos_header = context.object(pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
|
||||
offset = dll_entry.DllBase,
|
||||
layer_name = layer_name)
|
||||
|
||||
for offset, data in dos_header.reconstruct():
|
||||
filedata.data.seek(offset)
|
||||
filedata.data.write(data)
|
||||
except Exception as excp:
|
||||
with file_handler("{0}.{1}.{2:#x}.{3:#x}.dmp".format(layer_name, ntpath.basename(name),
|
||||
dll_entry.vol.offset,
|
||||
dll_entry.DllBase)):
|
||||
for offset, data in dos_header.reconstruct():
|
||||
filedata.seek(offset)
|
||||
filedata.write(data)
|
||||
except (IOError, exceptions.VolatilityException) as excp:
|
||||
vollog.debug("Unable to dump dll at offset {}: {}".format(dll_entry.DllBase, excp))
|
||||
return filedata
|
||||
|
||||
@@ -120,13 +126,14 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
else:
|
||||
DllLoadTime = renderers.NotApplicableValue()
|
||||
|
||||
dumped = False
|
||||
file_output = "Disabled"
|
||||
if self.config['dump']:
|
||||
filedata = self.dump_pe(self.context, pe_table_name, entry, proc_layer_name)
|
||||
if filedata:
|
||||
filedata.preferred_filename = "pid.{0}.".format(proc_id) + filedata.preferred_filename
|
||||
dumped = True
|
||||
self.produce_file(filedata)
|
||||
filedata = self.dump_pe(self.context, pe_table_name, entry, self._file_handler,
|
||||
proc_layer_name)
|
||||
if filedata and filedata.committed:
|
||||
file_output = filedata.preferred_filename
|
||||
else:
|
||||
file_output = "Error outputting file"
|
||||
|
||||
yield (0, (proc.UniqueProcessId,
|
||||
proc.ImageFileName.cast("string",
|
||||
|
||||
@@ -46,7 +46,7 @@ class Handles(interfaces.plugins.PluginInterface):
|
||||
element_type = int,
|
||||
description = "Process IDs to include (all other processes are excluded)",
|
||||
optional = True),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0))
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0))
|
||||
]
|
||||
|
||||
def _decode_pointer(self, value, magic):
|
||||
|
||||
@@ -35,7 +35,7 @@ class Malfind(interfaces.plugins.PluginInterface):
|
||||
description = "Extract injected VADs",
|
||||
default = False,
|
||||
optional = True),
|
||||
requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (1, 1, 0)),
|
||||
requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (2, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'vadinfo', component = vadinfo.VadInfo, version = (1, 1, 0))
|
||||
]
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class Memmap(interfaces.plugins.PluginInterface):
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)),
|
||||
requirements.IntRequirement(name = 'pid',
|
||||
description = "Process ID to include (all other processes are excluded)",
|
||||
optional = True),
|
||||
@@ -48,34 +48,38 @@ class Memmap(interfaces.plugins.PluginInterface):
|
||||
excp.layer_name))
|
||||
continue
|
||||
|
||||
filedata = interfaces.plugins.FileInterface("pid.{}.dmp".format(pid))
|
||||
filedata = self._file_handler("pid.{}.dmp".format(pid))
|
||||
|
||||
for mapval in proc_layer.mapping(0x0, proc_layer.maximum_address, ignore_errors = True):
|
||||
offset, size, mapped_offset, mapped_size, maplayer = mapval
|
||||
|
||||
dumped = False
|
||||
file_output = "Disabled"
|
||||
if self.config['dump']:
|
||||
try:
|
||||
data = proc_layer.read(offset, size, pad = True)
|
||||
filedata.data.write(data)
|
||||
dumped = True
|
||||
filedata.write(data)
|
||||
file_output = filedata.preferred_filename
|
||||
except exceptions.InvalidAddressException:
|
||||
file_output = "Error outputting file"
|
||||
vollog.debug("Unable to write {}'s address {} to {}.dmp".format(
|
||||
proc_layer_name, offset, filedata.preferred_filename))
|
||||
|
||||
yield (0, (format_hints.Hex(offset), format_hints.Hex(mapped_offset), format_hints.Hex(mapped_size),
|
||||
format_hints.Hex(offset), dumped))
|
||||
yield (0, (
|
||||
format_hints.Hex(offset),
|
||||
format_hints.Hex(mapped_offset),
|
||||
format_hints.Hex(mapped_size),
|
||||
format_hints.Hex(offset),
|
||||
file_output))
|
||||
offset += mapped_size
|
||||
|
||||
self.produce_file(filedata)
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter([self.config.get('pid', None)])
|
||||
|
||||
return renderers.TreeGrid([("Virtual", format_hints.Hex), ("Physical", format_hints.Hex),
|
||||
("Size", format_hints.Hex), ("Offset", format_hints.Hex), ("Dumped", bool)],
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(context = self.context,
|
||||
layer_name = self.config['primary'],
|
||||
symbol_table = self.config['nt_symbols'],
|
||||
filter_func = filter_func)))
|
||||
return renderers.TreeGrid(
|
||||
[("Virtual", format_hints.Hex), ("Physical", format_hints.Hex), ("Size", format_hints.Hex),
|
||||
("Offset", format_hints.Hex), ("File output", str)],
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(context = self.context,
|
||||
layer_name = self.config['primary'],
|
||||
symbol_table = self.config['nt_symbols'],
|
||||
filter_func = filter_func)))
|
||||
|
||||
@@ -8,7 +8,7 @@ from volatility.framework import renderers, interfaces, exceptions
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.renderers import format_hints
|
||||
from volatility.framework.symbols import intermed
|
||||
from volatility.framework.symbols.windows import extensions
|
||||
from volatility.framework.symbols.windows.extensions import pe
|
||||
from volatility.plugins.windows import poolscanner, dlllist
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class ModScan(interfaces.plugins.PluginInterface):
|
||||
requirements.VersionRequirement(name = 'poolerscanner',
|
||||
component = poolscanner.PoolScanner,
|
||||
version = (1, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'dlllist', component = dlllist.DllList, version = (1, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'dlllist', component = dlllist.DllList, version = (2, 0, 0)),
|
||||
requirements.BooleanRequirement(name = 'dump',
|
||||
description = "Extract listed modules",
|
||||
default = False,
|
||||
@@ -63,7 +63,7 @@ class ModScan(interfaces.plugins.PluginInterface):
|
||||
self.config_path,
|
||||
"windows",
|
||||
"pe",
|
||||
class_types = extensions.pe.class_types)
|
||||
class_types = pe.class_types)
|
||||
|
||||
for mod in self.scan_modules(self.context, self.config['primary'], self.config['nt_symbols']):
|
||||
|
||||
@@ -77,16 +77,23 @@ class ModScan(interfaces.plugins.PluginInterface):
|
||||
except exceptions.InvalidAddressException:
|
||||
FullDllName = ""
|
||||
|
||||
dumped = False
|
||||
file_output = "Disabled"
|
||||
if self.config['dump']:
|
||||
filedata = dlllist.DllList.dump_pe(self.context, pe_table_name, mod)
|
||||
if filedata:
|
||||
self.produce_file(filedata)
|
||||
dumped = True
|
||||
filedata = dlllist.DllList.dump_pe(self.context, pe_table_name, mod, self._file_handler)
|
||||
if filedata and filedata.committed:
|
||||
file_output = filedata.preferred_filename
|
||||
else:
|
||||
file_output = "Error outputting file"
|
||||
|
||||
yield (0, (format_hints.Hex(mod.vol.offset), format_hints.Hex(mod.DllBase),
|
||||
format_hints.Hex(mod.SizeOfImage), BaseDllName, FullDllName, dumped))
|
||||
yield (0, (
|
||||
format_hints.Hex(mod.vol.offset),
|
||||
format_hints.Hex(mod.DllBase),
|
||||
format_hints.Hex(mod.SizeOfImage),
|
||||
BaseDllName,
|
||||
FullDllName,
|
||||
file_output
|
||||
))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Offset", format_hints.Hex), ("Base", format_hints.Hex), ("Size", format_hints.Hex),
|
||||
("Name", str), ("Path", str), ("Dumped", bool)], self._generator())
|
||||
("Name", str), ("Path", str), ("File output", str)], self._generator())
|
||||
|
||||
@@ -10,7 +10,7 @@ from volatility.framework import renderers
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.renderers import format_hints
|
||||
from volatility.framework.symbols import intermed
|
||||
from volatility.framework.symbols.windows import extensions
|
||||
from volatility.framework.symbols.windows.extensions import pe
|
||||
from volatility.plugins.windows import pslist, dlllist
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
@@ -29,8 +29,8 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"),
|
||||
requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (1, 1, 0)),
|
||||
requirements.VersionRequirement(name = 'dlllist', component = dlllist.DllList, version = (1, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (2, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'dlllist', component = dlllist.DllList, version = (2, 0, 0)),
|
||||
requirements.BooleanRequirement(name = 'dump',
|
||||
description = "Extract listed modules",
|
||||
default = False,
|
||||
@@ -42,7 +42,7 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
self.config_path,
|
||||
"windows",
|
||||
"pe",
|
||||
class_types = extensions.pe.class_types)
|
||||
class_types = pe.class_types)
|
||||
|
||||
for mod in self.list_modules(self.context, self.config['primary'], self.config['nt_symbols']):
|
||||
|
||||
@@ -56,15 +56,22 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
except exceptions.InvalidAddressException:
|
||||
FullDllName = ""
|
||||
|
||||
dumped = False
|
||||
file_output = "Disabled"
|
||||
if self.config['dump']:
|
||||
filedata = dlllist.DllList.dump_pe(self.context, pe_table_name, mod)
|
||||
if filedata:
|
||||
self.produce_file(filedata)
|
||||
dumped = True
|
||||
filedata = dlllist.DllList.dump_pe(self.context, pe_table_name, mod, self._file_handler)
|
||||
if filedata and filedata.committed:
|
||||
file_output = filedata.preferred_filename
|
||||
else:
|
||||
file_output = "Error outputting file"
|
||||
|
||||
yield (0, (format_hints.Hex(mod.vol.offset), format_hints.Hex(mod.DllBase),
|
||||
format_hints.Hex(mod.SizeOfImage), BaseDllName, FullDllName, dumped))
|
||||
yield (0, (
|
||||
format_hints.Hex(mod.vol.offset),
|
||||
format_hints.Hex(mod.DllBase),
|
||||
format_hints.Hex(mod.SizeOfImage),
|
||||
BaseDllName,
|
||||
FullDllName,
|
||||
file_output
|
||||
))
|
||||
|
||||
@classmethod
|
||||
def get_session_layers(cls,
|
||||
@@ -175,4 +182,4 @@ class Modules(interfaces.plugins.PluginInterface):
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Offset", format_hints.Hex), ("Base", format_hints.Hex), ("Size", format_hints.Hex),
|
||||
("Name", str), ("Path", str), ("Dumped", bool)], self._generator())
|
||||
("Name", str), ("Path", str), ("File output", str)], self._generator())
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
from typing import Callable, Iterable, List
|
||||
from typing import Callable, Iterable, List, Type
|
||||
|
||||
from volatility.framework import renderers, interfaces, layers, constants
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.objects import utility
|
||||
from volatility.framework.renderers import format_hints
|
||||
from volatility.framework.symbols import intermed
|
||||
from volatility.framework.symbols.windows import extensions
|
||||
from volatility.framework.symbols.windows.extensions import pe
|
||||
from volatility.plugins import timeliner
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
@@ -21,7 +21,7 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Lists the processes present in a particular windows memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (1, 1, 0)
|
||||
_version = (2, 0, 0)
|
||||
PHYSICAL_DEFAULT = False
|
||||
|
||||
@classmethod
|
||||
@@ -46,18 +46,23 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def process_dump(cls, context: interfaces.context.ContextInterface, kernel_table_name: str, pe_table_name: str,
|
||||
proc: interfaces.objects.ObjectInterface) -> interfaces.plugins.FileInterface:
|
||||
"""Extracts the complete data for a process as a FileInterface
|
||||
def process_dump(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
kernel_table_name: str, pe_table_name: str,
|
||||
proc: interfaces.objects.ObjectInterface,
|
||||
file_handler: Type[
|
||||
interfaces.plugins.FileHandlerInterface]) -> interfaces.plugins.FileHandlerInterface:
|
||||
"""Extracts the complete data for a process as a FileHandlerInterface
|
||||
|
||||
Args:
|
||||
context: the context to operate upon
|
||||
kernel_table_name: the name for the symbol table containing the kernel's symbols
|
||||
pe_table_name: the name for the symbol table containing the PE format symbols
|
||||
proc: the process object whose memory should be output
|
||||
file_handler: class to write construct for writing the file
|
||||
|
||||
Returns:
|
||||
A FileInterface object containing the complete data for the process or None in the case of failure
|
||||
A FileHandlerInterface object containing the complete data for the process or None in the case of failure
|
||||
"""
|
||||
|
||||
filedata = None
|
||||
@@ -70,11 +75,10 @@ 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)
|
||||
filedata = interfaces.plugins.FileInterface("pid.{0}.{1:#x}.dmp".format(proc.UniqueProcessId,
|
||||
peb.ImageBaseAddress))
|
||||
filedata = file_handler("pid.{0}.{1:#x}.dmp".format(proc.UniqueProcessId, peb.ImageBaseAddress))
|
||||
for offset, data in dos_header.reconstruct():
|
||||
filedata.data.seek(offset)
|
||||
filedata.data.write(data)
|
||||
filedata.seek(offset)
|
||||
filedata.write(data)
|
||||
except Exception as excp:
|
||||
vollog.debug("Unable to dump PE with pid {}: {}".format(proc.UniqueProcessId, excp))
|
||||
|
||||
@@ -168,7 +172,7 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
self.config_path,
|
||||
"windows",
|
||||
"pe",
|
||||
class_types = extensions.pe.class_types)
|
||||
class_types = pe.class_types)
|
||||
|
||||
memory = self.context.layers[self.config['primary']]
|
||||
if not isinstance(memory, layers.intel.Intel):
|
||||
@@ -184,17 +188,19 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
else:
|
||||
(_, _, offset, _, _) = list(memory.mapping(offset = proc.vol.offset, length = 0))[0]
|
||||
|
||||
dumped = False
|
||||
file_output = "Disabled"
|
||||
if self.config['dump']:
|
||||
filedata = self.process_dump(self.context, self.config['nt_symbols'], pe_table_name, proc)
|
||||
if filedata:
|
||||
dumped = True
|
||||
self.produce_file(filedata)
|
||||
filedata = self.process_dump(self.context, self.config['nt_symbols'], pe_table_name, proc,
|
||||
self._file_handler)
|
||||
if filedata and filedata.committed:
|
||||
file_output = filedata.preferred_filename
|
||||
else:
|
||||
file_output = "Error outputting file"
|
||||
|
||||
yield (0, (proc.UniqueProcessId, proc.InheritedFromUniqueProcessId,
|
||||
proc.ImageFileName.cast("string", max_length = proc.ImageFileName.vol.count, errors = 'replace'),
|
||||
format_hints.Hex(offset), proc.ActiveThreads, proc.get_handle_count(), proc.get_session_id(),
|
||||
proc.get_is_wow64(), proc.get_create_time(), proc.get_exit_time(), dumped))
|
||||
proc.get_is_wow64(), proc.get_create_time(), proc.get_exit_time(), file_output))
|
||||
|
||||
def generate_timeline(self):
|
||||
for row in self._generator():
|
||||
@@ -210,4 +216,4 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
("Offset{0}".format(offsettype), format_hints.Hex), ("Threads", int),
|
||||
("Handles", int), ("SessionId", int), ("Wow64", bool),
|
||||
("CreateTime", datetime.datetime), ("ExitTime", datetime.datetime),
|
||||
("Dumped", bool)], self._generator())
|
||||
("File output", str)], self._generator())
|
||||
|
||||
@@ -6,7 +6,6 @@ from typing import Iterator, List, Tuple, Iterable, Optional
|
||||
|
||||
from volatility.framework import renderers, interfaces, exceptions
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.interfaces import plugins
|
||||
from volatility.framework.layers import registry
|
||||
from volatility.framework.renderers import format_hints
|
||||
from volatility.plugins.windows.registry import hivescan
|
||||
@@ -71,7 +70,7 @@ class HiveList(interfaces.plugins.PluginInterface):
|
||||
symbol_table = self.config["nt_symbols"],
|
||||
filter_string = self.config.get('filter', None)):
|
||||
|
||||
dumped = False
|
||||
file_output = "Disabled"
|
||||
if self.config['dump']:
|
||||
# Construct the hive
|
||||
hive = next(
|
||||
@@ -83,23 +82,22 @@ class HiveList(interfaces.plugins.PluginInterface):
|
||||
maxaddr = hive.hive.Storage[0].Length
|
||||
hive_name = self._sanitize_hive_name(hive.get_name())
|
||||
|
||||
filedata = plugins.FileInterface('registry.{}.{}.hive'.format(hive_name, hex(hive.hive_offset)))
|
||||
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.data.write(hive_data)
|
||||
with self._file_handler('registry.{}.{}.hive'.format(hive_name, hex(hive.hive_offset))) as filedata:
|
||||
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)
|
||||
|
||||
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.data.write(data)
|
||||
# if self._progress_callback:
|
||||
# self._progress_callback((i / maxaddr) * 100, 'Writing layer {}'.format(hive_name))
|
||||
self.produce_file(filedata)
|
||||
dumped = True
|
||||
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)
|
||||
# if self._progress_callback:
|
||||
# self._progress_callback((i / maxaddr) * 100, 'Writing layer {}'.format(hive_name))
|
||||
file_output = filedata.preferred_filename
|
||||
|
||||
yield (0, (format_hints.Hex(hive_object.vol.offset), hive_object.get_name() or "", dumped))
|
||||
yield (0, (format_hints.Hex(hive_object.vol.offset), hive_object.get_name() or "", file_output))
|
||||
|
||||
@classmethod
|
||||
def list_hives(cls,
|
||||
@@ -238,5 +236,5 @@ class HiveList(interfaces.plugins.PluginInterface):
|
||||
hex(hive.vol.offset)))
|
||||
|
||||
def run(self) -> renderers.TreeGrid:
|
||||
return renderers.TreeGrid([("Offset", format_hints.Hex), ("FileFullPath", str), ("Dumped", bool)],
|
||||
return renderers.TreeGrid([("Offset", format_hints.Hex), ("FileFullPath", str), ("File output", str)],
|
||||
self._generator())
|
||||
|
||||
@@ -25,7 +25,7 @@ class Strings(interfaces.plugins.PluginInterface):
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)),
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
|
||||
@@ -31,7 +31,7 @@ class SvcScan(interfaces.plugins.PluginInterface):
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "nt_symbols", description = "Windows kernel symbols"),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'poolscanner', plugin = poolscanner.PoolScanner, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'vadyarascan', plugin = vadyarascan.VadYaraScan, version = (1, 0, 0))
|
||||
]
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import logging
|
||||
from typing import Callable, List, Generator, Iterable, Optional
|
||||
|
||||
from volatility.framework import renderers, interfaces, exceptions
|
||||
from volatility.framework import renderers, interfaces
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.objects import utility
|
||||
from volatility.framework.renderers import format_hints
|
||||
@@ -58,7 +58,7 @@ class VadInfo(interfaces.plugins.PluginInterface):
|
||||
description = 'Filter on specific process IDs',
|
||||
element_type = int,
|
||||
optional = True),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)),
|
||||
requirements.BooleanRequirement(name = 'dump',
|
||||
description = "Extract listed memory ranges",
|
||||
default = False,
|
||||
@@ -180,6 +180,7 @@ class VadInfo(interfaces.plugins.PluginInterface):
|
||||
|
||||
for proc in procs:
|
||||
process_name = utility.array_to_string(proc.ImageFileName)
|
||||
proc_layer_name = proc.add_process_layer()
|
||||
|
||||
for vad in self.list_vads(proc, filter_func = filter_func):
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class VadYaraScan(interfaces.plugins.PluginInterface):
|
||||
default = 0x40000000,
|
||||
description = "Set the maximum size (default is 1GB)",
|
||||
optional = True),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'yarascanner', component = yarascan.YaraScanner,
|
||||
version = (2, 0, 0)),
|
||||
requirements.ListRequirement(name = 'pid',
|
||||
|
||||
@@ -10,7 +10,7 @@ from volatility.framework import exceptions, renderers, constants, interfaces
|
||||
from volatility.framework.configuration import requirements
|
||||
from volatility.framework.renderers import format_hints
|
||||
from volatility.framework.symbols import intermed
|
||||
from volatility.framework.symbols.windows import extensions
|
||||
from volatility.framework.symbols.windows.extensions import pe
|
||||
from volatility.plugins.windows import pslist, modules, dlllist
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
@@ -32,9 +32,9 @@ class VerInfo(interfaces.plugins.PluginInterface):
|
||||
## TODO: we might add a regex option on the name later, but otherwise we're good
|
||||
## TODO: and we don't want any CLI options from pslist, modules, or moddump
|
||||
return [
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'modules', plugin = modules.Modules, version = (1, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'dlllist', component = dlllist.DllList, version = (1, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'dlllist', component = dlllist.DllList, version = (2, 0, 0)),
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
@@ -101,7 +101,7 @@ class VerInfo(interfaces.plugins.PluginInterface):
|
||||
self.config_path,
|
||||
"windows",
|
||||
"pe",
|
||||
class_types = extensions.pe.class_types)
|
||||
class_types = pe.class_types)
|
||||
|
||||
for mod in mods:
|
||||
try:
|
||||
|
||||
@@ -10,6 +10,8 @@ from volatility.plugins.windows.registry import hivelist, printkey
|
||||
class Certificates(interfaces.plugins.PluginInterface):
|
||||
"""Lists the certificates in the registry's Certificate Store."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
@@ -55,10 +57,9 @@ class Certificates(interfaces.plugins.PluginInterface):
|
||||
key_hash = key_path[key_path.rindex("\\") + 1:]
|
||||
|
||||
if not isinstance(certificate_data, interfaces.renderers.BaseAbsentValue):
|
||||
filedata = interfaces.plugins.FileInterface("{} - {} - {}.crt".format(
|
||||
hex(hive.hive_offset), reg_section, key_hash))
|
||||
filedata.data.write(certificate_data)
|
||||
self.produce_file(filedata)
|
||||
with self._file_handler("{} - {} - {}.crt".format(
|
||||
hex(hive.hive_offset), reg_section, key_hash), True) as filedata:
|
||||
filedata.write(certificate_data)
|
||||
yield (0, (top_key, reg_section, key_hash, name))
|
||||
except KeyError:
|
||||
# Key wasn't found in this hive, carry on
|
||||
|
||||
@@ -13,6 +13,7 @@ vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Statistics(plugins.PluginInterface):
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
|
||||
Reference in New Issue
Block a user