mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-08-30 11:49:42 +02:00
Merge branch 'volatilityfoundation:develop' into linux_issue_1089_module_memory
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(
|
||||
|
||||
@@ -44,8 +44,8 @@ BANG = "!"
|
||||
|
||||
# We use the SemVer 2.0.0 versioning scheme
|
||||
VERSION_MAJOR = 2 # Number of releases of the library with a breaking change
|
||||
VERSION_MINOR = 5 # Number of changes that only add to the interface
|
||||
VERSION_PATCH = 2 # Number of changes that do not change the interface
|
||||
VERSION_MINOR = 6 # Number of changes that only add to the interface
|
||||
VERSION_PATCH = 0 # Number of changes that do not change the interface
|
||||
VERSION_SUFFIX = ""
|
||||
|
||||
# TODO: At version 2.0.0, remove the symbol_shift feature
|
||||
|
||||
@@ -10,6 +10,7 @@ from typing import Generator, Iterator, List, Tuple
|
||||
from volatility3.framework import (
|
||||
class_subclasses,
|
||||
constants,
|
||||
exceptions,
|
||||
interfaces,
|
||||
renderers,
|
||||
)
|
||||
@@ -495,7 +496,7 @@ class Kmsg_5_10_to_(ABCKmsg):
|
||||
class Kmsg(interfaces.plugins.PluginInterface):
|
||||
"""Kernel log buffer reader"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_required_framework_version = (2, 6, 0)
|
||||
|
||||
_version = (1, 0, 2)
|
||||
|
||||
@@ -514,6 +515,13 @@ class Kmsg(interfaces.plugins.PluginInterface):
|
||||
yield (0, values)
|
||||
|
||||
def run(self):
|
||||
if not self.context.symbol_space.verify_table_versions(
|
||||
"dwarf2json", lambda version, _: (not version) or version > (0, 4, 1)
|
||||
):
|
||||
raise exceptions.SymbolSpaceError(
|
||||
"Invalid symbol table, please ensure the ISF table produced by dwarf2json was produced using a version > 0.4.1"
|
||||
)
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("facility", str),
|
||||
|
||||
@@ -17,7 +17,7 @@ class PsList(interfaces.plugins.PluginInterface):
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
_version = (2, 2, 0)
|
||||
_version = (2, 2, 1)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -128,7 +128,7 @@ class PsList(interfaces.plugins.PluginInterface):
|
||||
else:
|
||||
# Find the vma that belongs to the main ELF of the process
|
||||
file_output = "Error outputting file"
|
||||
for v in task.mm.get_mmap_iter():
|
||||
for v in task.mm.get_vma_iter():
|
||||
if v.vm_start == task.mm.start_code:
|
||||
file_handle = elfs.Elfs.elf_dump(
|
||||
self.context,
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
|
||||
import logging, io, pefile
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework import renderers, interfaces, exceptions, constants
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.plugins.windows import pslist
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols.windows.extensions import pe
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IAT(interfaces.plugins.PluginInterface):
|
||||
"""Extract Import Address Table to list API (functions) used by a program contained in external libraries"""
|
||||
|
||||
_required_framework_version = (2, 4, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="pslist", component=pslist.PsList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pid",
|
||||
element_type=int,
|
||||
description="Process ID to include (all other processes are excluded)",
|
||||
optional=True,
|
||||
),
|
||||
]
|
||||
|
||||
def _generator(self, procs):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
for proc in procs:
|
||||
try:
|
||||
proc_id = proc.UniqueProcessId
|
||||
proc_layer_name = proc.add_process_layer()
|
||||
peb = self.context.object(
|
||||
kernel.symbol_table_name + constants.BANG + "_PEB",
|
||||
layer_name=proc_layer_name,
|
||||
offset=proc.Peb,
|
||||
)
|
||||
|
||||
if proc_layer_name is None:
|
||||
raise TypeError("add_process_layer failed")
|
||||
|
||||
pe_table_name = intermed.IntermediateSymbolTable.create(
|
||||
self.context,
|
||||
self.config_path,
|
||||
"windows",
|
||||
"pe",
|
||||
class_types=pe.class_types,
|
||||
)
|
||||
pe_data = io.BytesIO()
|
||||
|
||||
dos_header = self.context.object(
|
||||
pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
|
||||
offset=peb.ImageBaseAddress,
|
||||
layer_name=proc_layer_name,
|
||||
)
|
||||
|
||||
for offset, data in dos_header.reconstruct():
|
||||
pe_data.seek(offset)
|
||||
pe_data.write(data)
|
||||
|
||||
pe_obj = pefile.PE(data=pe_data.getvalue(), fast_load=True)
|
||||
pe_obj.parse_data_directories(
|
||||
[pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_IMPORT"]]
|
||||
)
|
||||
if hasattr(pe_obj, "DIRECTORY_ENTRY_IMPORT"):
|
||||
for entry in pe_obj.DIRECTORY_ENTRY_IMPORT:
|
||||
dll_entry = entry.dll
|
||||
if dll_entry:
|
||||
dll_entry = dll_entry.decode()
|
||||
else:
|
||||
dll_entry = renderers.NotAvailableValue
|
||||
|
||||
bound = True
|
||||
# Initially set to 0 if not bound
|
||||
time_date_stamp = entry.struct.TimeDateStamp
|
||||
if not time_date_stamp:
|
||||
bound = False
|
||||
|
||||
# Iterate over imported functions
|
||||
for imp in entry.imports:
|
||||
import_name = imp.name
|
||||
if import_name:
|
||||
import_name = imp.name.decode()
|
||||
else:
|
||||
import_name = renderers.NotAvailableValue()
|
||||
function_address = (
|
||||
pe_obj.OPTIONAL_HEADER.ImageBase + imp.address
|
||||
)
|
||||
if not function_address:
|
||||
function_address = renderers.NotAvailableValue
|
||||
|
||||
yield (
|
||||
0,
|
||||
(
|
||||
proc_id,
|
||||
proc.ImageFileName.cast(
|
||||
"string",
|
||||
max_length=proc.ImageFileName.vol.count,
|
||||
errors="replace",
|
||||
),
|
||||
dll_entry,
|
||||
bound,
|
||||
import_name,
|
||||
format_hints.Hex(function_address),
|
||||
),
|
||||
)
|
||||
except exceptions.InvalidAddressException as excp:
|
||||
vollog.debug(
|
||||
"Process {}: invalid address {} in layer {}".format(
|
||||
proc_id, excp.invalid_address, excp.layer_name
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
def run(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("PID", int),
|
||||
("Name", str),
|
||||
("Library", str),
|
||||
("Bound", bool),
|
||||
("Function", str),
|
||||
("Address", format_hints.Hex),
|
||||
],
|
||||
self._generator(
|
||||
pslist.PsList.list_processes(
|
||||
context=self.context,
|
||||
layer_name=kernel.layer_name,
|
||||
symbol_table=kernel.symbol_table_name,
|
||||
filter_func=pslist.PsList.create_pid_filter(
|
||||
self.config.get("pid", None)
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,144 @@
|
||||
# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
|
||||
from typing import Iterable, Generator, List, Tuple
|
||||
|
||||
from volatility3.framework import constants, interfaces, renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces.configuration import RequirementInterface
|
||||
from volatility3.framework.interfaces.objects import ObjectInterface
|
||||
from volatility3.framework.objects import Bytes, DataFormatInfo, Integer, StructType
|
||||
from volatility3.framework.objects.templates import ObjectTemplate
|
||||
from volatility3.framework.objects.utility import array_to_string
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows.extensions import pe
|
||||
|
||||
from volatility3.plugins.windows import modules
|
||||
|
||||
|
||||
class Passphrase(interfaces.plugins.PluginInterface):
|
||||
"""TrueCrypt Cached Passphrase Finder"""
|
||||
|
||||
_version = (0, 1, 0)
|
||||
_required_framework_version = (2, 5, 2)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[RequirementInterface]:
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
"kernel",
|
||||
description="Windows kernel",
|
||||
architectures=["Intel32", "Intel64"],
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="modules", component=modules.Modules, version=(1, 1, 0)
|
||||
),
|
||||
requirements.IntRequirement(
|
||||
name="min-length",
|
||||
description="Minimum length of passphrases to identify",
|
||||
default=5,
|
||||
optional=True,
|
||||
),
|
||||
]
|
||||
|
||||
def scan_module(
|
||||
self, module_base: int, layer_name: str
|
||||
) -> Generator[Tuple[int, str], None, None]:
|
||||
"""Scans the TrueCrypt kernel module for cached passphrases.
|
||||
|
||||
Args:
|
||||
module_base: the module's DLL base
|
||||
layer_name: the name of the layer in which the module resides
|
||||
|
||||
Generates:
|
||||
A tuple of the offset at which a password is found, and the password
|
||||
"""
|
||||
pe_table_name = intermed.IntermediateSymbolTable.create(
|
||||
self.context, self.config_path, "windows", "pe", class_types=pe.class_types
|
||||
)
|
||||
dos_header: pe.IMAGE_DOS_HEADER = self.context.object(
|
||||
pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER",
|
||||
layer_name,
|
||||
module_base,
|
||||
)
|
||||
data_section: StructType = next(
|
||||
sec
|
||||
for sec in dos_header.get_nt_header().get_sections()
|
||||
if array_to_string(sec.Name) == ".data"
|
||||
)
|
||||
base: int = data_section.VirtualAddress + module_base
|
||||
size: int = data_section.Misc.VirtualSize
|
||||
# Looking at `Length` in TrueCrypt/Common/Password.h::Password struct
|
||||
DWORD_SIZE_BYTES: int = 4
|
||||
format = DataFormatInfo(
|
||||
length=DWORD_SIZE_BYTES, byteorder="little", signed=True
|
||||
)
|
||||
int32 = ObjectTemplate(
|
||||
Integer, pe_table_name + constants.BANG + "int", data_format=format
|
||||
)
|
||||
count, not_aligned = divmod(size, DWORD_SIZE_BYTES)
|
||||
if not_aligned:
|
||||
raise ValueError("PE data section not DWORD-aligned!")
|
||||
lengths = self.context.object(
|
||||
pe_table_name + constants.BANG + "array",
|
||||
layer_name,
|
||||
base,
|
||||
count=count,
|
||||
subtype=int32,
|
||||
)
|
||||
min_length = self.config.get("min-length")
|
||||
for length in lengths:
|
||||
# TrueCrypt maximum password length is 64
|
||||
# (see TrueCrypt/Common/Password.h)
|
||||
if not min_length <= length <= 64:
|
||||
continue
|
||||
offset = length.vol["offset"] + DWORD_SIZE_BYTES
|
||||
passphrase: Bytes = self.context.object(
|
||||
pe_table_name + constants.BANG + "bytes",
|
||||
layer_name,
|
||||
offset,
|
||||
length=length,
|
||||
)
|
||||
# TrueCrypt/Common/Password.c permits chars in the range
|
||||
# [0x20, 0x7F).
|
||||
if not all(0x20 <= c < 0x7F for c in passphrase):
|
||||
continue
|
||||
# TrueCrypt/Common/Password.h::Password struct is padded with
|
||||
# 3 zero bytes to keep 64-byte alignment.
|
||||
buf: Bytes = self.context.object(
|
||||
pe_table_name + constants.BANG + "bytes",
|
||||
layer_name,
|
||||
offset + length + 1, # +1 for '\0'-terminated password string
|
||||
length=3,
|
||||
)
|
||||
if any(buf):
|
||||
continue
|
||||
# Password found.
|
||||
yield offset, passphrase.decode(encoding="ascii")
|
||||
|
||||
def _generator(self):
|
||||
kernel = self.context.modules[self.config["kernel"]]
|
||||
mods: Iterable[ObjectInterface] = modules.Modules.list_modules(
|
||||
self.context, kernel.layer_name, kernel.symbol_table_name
|
||||
)
|
||||
truecrypt_module_base = next(
|
||||
mod.DllBase
|
||||
for mod in mods
|
||||
if mod.BaseDllName.get_string().lower() == "truecrypt.sys"
|
||||
)
|
||||
for offset, password in self.scan_module(
|
||||
truecrypt_module_base, kernel.layer_name
|
||||
):
|
||||
yield (0, (format_hints.Hex(offset), len(password), password))
|
||||
|
||||
def run(self) -> renderers.TreeGrid:
|
||||
return renderers.TreeGrid(
|
||||
[
|
||||
("Offset", format_hints.Hex),
|
||||
("Length", int),
|
||||
("Password", str),
|
||||
],
|
||||
self._generator(),
|
||||
)
|
||||
@@ -4,9 +4,20 @@
|
||||
|
||||
import collections
|
||||
import collections.abc
|
||||
import datetime
|
||||
import enum
|
||||
import logging
|
||||
from typing import Any, Dict, Iterable, Iterator, TypeVar, List
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
Iterator,
|
||||
Optional,
|
||||
Tuple,
|
||||
TypeVar,
|
||||
List,
|
||||
)
|
||||
|
||||
from volatility3.framework import constants, exceptions, interfaces, objects
|
||||
|
||||
@@ -113,6 +124,42 @@ class SymbolSpace(interfaces.symbols.SymbolSpaceInterface):
|
||||
self._resolved = {}
|
||||
del self._dict[key]
|
||||
|
||||
def verify_table_versions(
|
||||
self,
|
||||
producer: str,
|
||||
validator: Callable[[Optional[Tuple], Optional[datetime.datetime]], bool],
|
||||
tables: List[str] = None,
|
||||
) -> bool:
|
||||
"""Verifies the producer metadata and version of tables
|
||||
|
||||
Args:
|
||||
producer: String name of a table producer to have validation performed
|
||||
validator: callable that takes an optional version and an optional datetime that returns False if table is invalid
|
||||
|
||||
Returns:
|
||||
False if an invalid table was found or True if no invalid table was found
|
||||
"""
|
||||
if tables is None:
|
||||
tables = self._dict.keys()
|
||||
for table_name in tables:
|
||||
table = self._dict[table_name]
|
||||
if not table.producer:
|
||||
vollog.debug(
|
||||
f"Symbol table {table_name} could not be validated because no producer metadata was found"
|
||||
)
|
||||
continue
|
||||
if table.producer.name == producer:
|
||||
# Run the verification
|
||||
if not validator(
|
||||
table.producer.version,
|
||||
table.producer.datetime,
|
||||
):
|
||||
vollog.debug(f"Symbol table {table_name} does not pass validator")
|
||||
return False
|
||||
else:
|
||||
continue
|
||||
return True
|
||||
|
||||
### Resolution functions
|
||||
|
||||
class UnresolvedTemplate(objects.templates.ReferenceTemplate):
|
||||
|
||||
@@ -183,6 +183,7 @@ class IntermediateSymbolTable(interfaces.symbols.SymbolTableInterface):
|
||||
types = _construct_delegate_function("types", True)
|
||||
enumerations = _construct_delegate_function("enumerations", True)
|
||||
metadata = _construct_delegate_function("metadata", True)
|
||||
producer = _construct_delegate_function("producer", True)
|
||||
clear_symbol_cache = _construct_delegate_function("clear_symbol_cache")
|
||||
get_type = _construct_delegate_function("get_type")
|
||||
get_symbol = _construct_delegate_function("get_symbol")
|
||||
@@ -372,6 +373,14 @@ class ISFormatTable(interfaces.symbols.SymbolTableInterface, metaclass=ABCMeta):
|
||||
table."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def producer(self) -> Optional["metadata.ProducerMetadata"]:
|
||||
"""Returns a metadata object containing information about the symbol
|
||||
table."""
|
||||
return metadata.ProducerMetadata(
|
||||
self._json_object.get("metadata", {}).get("producer", {})
|
||||
)
|
||||
|
||||
def clear_symbol_cache(self) -> None:
|
||||
"""Clears the symbol cache of the symbol table."""
|
||||
self._symbol_cache.clear()
|
||||
|
||||
@@ -517,12 +517,25 @@ class maple_tree(objects.StructType):
|
||||
|
||||
|
||||
class mm_struct(objects.StructType):
|
||||
|
||||
# TODO: As of version 3.0.0 this method should be removed
|
||||
def get_mmap_iter(self) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Returns an iterator for the mmap list member of an mm_struct."""
|
||||
"""
|
||||
Deprecated: Use either get_vma_iter() or _get_mmap_iter().
|
||||
"""
|
||||
vollog.warning(
|
||||
"This method has been deprecated in favour of using the get_vma_iter() method."
|
||||
)
|
||||
yield from self.get_vma_iter()
|
||||
|
||||
def _get_mmap_iter(self) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Returns an iterator for the mmap list member of an mm_struct. Use this only if
|
||||
required, get_vma_iter() will choose the correct _get_maple_tree_iter() or
|
||||
_get_mmap_iter() automatically as required."""
|
||||
|
||||
if not self.has_member("mmap"):
|
||||
raise AttributeError(
|
||||
"get_mmap_iter called on mm_struct where no mmap member exists."
|
||||
"_get_mmap_iter called on mm_struct where no mmap member exists."
|
||||
)
|
||||
if not self.mmap:
|
||||
return None
|
||||
@@ -536,12 +549,24 @@ class mm_struct(objects.StructType):
|
||||
seen.add(link.vol.offset)
|
||||
link = link.vm_next
|
||||
|
||||
# TODO: As of version 3.0.0 this method should be removed
|
||||
def get_maple_tree_iter(self) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Returns an iterator for the mm_mt member of an mm_struct."""
|
||||
"""
|
||||
Deprecated: Use either get_vma_iter() or _get_maple_tree_iter().
|
||||
"""
|
||||
vollog.warning(
|
||||
"This method has been deprecated in favour of using the get_vma_iter() method."
|
||||
)
|
||||
yield from self.get_vma_iter()
|
||||
|
||||
def _get_maple_tree_iter(self) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Returns an iterator for the mm_mt member of an mm_struct. Use this only if
|
||||
required, get_vma_iter() will choose the correct _get_maple_tree_iter() or
|
||||
get_mmap_iter() automatically as required."""
|
||||
|
||||
if not self.has_member("mm_mt"):
|
||||
raise AttributeError(
|
||||
"get_maple_tree_iter called on mm_struct where no mm_mt member exists."
|
||||
"_get_maple_tree_iter called on mm_struct where no mm_mt member exists."
|
||||
)
|
||||
symbol_table_name = self.get_symbol_table_name()
|
||||
for vma_pointer in self.mm_mt.get_slot_iter():
|
||||
@@ -557,9 +582,9 @@ class mm_struct(objects.StructType):
|
||||
"""Returns an iterator for the VMAs in an mm_struct. Automatically choosing the mmap or mm_mt as required."""
|
||||
|
||||
if self.has_member("mmap"):
|
||||
yield from self.get_mmap_iter()
|
||||
yield from self._get_mmap_iter()
|
||||
elif self.has_member("mm_mt"):
|
||||
yield from self.get_maple_tree_iter()
|
||||
yield from self._get_maple_tree_iter()
|
||||
else:
|
||||
raise AttributeError("Unable to find mmap or mm_mt in mm_struct")
|
||||
|
||||
|
||||
@@ -2,9 +2,49 @@
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
from typing import Optional, Tuple, Union
|
||||
|
||||
from volatility3.framework import interfaces
|
||||
from volatility3.framework import constants, interfaces
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ProducerMetadata(interfaces.symbols.MetadataInterface):
|
||||
"""Class to handle the Producer metadata from an ISF"""
|
||||
|
||||
@property
|
||||
def name(self) -> Optional[str]:
|
||||
return self._json_data.get("name", None)
|
||||
|
||||
@property
|
||||
def version(self) -> Optional[Tuple[int]]:
|
||||
"""Returns the version of the ISF file producer"""
|
||||
version = self._json_data.get("version", None)
|
||||
if not version:
|
||||
return None
|
||||
if all([x in "0123456789." for x in version]):
|
||||
return tuple([int(x) for x in version.split(".")])
|
||||
vollog.log(
|
||||
constants.LOGLEVEL_VVVV,
|
||||
f"Metadata version contains unexpected characters: '{version}'",
|
||||
)
|
||||
return None
|
||||
|
||||
@property
|
||||
def datetime(self) -> Optional[datetime.datetime]:
|
||||
"""Returns a timestamp for when the file was produced"""
|
||||
if "datetime" not in self._json_data:
|
||||
return None
|
||||
try:
|
||||
timestamp = datetime.datetime.strptime(
|
||||
self._json_data["datetime"], "YYYY-MM-DD"
|
||||
)
|
||||
except (TypeError, ValueError):
|
||||
vollog.debug("Invalid timestamp in producer information of symbol table")
|
||||
return None
|
||||
return timestamp
|
||||
|
||||
|
||||
class WindowsMetadata(interfaces.symbols.MetadataInterface):
|
||||
|
||||
@@ -0,0 +1,507 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/schema#",
|
||||
"id": "http://volatilityfoundation.org/intermediate-format/schema",
|
||||
"title": "Symbol Container",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"metadata_producer": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+.[0-9]+.[0-9]+$"
|
||||
},
|
||||
"datetime": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
}
|
||||
},
|
||||
"required":[
|
||||
"name",
|
||||
"version"
|
||||
]
|
||||
},
|
||||
"metadata_windows_pe": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"major": {
|
||||
"type": "integer"
|
||||
},
|
||||
"minor": {
|
||||
"type": "integer"
|
||||
},
|
||||
"revision": {
|
||||
"type": "integer"
|
||||
},
|
||||
"build": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"major",
|
||||
"minor",
|
||||
"revision"
|
||||
]
|
||||
},
|
||||
"metadata_windows_pdb": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"GUID": {
|
||||
"type": "string"
|
||||
},
|
||||
"age": {
|
||||
"type": "integer"
|
||||
},
|
||||
"database": {
|
||||
"type": "string"
|
||||
},
|
||||
"machine_type": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"GUID",
|
||||
"age",
|
||||
"database",
|
||||
"machine_type"
|
||||
]
|
||||
},
|
||||
"metadata_windows": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pe": {
|
||||
"$ref": "#/definitions/metadata_windows_pe"
|
||||
},
|
||||
"pdb": {
|
||||
"$ref": "#/definitions/metadata_windows_pdb"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"metadata_nix": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"symbols": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/metadata_nix_item"
|
||||
}
|
||||
},
|
||||
"types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/metadata_nix_item"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"metadata_format": {
|
||||
"type": "string",
|
||||
"pattern": "^6.[0-9]+.[0-9]+$"
|
||||
},
|
||||
"metadata_nix_item": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"pattern": "^(dwarf|symtab|system-map)$"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"hash_type": {
|
||||
"type": "string",
|
||||
"pattern": "^(sha256)$"
|
||||
},
|
||||
"hash_value": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-fA-F0-9]+$"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"element_metadata": {
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{
|
||||
"properties": {
|
||||
"format": {
|
||||
"$ref": "#/definitions/metadata_format"
|
||||
},
|
||||
"producer": {
|
||||
"$ref": "#/definitions/metadata_producer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"format",
|
||||
"producer"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"format": {
|
||||
"$ref": "#/definitions/metadata_format"
|
||||
},
|
||||
"producer": {
|
||||
"$ref": "#/definitions/metadata_producer"
|
||||
},
|
||||
"windows": {
|
||||
"$ref": "#/definitions/metadata_windows"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"format",
|
||||
"producer",
|
||||
"windows"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"format": {
|
||||
"$ref": "#/definitions/metadata_format"
|
||||
},
|
||||
"producer": {
|
||||
"$ref": "#/definitions/metadata_producer"
|
||||
},
|
||||
"linux": {
|
||||
"$ref": "#/definitions/metadata_nix"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"format",
|
||||
"producer",
|
||||
"linux"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"format": {
|
||||
"$ref": "#/definitions/metadata_format"
|
||||
},
|
||||
"producer": {
|
||||
"$ref": "#/definitions/metadata_producer"
|
||||
},
|
||||
"mac": {
|
||||
"$ref": "#/definitions/metadata_nix"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"format",
|
||||
"producer",
|
||||
"mac"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"element_enum": {
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"base": {
|
||||
"type": "string"
|
||||
},
|
||||
"constants": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"size",
|
||||
"base",
|
||||
"constants"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"element_symbol": {
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "number"
|
||||
},
|
||||
"linkage_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"$ref": "#/definitions/type_descriptor"
|
||||
},
|
||||
"constant_data": {
|
||||
"type": "string",
|
||||
"media": {
|
||||
"binaryEncoding": "base64",
|
||||
"readOnly": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"address"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"element_base_type": {
|
||||
"properties": {
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"signed": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"pattern": "^(void|int|float|char|bool)$"
|
||||
},
|
||||
"endian": {
|
||||
"type": "string",
|
||||
"pattern": "^(little|big)$"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"size",
|
||||
"kind",
|
||||
"signed",
|
||||
"endian"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"element_user_type": {
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"pattern": "^(struct|union|class)$"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer"
|
||||
},
|
||||
"fields": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/field"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"size",
|
||||
"fields"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"field": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"$ref": "#/definitions/type_descriptor"
|
||||
},
|
||||
"offset": {
|
||||
"type": "integer"
|
||||
},
|
||||
"anonymous": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"offset"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type_descriptor": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/type_pointer"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/type_base"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/type_array"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/type_struct"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/type_enum"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/type_function"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/type_bitfield"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type_pointer": {
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"pattern": "^pointer$"
|
||||
},
|
||||
"base": {
|
||||
"type": "string"
|
||||
},
|
||||
"subtype": {
|
||||
"$ref": "#/definitions/type_descriptor"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"subtype"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type_base": {
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"pattern": "^base$"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type_array": {
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"pattern": "^array$"
|
||||
},
|
||||
"subtype": {
|
||||
"$ref": "#/definitions/type_descriptor"
|
||||
},
|
||||
"count": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"subtype",
|
||||
"count"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type_struct": {
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"pattern": "^(struct|class|union)$"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type_enum": {
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"pattern": "^enum$"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"name"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type_function": {
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"pattern": "^function$"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind"
|
||||
],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"type_bitfield": {
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"pattern": "^bitfield$"
|
||||
},
|
||||
"bit_position": {
|
||||
"type": "integer"
|
||||
},
|
||||
"bit_length": {
|
||||
"type": "integer"
|
||||
},
|
||||
"type": {
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/type_base"
|
||||
},
|
||||
{
|
||||
"$ref": "#/definitions/type_enum"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"bit_position",
|
||||
"bit_length",
|
||||
"type"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"metadata": {
|
||||
"$ref": "#/definitions/element_metadata"
|
||||
},
|
||||
"base_types": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/element_base_type"
|
||||
}
|
||||
},
|
||||
"user_types": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/element_user_type"
|
||||
}
|
||||
},
|
||||
"enums": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/element_enum"
|
||||
}
|
||||
},
|
||||
"symbols": {
|
||||
"additionalProperties": {
|
||||
"$ref": "#/definitions/element_symbol"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"metadata",
|
||||
"base_types",
|
||||
"user_types",
|
||||
"enums",
|
||||
"symbols"
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
Reference in New Issue
Block a user