Merge branch 'volatilityfoundation:develop' into linux_issue_1089_module_memory

This commit is contained in:
Eve
2024-02-21 11:51:54 +00:00
committed by GitHub
13 changed files with 1064 additions and 43 deletions
+15
View File
@@ -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
View File
@@ -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
+38 -16
View File
@@ -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(
+2 -2
View File
@@ -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
+9 -1
View File
@@ -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(),
)
+48 -1
View File
@@ -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")
+41 -1
View File
@@ -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):
+507
View File
@@ -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
}