mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-25 07:32:23 +02:00
129 lines
5.9 KiB
Python
129 lines
5.9 KiB
Python
import abc
|
|
import datetime
|
|
import enum
|
|
import logging
|
|
import traceback
|
|
import typing
|
|
|
|
from volatility import framework
|
|
from volatility.framework import renderers, automagic
|
|
from volatility.framework.configuration import requirements
|
|
from volatility.framework.interfaces import plugins, configuration
|
|
|
|
vollog = logging.getLogger(__name__)
|
|
|
|
|
|
class TimeLinerType(enum.IntEnum):
|
|
CREATED = 1
|
|
MODIFIED = 2
|
|
ACCESSED = 3
|
|
CHANGED = 4
|
|
|
|
|
|
class TimeLinerInterface(object, metaclass = abc.ABCMeta):
|
|
"""Interface defining methods that timeliner will use to generate a body file"""
|
|
|
|
@abc.abstractmethod
|
|
def generate_timeline(self) -> typing.Generator[
|
|
typing.Tuple[str, TimeLinerType, datetime.datetime, TimeLinerType], None, None]:
|
|
"""Method generates Tuples of (description, timestamp_type, timestamp)
|
|
|
|
These need not be generated in any particular order, sorting will be done later
|
|
"""
|
|
|
|
|
|
class Timeliner(plugins.PluginInterface):
|
|
"""Runs all relevant plugins that provide time related information and orders the results by time"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.timeline = {}
|
|
|
|
@classmethod
|
|
def get_requirements(cls):
|
|
return [requirements.StringRequirement(name = 'plugins',
|
|
description = "Comma separated list of plugins to run",
|
|
optional = True,
|
|
default = None)]
|
|
|
|
def _generator(self) -> typing.Optional[typing.Iterable[typing.Tuple[int, typing.Tuple]]]:
|
|
"""Takes a timeline, sorts it and output the data from each relevant row from each plugin"""
|
|
# Generate the results for each plugin
|
|
for plugin in self.runable_plugins:
|
|
plugin_name = plugin.__class__.__name__
|
|
try:
|
|
vollog.log(logging.INFO, "Running {}".format(plugin_name))
|
|
for (item, timestamp_type, timestamp) in plugin.generate_timeline():
|
|
times = self.timeline.get((plugin_name, item), {})
|
|
if times.get(timestamp_type, None) is not None:
|
|
vollog.debug(
|
|
"Multiple timestamps for the same plugin/file combination found: {} {}".format(plugin_name,
|
|
item))
|
|
times[timestamp_type] = timestamp
|
|
self.timeline[(plugin_name, item)] = times
|
|
except Exception:
|
|
# FIXME: traceback shouldn't be printed directly, but logged instead
|
|
traceback.print_exc()
|
|
vollog.log(logging.INFO, "Exception occurred running plugin: {}".format(plugin_name))
|
|
|
|
for (plugin_name, item) in self.timeline:
|
|
times = self.timeline[(plugin_name, item)]
|
|
data = (0, [plugin_name, item,
|
|
times.get(TimeLinerType.CREATED, renderers.NotApplicableValue()),
|
|
times.get(TimeLinerType.MODIFIED, renderers.NotApplicableValue()),
|
|
times.get(TimeLinerType.ACCESSED, renderers.NotApplicableValue()),
|
|
times.get(TimeLinerType.CHANGED, renderers.NotApplicableValue())])
|
|
yield data
|
|
|
|
def run(self):
|
|
"""Isolate each plugin and run it"""
|
|
|
|
# Initialize for the run
|
|
sep = configuration.CONFIG_SEPARATOR
|
|
plugin_list = list(framework.class_subclasses(TimeLinerInterface))
|
|
automagics = automagic.available(self._context)
|
|
self.runable_plugins = []
|
|
|
|
# Get the filter from the configuration
|
|
selected_list = self.config.get('plugins', None)
|
|
if selected_list is not None:
|
|
selected_list = selected_list.split(",")
|
|
else:
|
|
# Use all the plugins if there's no filter
|
|
selected_list = [plugin.__name__ for plugin in plugin_list]
|
|
|
|
# Identify plugins that we can run which output datetimes
|
|
for plugin_class in plugin_list:
|
|
usable = False
|
|
plugin_name = plugin_class.__name__
|
|
for selected in selected_list:
|
|
if selected in plugin_name:
|
|
usable = True
|
|
if usable:
|
|
try:
|
|
automagics = automagic.choose_automagic(automagics, plugin_class)
|
|
automagic_config_path = configuration.path_join(self.config_path,
|
|
sep.join(plugin_name.split(sep)[:-1]))
|
|
errors = automagic.run(automagics,
|
|
self.context,
|
|
plugin_class,
|
|
automagic_config_path,
|
|
progress_callback = self._progress_callback)
|
|
for error in errors:
|
|
vollog.log(logging.DEBUG, "\n".join(error.format(chain = True)))
|
|
plugin = plugin_class(self.context,
|
|
configuration.path_join(self.config_path, plugin_name),
|
|
progress_callback = self._progress_callback)
|
|
self.runable_plugins.append(plugin)
|
|
except Exception:
|
|
# Remove the failed plugin from the list and continue
|
|
continue
|
|
|
|
return renderers.TreeGrid(columns = [("Plugin", str),
|
|
("Description", str),
|
|
("Created Date", datetime.datetime),
|
|
("Modified Date", datetime.datetime),
|
|
("Accessed Date", datetime.datetime),
|
|
("Changed Date", datetime.datetime)],
|
|
generator = self._generator())
|