mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-07 10:17:38 +02:00
Merge pull request #754 from volatilityfoundation/feature/cli-config-hierarchy
CLI: Add support for a configuration options file
This commit is contained in:
@@ -143,3 +143,18 @@ Options
|
||||
`hivescan` would match `windows.registry.hivescan.HiveScan`, but
|
||||
`pslist` is ambiguous because it could match `windows.pslist` or
|
||||
`linux.pslist`.
|
||||
|
||||
Overriding options
|
||||
------------------
|
||||
|
||||
The default values for the command line interface are defined by constants within the code,
|
||||
but can be overridden by creating a JSON file (`%APPDATA%/volatility3/vol.json` for Windows
|
||||
systems, or `~/.config/volatility3/vol.json` or `volshell.json` for all others).
|
||||
|
||||
The format of this file is a JSON dictionary, containing the options above and their value.
|
||||
It should be noted that the ordering is (`<` means is overridden by):
|
||||
|
||||
`in-built default value < config file value < command line parameter`
|
||||
|
||||
It should also be noted that boolean flags (such as `offline`) that are overridden as true will
|
||||
not be unset by not specifying the command line flag.
|
||||
|
||||
+68
-14
@@ -19,7 +19,7 @@ import os
|
||||
import sys
|
||||
import tempfile
|
||||
import traceback
|
||||
from typing import Any, Dict, Type, Union
|
||||
from typing import Any, Dict, List, Tuple, Type, Union
|
||||
from urllib import parse, request
|
||||
|
||||
import volatility3.plugins
|
||||
@@ -105,6 +105,9 @@ class CommandLine:
|
||||
]
|
||||
)
|
||||
|
||||
# Load up system defaults
|
||||
delayed_logs, default_config = self.load_system_defaults("vol.json")
|
||||
|
||||
parser = volargparse.HelpfulArgParser(
|
||||
add_help=False,
|
||||
prog=self.CLI_NAME,
|
||||
@@ -231,6 +234,8 @@ class CommandLine:
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
parser.set_defaults(**default_config)
|
||||
|
||||
# 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.
|
||||
known_args = [arg for arg in sys.argv if arg != "--help" and arg != "-h"]
|
||||
@@ -241,19 +246,7 @@ class CommandLine:
|
||||
banner_output = sys.stderr
|
||||
banner_output.write(f"Volatility 3 Framework {constants.PACKAGE_VERSION}\n")
|
||||
|
||||
if partial_args.plugin_dirs:
|
||||
volatility3.plugins.__path__ = [
|
||||
os.path.abspath(p) for p in partial_args.plugin_dirs.split(";")
|
||||
] + constants.PLUGINS_PATH
|
||||
|
||||
if partial_args.symbol_dirs:
|
||||
volatility3.symbols.__path__ = [
|
||||
os.path.abspath(p) for p in partial_args.symbol_dirs.split(";")
|
||||
] + constants.SYMBOL_BASEPATHS
|
||||
|
||||
if partial_args.cache_path:
|
||||
constants.CACHE_PATH = partial_args.cache_path
|
||||
|
||||
### Start up logging
|
||||
if partial_args.log:
|
||||
file_logger = logging.FileHandler(partial_args.log)
|
||||
file_logger.setLevel(1)
|
||||
@@ -271,6 +264,23 @@ class CommandLine:
|
||||
else:
|
||||
console.setLevel(10 - (partial_args.verbosity - 2))
|
||||
|
||||
for level, msg in delayed_logs:
|
||||
vollog.log(level, msg)
|
||||
|
||||
### Alter constants if necessary
|
||||
if partial_args.plugin_dirs:
|
||||
volatility3.plugins.__path__ = [
|
||||
os.path.abspath(p) for p in partial_args.plugin_dirs.split(";")
|
||||
] + constants.PLUGINS_PATH
|
||||
|
||||
if partial_args.symbol_dirs:
|
||||
volatility3.symbols.__path__ = [
|
||||
os.path.abspath(p) for p in partial_args.symbol_dirs.split(";")
|
||||
] + constants.SYMBOL_BASEPATHS
|
||||
|
||||
if partial_args.cache_path:
|
||||
constants.CACHE_PATH = partial_args.cache_path
|
||||
|
||||
vollog.info(f"Volatility plugins path: {volatility3.plugins.__path__}")
|
||||
vollog.info(f"Volatility symbols path: {volatility3.symbols.__path__}")
|
||||
|
||||
@@ -463,6 +473,50 @@ class CommandLine:
|
||||
)
|
||||
return requirements.URIRequirement.location_from_file(filename)
|
||||
|
||||
def load_system_defaults(
|
||||
self, filename: str
|
||||
) -> Tuple[List[Tuple[int, str]], Dict[str, Any]]:
|
||||
"""Modify the main configuration based on the default configuration override"""
|
||||
# Build the config path
|
||||
default_config_path = os.path.join(
|
||||
os.path.expanduser("~"), ".config", "volatility3", filename
|
||||
)
|
||||
if sys.platform == "win32":
|
||||
default_config_path = os.path.join(
|
||||
os.environ.get("APPDATA", os.path.expanduser("~")),
|
||||
"volatility3",
|
||||
filename,
|
||||
)
|
||||
|
||||
delayed_logs = []
|
||||
|
||||
# Process it if the files exist
|
||||
if os.path.exists(default_config_path):
|
||||
with open(default_config_path, "rb") as config_json:
|
||||
result = json.load(config_json)
|
||||
if not isinstance(result, dict):
|
||||
delayed_logs.append(
|
||||
(
|
||||
logging.INFO,
|
||||
f"Default configuration file {default_config_path} does not contain a dictionary",
|
||||
)
|
||||
)
|
||||
else:
|
||||
delayed_logs.append(
|
||||
(
|
||||
logging.INFO,
|
||||
f"Loading default configuration options from {default_config_path}",
|
||||
)
|
||||
)
|
||||
delayed_logs.append(
|
||||
(
|
||||
logging.DEBUG,
|
||||
f"Loaded configuration: {json.dumps(result, indent = 2, sort_keys = True)}",
|
||||
)
|
||||
)
|
||||
return delayed_logs, result
|
||||
return delayed_logs, {}
|
||||
|
||||
def process_exceptions(self, excp):
|
||||
"""Provide useful feedback if an exception occurs during a run of a plugin."""
|
||||
# Ensure there's nothing in the cache
|
||||
|
||||
@@ -22,12 +22,14 @@ from volatility3.framework import (
|
||||
)
|
||||
|
||||
# Make sure we log everything
|
||||
|
||||
rootlog = logging.getLogger()
|
||||
vollog = logging.getLogger()
|
||||
vollog.setLevel(0)
|
||||
# Trim the console down by default
|
||||
console = logging.StreamHandler()
|
||||
console.setLevel(logging.WARNING)
|
||||
formatter = logging.Formatter("%(levelname)-8s %(name)-12s: %(message)s")
|
||||
# Trim the console down by default
|
||||
console.setFormatter(formatter)
|
||||
vollog.addHandler(console)
|
||||
|
||||
@@ -53,6 +55,9 @@ class VolShell(cli.CommandLine):
|
||||
|
||||
framework.require_interface_version(2, 0, 0)
|
||||
|
||||
# Load up system defaults
|
||||
delayed_logs, default_config = self.load_system_defaults("volshell.json")
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog=self.CLI_NAME,
|
||||
description="A tool for interactivate forensic analysis of memory images",
|
||||
@@ -146,6 +151,12 @@ class VolShell(cli.CommandLine):
|
||||
default=constants.CACHE_PATH,
|
||||
type=str,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--offline",
|
||||
help="Do not search online for additional JSON files",
|
||||
default=False,
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
# Volshell specific flags
|
||||
os_specific = parser.add_mutually_exclusive_group(required=False)
|
||||
@@ -167,26 +178,14 @@ class VolShell(cli.CommandLine):
|
||||
"-m", "--mac", default=False, action="store_true", help="Run a Mac volshell"
|
||||
)
|
||||
|
||||
parser.set_defaults(**default_config)
|
||||
|
||||
# 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.
|
||||
known_args = [arg for arg in sys.argv if arg != "--help" and arg != "-h"]
|
||||
partial_args, _ = parser.parse_known_args(known_args)
|
||||
if partial_args.plugin_dirs:
|
||||
volatility3.plugins.__path__ = [
|
||||
os.path.abspath(p) for p in partial_args.plugin_dirs.split(";")
|
||||
] + constants.PLUGINS_PATH
|
||||
|
||||
if partial_args.symbol_dirs:
|
||||
volatility3.symbols.__path__ = [
|
||||
os.path.abspath(p) for p in partial_args.symbol_dirs.split(";")
|
||||
] + constants.SYMBOL_BASEPATHS
|
||||
|
||||
if partial_args.cache_path:
|
||||
constants.CACHE_PATH = partial_args.cache_path
|
||||
|
||||
vollog.info(f"Volatility plugins path: {volatility3.plugins.__path__}")
|
||||
vollog.info(f"Volatility symbols path: {volatility3.symbols.__path__}")
|
||||
|
||||
### Start up logging
|
||||
if partial_args.log:
|
||||
file_logger = logging.FileHandler(partial_args.log)
|
||||
file_logger.setLevel(0)
|
||||
@@ -203,9 +202,32 @@ class VolShell(cli.CommandLine):
|
||||
else:
|
||||
console.setLevel(10 - (partial_args.verbosity - 2))
|
||||
|
||||
for level, msg in delayed_logs:
|
||||
vollog.log(level, msg)
|
||||
|
||||
### Alter constants if necessary
|
||||
if partial_args.plugin_dirs:
|
||||
volatility3.plugins.__path__ = [
|
||||
os.path.abspath(p) for p in partial_args.plugin_dirs.split(";")
|
||||
] + constants.PLUGINS_PATH
|
||||
|
||||
if partial_args.symbol_dirs:
|
||||
volatility3.symbols.__path__ = [
|
||||
os.path.abspath(p) for p in partial_args.symbol_dirs.split(";")
|
||||
] + constants.SYMBOL_BASEPATHS
|
||||
|
||||
if partial_args.cache_path:
|
||||
constants.CACHE_PATH = partial_args.cache_path
|
||||
|
||||
vollog.info(f"Volatility plugins path: {volatility3.plugins.__path__}")
|
||||
vollog.info(f"Volatility symbols path: {volatility3.symbols.__path__}")
|
||||
|
||||
if partial_args.clear_cache:
|
||||
framework.clear_cache()
|
||||
|
||||
if partial_args.offline:
|
||||
constants.OFFLINE = partial_args.offline
|
||||
|
||||
# Do the initialization
|
||||
ctx = contexts.Context() # Construct a blank context
|
||||
failures = framework.import_files(
|
||||
|
||||
Reference in New Issue
Block a user