mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-06 17:57:38 +02:00
Merge branch 'volatilityfoundation:develop' into feature/reg-cert
This commit is contained in:
+104
-53
@@ -6,6 +6,12 @@ This guide will step through how to construct a simple plugin using Volatility 3
|
||||
The example plugin we'll use is :py:class:`~volatility3.plugins.windows.dlllist.DllList`, which features the main traits
|
||||
of a normal plugin, and reuses other plugins appropriately.
|
||||
|
||||
.. note::
|
||||
|
||||
This document will not include the complete code necessary for a
|
||||
working plugin (such as imports, etc) since it's designed to focus on the necessary componets for writing a plugin.
|
||||
For complete and functioning plugins, the ``framework/plugins`` directory should be consulted.
|
||||
|
||||
Inherit from PluginInterface
|
||||
----------------------------
|
||||
|
||||
@@ -30,20 +36,20 @@ to be able to run properly. Any that are defined as optional need not necessari
|
||||
|
||||
::
|
||||
|
||||
_version = (1, 0, 0)
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
return [requirements.TranslationLayerRequirement(name = 'primary',
|
||||
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)),
|
||||
return [requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.ListRequirement(name = 'pid',
|
||||
element_type = int,
|
||||
description = "Process IDs to include (all other processes are excluded)",
|
||||
optional = True)]
|
||||
optional = True),
|
||||
requirements.PluginRequirement(name = 'pslist',
|
||||
plugin = pslist.PsList,
|
||||
version = (2, 0, 0))]
|
||||
|
||||
|
||||
This is a classmethod, because it is called before the specific plugin object has been instantiated (in order to know how
|
||||
@@ -51,69 +57,112 @@ to instantiate the plugin). At the moment these requirements are fairly straigh
|
||||
|
||||
::
|
||||
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
|
||||
This requirement indicates that the plugin will operate on a single
|
||||
:py:class:`TranslationLayer <volatility3.framework.interfaces.layers.TranslationLayerInterface>`. The name of the
|
||||
loaded layer will appear in the plugin's configuration under the name ``primary``. Requirement values can be
|
||||
accessed within the plugin through the plugin's `config` attribute (for example ``self.config['pid']``).
|
||||
This requirement specifies the need for a particular submodule. Each module requires a
|
||||
:py:class:`TranslationLayer <volatility3.framework.interfaces.layers.TranslationLayerInterface>` and a
|
||||
:py:class:`SymbolTable <volatility3.framework.interfaces.symbols.SymbolTableInterface>`, which are fulfilled by two
|
||||
subrequirements: a
|
||||
:py:class:`~volatility3.framework.configuration.requirements.TranslationLayerRequirement` and a
|
||||
:py:class:`~volatility3.framework.configuration.requirements.SymbolTableRequirement`. At the moment, the automagic
|
||||
only fills `ModuleRequirements` with kernels, and so has relatively few parameters. It requires the architecture for
|
||||
the underlying TranslationLayer, and the offset of the module within that layer.
|
||||
|
||||
.. note:: The name itself is dynamic depending on the other layers already present in the Context. Always use the value
|
||||
from the configuration rather than attempting to guess what the layer will be called.
|
||||
The name of the module will be stored in the ``kernel`` configuration option, and the module object itself
|
||||
can be accessed from the ``context.modules`` collection. This requirement is a Complex Requirement and therefore will
|
||||
not be requested directly from the user.
|
||||
|
||||
Finally, this defines that the translation layer must be on the Intel Architecture. At the moment, this acts as a filter,
|
||||
failing to be satisfied by memory images that do not match the architecture required.
|
||||
|
||||
Most plugins will only operate on a single layer, but it is entirely possible for a plugin to request two different
|
||||
layers, for example a plugin that carries out some form of difference or statistics against multiple memory images.
|
||||
.. note::
|
||||
|
||||
This requirement (and the next two) are known as Complex Requirements, and user interfaces will likely not directly
|
||||
request a value for this from a user. The value stored in the configuration tree for a
|
||||
:py:class:`~volatility3.framework.configuration.requirements.TranslationLayerRequirement` is
|
||||
the string name of a layer present in the context's memory that satisfies the requirement.
|
||||
In previous versions of volatility 3, there was no `ModuleRequirement`, and instead two requirements were defined
|
||||
a :py:class:`TranslationLayer <volatility3.framework.interfaces.layers.TranslationLayerInterface>` and a `SymbolTableRequirement`. These still exist, and can be used, most plugins just
|
||||
define a single `ModuleRequirement` for the kernel, which the automagic will populate. The `ModuleRequirement` has
|
||||
two automatic sub-requirements, a `TranslationLayerRequirement` and a `SymbolTableRequirement`, but the module also
|
||||
includes the offset of the module, and will allow future expansion to specify specific modules when application
|
||||
level plugins become more common. Below are how the requirements would be specified:
|
||||
|
||||
::
|
||||
::
|
||||
|
||||
requirements.SymbolTableRequirement(name = "nt_symbols",
|
||||
description = "Windows kernel symbols"),
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
|
||||
This requirement specifies the need for a particular
|
||||
:py:class:`SymbolTable <volatility3.framework.interfaces.symbols.SymbolTableInterface>`
|
||||
to be loaded. This gets populated by various
|
||||
:py:class:`Automagic <volatility3.framework.interfaces.automagic.AutoMagicInterface>` as the nearest sibling to a particular
|
||||
:py:class:`~volatility3.framework.configuration.requirements.TranslationLayerRequirement`.
|
||||
This means that if the :py:class:`~volatility3.framework.configuration.requirements.TranslationLayerRequirement`
|
||||
is satisfied and the :py:class:`Automagic <volatility3.framework.interfaces.automagic.AutoMagicInterface>` can determine
|
||||
the appropriate :py:class:`SymbolTable <volatility3.framework.interfaces.symbols.SymbolTableInterface>`, the
|
||||
name of the :py:class:`SymbolTable <volatility3.framework.interfaces.symbols.SymbolTableInterface>` will be stored in the configuration.
|
||||
This requirement indicates that the plugin will operate on a single
|
||||
:py:class:`TranslationLayer <volatility3.framework.interfaces.layers.TranslationLayerInterface>`. The name of the
|
||||
loaded layer will appear in the plugin's configuration under the name ``primary``. Requirement values can be
|
||||
accessed within the plugin through the plugin's `config` attribute (for example ``self.config['pid']``).
|
||||
|
||||
This requirement is also a Complex Requirement and therefore will not be requested directly from the user.
|
||||
.. note:: The name itself is dynamic depending on the other layers already present in the Context. Always use the value
|
||||
from the configuration rather than attempting to guess what the layer will be called.
|
||||
|
||||
::
|
||||
Finally, this defines that the translation layer must be on the Intel Architecture. At the moment, this acts as a filter,
|
||||
failing to be satisfied by memory images that do not match the architecture required.
|
||||
|
||||
requirements.PluginRequirement(name = 'pslist',
|
||||
plugin = pslist.PsList,
|
||||
version = (1, 0, 0)),
|
||||
Most plugins will only operate on a single layer, but it is entirely possible for a plugin to request two different
|
||||
layers, for example a plugin that carries out some form of difference or statistics against multiple memory images.
|
||||
|
||||
This requirement indicates that the plugin will make use of another plugin's code, and specifies the version requirements
|
||||
on that plugin. The version is specified in terms of Semantic Versioning, meaning that to be compatible, the major
|
||||
versions must be identical and the minor version must be equal to or higher than the one provided. This requirement
|
||||
does not make use of any data from the configuration, even if it were provided, it is merely a functional check before
|
||||
running the plugin.
|
||||
This requirement (and the next two) are known as Complex Requirements, and user interfaces will likely not directly
|
||||
request a value for this from a user. The value stored in the configuration tree for a
|
||||
:py:class:`~volatility3.framework.configuration.requirements.TranslationLayerRequirement` is
|
||||
the string name of a layer present in the context's memory that satisfies the requirement.
|
||||
|
||||
::
|
||||
|
||||
requirements.SymbolTableRequirement(name = "nt_symbols",
|
||||
description = "Windows kernel symbols"),
|
||||
|
||||
This requirement specifies the need for a particular
|
||||
:py:class:`SymbolTable <volatility3.framework.interfaces.symbols.SymbolTableInterface>`
|
||||
to be loaded. This gets populated by various
|
||||
:py:class:`Automagic <volatility3.framework.interfaces.automagic.AutoMagicInterface>` as the nearest sibling to a particular
|
||||
:py:class:`~volatility3.framework.configuration.requirements.TranslationLayerRequirement`.
|
||||
This means that if the :py:class:`~volatility3.framework.configuration.requirements.TranslationLayerRequirement`
|
||||
is satisfied and the :py:class:`Automagic <volatility3.framework.interfaces.automagic.AutoMagicInterface>` can determine
|
||||
the appropriate :py:class:`SymbolTable <volatility3.framework.interfaces.symbols.SymbolTableInterface>`, the
|
||||
name of the :py:class:`SymbolTable <volatility3.framework.interfaces.symbols.SymbolTableInterface>` will be stored in the configuration.
|
||||
|
||||
This requirement is also a Complex Requirement and therefore will not be requested directly from the user.
|
||||
|
||||
::
|
||||
|
||||
requirements.ListRequirement(name = 'pid',
|
||||
description = 'Filter on specific process IDs',
|
||||
element_type = int,
|
||||
optional = True)
|
||||
optional = True),
|
||||
|
||||
The final requirement is a List Requirement, populated by integers. The description will be presented to the user to
|
||||
The next requirement is a List Requirement, populated by integers. The description will be presented to the user to
|
||||
describe what the value represents. The optional flag indicates that the plugin can function without the ``pid`` value
|
||||
being defined within the configuration tree at all.
|
||||
|
||||
::
|
||||
|
||||
requirements.PluginRequirement(name = 'pslist',
|
||||
plugin = pslist.PsList,
|
||||
version = (2, 0, 0))]
|
||||
|
||||
This requirement indicates that the plugin will make use of another plugin's code, and specifies the version requirements
|
||||
on that plugin. The version is specified in terms of Semantic Versioning meaning that, to be compatible, the major
|
||||
versions must be identical and the minor version must be equal to or higher than the one provided. This requirement
|
||||
does not make use of any data from the configuration, even if it were provided, it is merely a functional check before
|
||||
running the plugin. To define the version of a plugin, populate the `_version` class variable as a tuple of version
|
||||
numbers `(major, minor, patch)`. So for example:
|
||||
|
||||
::
|
||||
|
||||
_version = (1, 0, 0)
|
||||
|
||||
The plugin may also require a specific version of the framework, and this also uses Semantic Versioning, and can be
|
||||
set by defining the `_required_framework_version`. The major version should match the version of volatility the plugin
|
||||
is to be used with, which at the time of writing would be 2.2.0, and so would be specified as below. If only features, for example,
|
||||
from 2.0.0 are used, then the lowest applicable version number should be used to support the greatest number of
|
||||
installations:
|
||||
|
||||
::
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
Define the `run` method
|
||||
-----------------------
|
||||
|
||||
@@ -129,6 +178,7 @@ that will be output as part of the :py:class:`~volatility3.framework.interfaces.
|
||||
def run(self):
|
||||
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
|
||||
kernel = self.context.modules[self.config['kernel']]
|
||||
|
||||
return renderers.TreeGrid([("PID", int),
|
||||
("Process", str),
|
||||
@@ -137,8 +187,8 @@ that will be output as part of the :py:class:`~volatility3.framework.interfaces.
|
||||
("Name", str),
|
||||
("Path", str)],
|
||||
self._generator(pslist.PsList.list_processes(self.context,
|
||||
self.config['primary'],
|
||||
self.config['nt_symbols'],
|
||||
kernel.layer_name,
|
||||
kernel.symbol_table_name,
|
||||
filter_func = filter_func)))
|
||||
|
||||
In this instance, the plugin constructs a filter (using the PsList plugin's *classmethod* for creating filters).
|
||||
@@ -157,7 +207,8 @@ the :py:class:`~volatility3.plugins.windows.pslist.PsList` plugin. That plugin
|
||||
so that other plugins can call it. As such, it takes all the necessary parameters rather than accessing them
|
||||
from a configuration. Since it must be portable code, it takes a context, as well as the layer name,
|
||||
symbol table and optionally a filter. In this instance we unconditionally
|
||||
pass it the values from the configuration for the ``primary`` and ``nt_symbols`` requirements. This will generate a list
|
||||
pass it the values from the configuration for the layer and symbol table from the kernel module object, constructed from
|
||||
the ``kernel`` configuration requirement. This will generate a list
|
||||
of :py:class:`~volatility3.framework.symbols.windows.extensions.EPROCESS` objects, as provided by the :py:class:`~volatility.plugins.windows.pslist.PsList` plugin,
|
||||
and is not covered here but is used as an example for how to share code across plugins
|
||||
(both as the provider and the consumer of the shared code).
|
||||
|
||||
+23
-1
@@ -9,7 +9,11 @@ Synopsis
|
||||
**volatility** [-h] [-c CONFIG] [--parallelism [{processes,threads,off}]]
|
||||
[-e EXTEND] [-p PLUGIN_DIRS] [-s SYMBOL_DIRS] [-v] [-l LOG]
|
||||
[-o OUTPUT_DIR] [-q] [-r RENDERER] [-f FILE]
|
||||
[--write-config] [--single-location SINGLE_LOCATION]
|
||||
[--write-config] [--save-config SAVE_CONFIG]
|
||||
[--clear-cache] [--cache-path CACHE_PATH]
|
||||
[--offline]
|
||||
[--single-location SINGLE_LOCATION]
|
||||
[--stackers [STACKERS ...]]
|
||||
[--single-swap-locations SINGLE_SWAP_LOCATIONS]
|
||||
<plugin> ...
|
||||
|
||||
@@ -98,6 +102,10 @@ Options
|
||||
attempt to build upon, and can be considered the input for the program.
|
||||
|
||||
--write-config
|
||||
*Deprecated*
|
||||
Use of `--write-config` has been deprecated, replaced by `--save-config`
|
||||
|
||||
--save-config
|
||||
This flag specifies that volatility should write or overwrite a file
|
||||
called config.json in the current directory. The file will contain
|
||||
the necessary JSON configuration to recreate the environment that the
|
||||
@@ -105,11 +113,25 @@ Options
|
||||
other plugins, but there's no guarantee that plugins use the same
|
||||
configuration options.
|
||||
|
||||
--clear-cache
|
||||
Clears out all short-term cached items.
|
||||
|
||||
--cache-path
|
||||
Change the default path used to store the cache.
|
||||
|
||||
--offline
|
||||
Do not search online for additional JSON files.
|
||||
Run offline mode (defaults to false) and for
|
||||
remote windows symbol tables, linux/mac banner repositories.
|
||||
|
||||
--single-location SINGLE_LOCATION
|
||||
This specifies a URL which will be downloaded if necessary, and built
|
||||
upon by the automagic and, since most plugins require a single memory
|
||||
image, can be considered the input for the program.
|
||||
|
||||
--stackers STACKERS
|
||||
Creates the list of stackers to use based on the config option.
|
||||
|
||||
--single-swap-locations SINGLE_SWAP_LOCATIONS
|
||||
A comma-separated list of swap files to be considered as part of the
|
||||
memory image specified by the single-location or file parameters.
|
||||
|
||||
@@ -26,7 +26,7 @@ except ImportError:
|
||||
|
||||
# Volatility must be findable in sys.path in order for collect_submodules to work
|
||||
# This adds the current working directory, which should usually do the trick
|
||||
sys.path.append(os.getcwd())
|
||||
sys.path.append(os.path.dirname(os.path.abspath(SPEC)))
|
||||
|
||||
vol_analysis = Analysis(['vol.py'],
|
||||
pathex = [],
|
||||
|
||||
@@ -37,9 +37,9 @@ class WarningFindSpec(abc.MetaPathFinder):
|
||||
first."""
|
||||
if fullname.startswith("volatility3.framework.plugins."):
|
||||
warning = "Please do not use the volatility3.framework.plugins namespace directly, only use volatility3.plugins"
|
||||
# Pyinstaller uses walk_packages to import, but needs to read the modules to figure out dependencies
|
||||
# As such, we only print the warning when directly imported rather than from within walk_packages
|
||||
if inspect.stack()[-2].function != 'walk_packages':
|
||||
# Pyinstaller uses walk_packages/_collect_submodules to import, but needs to read the modules to figure out dependencies
|
||||
# As such, we only print the warning when directly imported rather than from within walk_packages/_collect_submodules
|
||||
if inspect.stack()[-2].function in ['walk_packages', '_collect_submodules']:
|
||||
raise Warning(warning)
|
||||
|
||||
|
||||
|
||||
@@ -257,7 +257,6 @@ class VolShell(cli.CommandLine):
|
||||
constructed.run()
|
||||
except exceptions.VolatilityException as excp:
|
||||
self.process_exceptions(excp)
|
||||
parser.exit(1, f"Unable to validate the plugin requirements: {[x for x in excp.unsatisfied]}\n")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -56,13 +56,13 @@ class Volshell(generic.Volshell):
|
||||
"""Display Type describes the members of a particular object in alphabetical order"""
|
||||
if isinstance(object, str):
|
||||
if constants.BANG not in object:
|
||||
object = self.config['vmlinux'] + constants.BANG + object
|
||||
object = self.current_symbol_table + constants.BANG + object
|
||||
return super().display_type(object, offset)
|
||||
|
||||
def display_symbols(self, symbol_table: str = None):
|
||||
"""Prints an alphabetical list of symbols for a symbol table"""
|
||||
if symbol_table is None:
|
||||
symbol_table = self.config['vmlinux']
|
||||
symbol_table = self.current_symbol_table
|
||||
return super().display_symbols(symbol_table)
|
||||
|
||||
@property
|
||||
|
||||
@@ -56,7 +56,7 @@ class Volshell(generic.Volshell):
|
||||
"""Display Type describes the members of a particular object in alphabetical order"""
|
||||
if isinstance(object, str):
|
||||
if constants.BANG not in object:
|
||||
object = self.config['darwin'] + constants.BANG + object
|
||||
object = self.current_symbol_table + constants.BANG + object
|
||||
return super().display_type(object, offset)
|
||||
|
||||
def display_symbols(self, symbol_table: str = None):
|
||||
|
||||
@@ -148,7 +148,7 @@ class KernelPDBScanner(interfaces.automagic.AutomagicInterface):
|
||||
|
||||
vollog.debug("Kernel base determination - optimized scan virtual layer")
|
||||
valid_kernel = self._method_layer_pdb_scan(context, vlayer, test_virtual_kernel, True, False, progress_callback)
|
||||
if valid_kernel != None:
|
||||
if valid_kernel is not None:
|
||||
return valid_kernel
|
||||
|
||||
vollog.debug("Kernel base determination - slow scan virtual layer")
|
||||
|
||||
@@ -523,7 +523,7 @@ class ConstructableRequirementInterface(RequirementInterface):
|
||||
must happen after the class configuration value has been provided).
|
||||
These values are then provided to the object's constructor by name
|
||||
as arguments (as well as the standard `context` and `config_path`
|
||||
arguments.
|
||||
arguments).
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
|
||||
@@ -307,7 +307,7 @@ class DataLayerInterface(interfaces.configuration.ConfigurableInterface, metacla
|
||||
while length > 0:
|
||||
chunk_size = min(length, scanner.chunk_size + scanner.overlap)
|
||||
yield [(layer_name, mapped_offset, chunk_size)], offset + chunk_size
|
||||
# It we've got more than the scanner's chunk_size, only move up by the chunk_size
|
||||
# If we've got more than the scanner's chunk_size, only move up by the chunk_size
|
||||
if chunk_size > scanner.chunk_size:
|
||||
chunk_size -= scanner.overlap
|
||||
length -= chunk_size
|
||||
@@ -517,7 +517,7 @@ class TranslationLayerInterface(DataLayerInterface, metaclass = ABCMeta):
|
||||
yield output, chunk_position
|
||||
output = []
|
||||
chunk_position = chunk_start
|
||||
# Take from chunk_position as far as far as the block can go,
|
||||
# Take from chunk_position as far as the block can go,
|
||||
# or as much left of a scanner chunk as we can
|
||||
chunk_size = min(block_end - chunk_position,
|
||||
scanner.chunk_size + scanner.overlap - (chunk_position - chunk_start))
|
||||
|
||||
@@ -74,7 +74,7 @@ class Kevents(interfaces.plugins.PluginInterface):
|
||||
@classmethod
|
||||
def _walk_klist_array(cls, kernel, fdp, array_pointer_member, array_size_member):
|
||||
"""
|
||||
Convience wrapper for walking an array of lists of kernel events
|
||||
Convenience wrapper for walking an array of lists of kernel events
|
||||
Handles invalid address references
|
||||
"""
|
||||
try:
|
||||
|
||||
@@ -71,14 +71,14 @@ class LdrModules(interfaces.plugins.PluginInterface):
|
||||
mem_mod = mem_order_mod.get(base, None)
|
||||
|
||||
yield (0, [int(proc.UniqueProcessId),
|
||||
str(proc.ImageFileName.cast("string",
|
||||
str(proc.ImageFileName.cast("string",
|
||||
max_length = proc.ImageFileName.vol.count,
|
||||
errors = 'replace')),
|
||||
format_hints.Hex(base),
|
||||
load_mod != None,
|
||||
init_mod != None,
|
||||
mem_mod != None,
|
||||
mapped_files[base]])
|
||||
format_hints.Hex(base),
|
||||
load_mod is not None,
|
||||
init_mod is not None,
|
||||
mem_mod is not None,
|
||||
mapped_files[base]])
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
|
||||
|
||||
@@ -25,7 +25,7 @@ class ModScan(interfaces.plugins.PluginInterface):
|
||||
return [
|
||||
requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.VersionRequirement(name = 'poolerscanner',
|
||||
requirements.VersionRequirement(name = 'poolscanner',
|
||||
component = poolscanner.PoolScanner,
|
||||
version = (1, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (2, 0, 0)),
|
||||
|
||||
@@ -132,7 +132,7 @@ class VadInfo(interfaces.plugins.PluginInterface):
|
||||
vollog.debug("Unable to find the starting/ending VPN member")
|
||||
return None
|
||||
|
||||
if maxsize > 0 and (vad_end - vad_start) > maxsize:
|
||||
if 0 < maxsize < (vad_end - vad_start):
|
||||
vollog.debug(f"Skip VAD dump {vad_start:#x}-{vad_end:#x} due to maxsize limit")
|
||||
return None
|
||||
|
||||
|
||||
@@ -409,7 +409,7 @@ class vm_area_struct(objects.StructType):
|
||||
fname = linux.LinuxUtilities.path_for_file(context, task, self.vm_file)
|
||||
elif self.vm_start <= task.mm.start_brk and self.vm_end >= task.mm.brk:
|
||||
fname = "[heap]"
|
||||
elif self.vm_start <= task.mm.start_stack and self.vm_end >= task.mm.start_stack:
|
||||
elif self.vm_start <= task.mm.start_stack <= self.vm_end:
|
||||
fname = "[stack]"
|
||||
elif self.vm_mm.context.has_member("vdso") and self.vm_start == self.vm_mm.context.vdso:
|
||||
fname = "[vdso]"
|
||||
|
||||
@@ -10,10 +10,10 @@ import os
|
||||
import re
|
||||
import struct
|
||||
from typing import Any, Dict, Generator, List, Optional, Tuple, Union
|
||||
from urllib import request, parse
|
||||
from urllib import parse, request
|
||||
|
||||
from volatility3 import symbols
|
||||
from volatility3.framework import constants, interfaces, exceptions
|
||||
from volatility3.framework import constants, contexts, exceptions, interfaces
|
||||
from volatility3.framework.configuration.requirements import SymbolTableRequirement
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows import pdbconv
|
||||
@@ -24,7 +24,7 @@ vollog = logging.getLogger(__name__)
|
||||
class PDBUtility(interfaces.configuration.VersionableInterface):
|
||||
"""Class to handle and manage all getting symbols based on MZ header"""
|
||||
|
||||
_version = (1, 0, 0)
|
||||
_version = (1, 0, 1)
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
@@ -131,14 +131,14 @@ class PDBUtility(interfaces.configuration.VersionableInterface):
|
||||
# Check it is actually the MZ header
|
||||
if mz_sig != b"MZ":
|
||||
return None
|
||||
|
||||
|
||||
nt_header_start, = struct.unpack("<I", layer.read(offset + 0x3C, 4))
|
||||
pe_sig = layer.read(offset + nt_header_start, 2)
|
||||
|
||||
|
||||
# Check it is actually the Nt Headers
|
||||
if pe_sig != b"PE":
|
||||
return None
|
||||
|
||||
|
||||
optional_header_size, = struct.unpack('<H', layer.read(offset + nt_header_start + 0x14, 2))
|
||||
# Just enough to tell us the max size
|
||||
pe_header = layer.read(offset, nt_header_start + 0x16 + optional_header_size)
|
||||
@@ -146,7 +146,7 @@ class PDBUtility(interfaces.configuration.VersionableInterface):
|
||||
max_size = pe_data.OPTIONAL_HEADER.SizeOfImage
|
||||
|
||||
# Proper data
|
||||
virtual_data = layer.read(offset, max_size, pad=True)
|
||||
virtual_data = layer.read(offset, max_size, pad = True)
|
||||
pe_data = pefile.PE(data = virtual_data)
|
||||
|
||||
# De-virtualize the memory
|
||||
@@ -291,7 +291,7 @@ class PDBUtility(interfaces.configuration.VersionableInterface):
|
||||
|
||||
@classmethod
|
||||
def symbol_table_from_pdb(cls, context: interfaces.context.ContextInterface, config_path: str, layer_name: str,
|
||||
pdb_name: str, module_offset: int, module_size: int) -> str:
|
||||
pdb_name: str, module_offset: int = None, module_size: int = None) -> str:
|
||||
"""Creates symbol table for a module in the specified layer_name.
|
||||
|
||||
Searches the memory section of the loaded module for its PDB GUID
|
||||
@@ -307,6 +307,19 @@ class PDBUtility(interfaces.configuration.VersionableInterface):
|
||||
Returns:
|
||||
The name of the constructed and loaded symbol table
|
||||
"""
|
||||
_, symbol_table_name = cls._modtable_from_pdb(context, config_path, layer_name, pdb_name, module_offset,
|
||||
module_size)
|
||||
return symbol_table_name
|
||||
|
||||
@classmethod
|
||||
def _modtable_from_pdb(cls, context: interfaces.context.ContextInterface, config_path: str, layer_name: str,
|
||||
pdb_name: str, module_offset: int = None, module_size: int = None,
|
||||
create_module: bool = False) -> Tuple[Optional[str], Optional[str]]:
|
||||
|
||||
if module_offset is None:
|
||||
module_offset = context.layers[layer_name].minimum_address
|
||||
if module_size is None:
|
||||
module_size = context.layers[layer_name].maximum_address - module_offset
|
||||
|
||||
guids = list(
|
||||
cls.pdbname_scan(context,
|
||||
@@ -323,12 +336,46 @@ class PDBUtility(interfaces.configuration.VersionableInterface):
|
||||
|
||||
vollog.debug(f"Found {guid['pdb_name']}: {guid['GUID']}-{guid['age']}")
|
||||
|
||||
return cls.load_windows_symbol_table(context,
|
||||
guid["GUID"],
|
||||
guid["age"],
|
||||
guid["pdb_name"],
|
||||
"volatility3.framework.symbols.intermed.IntermediateSymbolTable",
|
||||
config_path = config_path)
|
||||
module_name = guid["pdb_name"].strip('.pdb')
|
||||
|
||||
symbol_table_name = cls.load_windows_symbol_table(context,
|
||||
guid["GUID"],
|
||||
guid["age"],
|
||||
guid["pdb_name"],
|
||||
"volatility3.framework.symbols.intermed.IntermediateSymbolTable",
|
||||
config_path = config_path)
|
||||
|
||||
new_module_name = None
|
||||
if create_module:
|
||||
new_module = contexts.Module.create(context, module_name, layer_name, offset = guid['mz_offset'],
|
||||
symbol_table_name = symbol_table_name)
|
||||
new_module_name = new_module.name
|
||||
|
||||
return new_module_name, symbol_table_name
|
||||
|
||||
@classmethod
|
||||
def module_from_pdb(cls, context: interfaces.context.ContextInterface, config_path: str, layer_name: str,
|
||||
pdb_name: str, module_offset: int = None, module_size: int = None) -> str:
|
||||
"""Creates a module in the specified layer_name based on a pdb name.
|
||||
|
||||
Searches the memory section of the loaded module for its PDB GUID
|
||||
and loads the associated symbol table into the symbol space.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
config_path: The config path where to find symbol files
|
||||
layer_name: The name of the layer on which to operate
|
||||
module_offset: This memory dump's module image offset
|
||||
module_size: The size of the module for this dump
|
||||
|
||||
Returns:
|
||||
The name of the constructed and loaded symbol table
|
||||
"""
|
||||
|
||||
module_name, _ = cls._modtable_from_pdb(context, config_path, layer_name, pdb_name, module_offset,
|
||||
module_size, create_module = True)
|
||||
|
||||
return module_name
|
||||
|
||||
|
||||
class PdbSignatureScanner(interfaces.layers.ScannerInterface):
|
||||
|
||||
Reference in New Issue
Block a user