mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-02 14:28:58 +02:00
Merge remote-tracking branch 'upstream/develop' into mountinfo
This commit is contained in:
+11
@@ -27,3 +27,14 @@ config*.json
|
||||
# Pyinstaller files
|
||||
build
|
||||
dist
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
|
||||
# Memory dump files
|
||||
*.dmp
|
||||
*.vmem
|
||||
|
||||
+2
-2
@@ -4,9 +4,9 @@ API Changes
|
||||
When an addition to the existing API is made, the minor version is bumped.
|
||||
When an API feature or function is removed or changed, the major version is bumped.
|
||||
|
||||
2.0.4
|
||||
2.1.0
|
||||
=====
|
||||
Add in the linux `task.get_threads` method added to rhe API.
|
||||
Add in the linux `task.get_threads` method added to the API.
|
||||
|
||||
2.0.3
|
||||
=====
|
||||
|
||||
@@ -63,7 +63,7 @@ To determine the string for a particular memory image, use the `banners` plugin.
|
||||
try to locate that exact kernel debugging package for the operating system. Unfortunately each distribution provides
|
||||
its debugging packages under different package names and there are so many that the distribution may not keep all old
|
||||
versions of the debugging symbols, and therefore **it may not be possible to find the right symbols to analyze a linux
|
||||
memory image with volatlity**. With Macs there are far fewer kernels and only one distribution, making it easier to
|
||||
memory image with volatility**. With Macs there are far fewer kernels and only one distribution, making it easier to
|
||||
ensure that the right symbols can be found.
|
||||
|
||||
Once a kernel with debugging symbols/appropriate DWARF file has been located, `dwarf2json <https://github.com/volatilityfoundation/dwarf2json>`_ will convert it into an
|
||||
|
||||
@@ -110,7 +110,7 @@ This means that pointers do not need to be explicitly dereferenced to access und
|
||||
Running plugins
|
||||
---------------
|
||||
|
||||
It's possible to run any plugin by importing it appropriately and passing it to the `display_plugin_ouptut` or `dpo`
|
||||
It's possible to run any plugin by importing it appropriately and passing it to the `display_plugin_output` or `dpo`
|
||||
method. In the following example we'll provide no additional parameters. Volatility will show us which parameters
|
||||
were required:
|
||||
|
||||
|
||||
@@ -423,7 +423,7 @@ class CommandLine:
|
||||
detail = f"{excp}"
|
||||
caused_by = ["A required python module is not installed (install the module and re-run)"]
|
||||
else:
|
||||
general = "Volatilty encountered an unexpected situation."
|
||||
general = "Volatility encountered an unexpected situation."
|
||||
detail = ""
|
||||
caused_by = [
|
||||
"Please re-run using with -vvv and file a bug with the output", f"at {constants.BUG_URL}"
|
||||
|
||||
@@ -322,10 +322,7 @@ class PrettyTextRenderer(CLIRenderer):
|
||||
tab_width = 8
|
||||
while line.find('\t') >= 0:
|
||||
i = line.find('\t')
|
||||
if (tab_width > 0):
|
||||
pad = " " * (tab_width - (i % tab_width))
|
||||
else:
|
||||
pad = ""
|
||||
pad = " " * (tab_width - (i % tab_width))
|
||||
line = line.replace("\t", pad, 1)
|
||||
return line
|
||||
|
||||
|
||||
@@ -470,14 +470,14 @@ class ModuleRequirement(interfaces.configuration.ConstructableRequirementInterfa
|
||||
if req_unsatisfied:
|
||||
result.update(req_unsatisfied)
|
||||
if not result:
|
||||
vollog.log(constants.LOGLEVEL_V, f"IndexError - No configuration provided: {config_path}")
|
||||
result = {config_path: self}
|
||||
return result
|
||||
|
||||
### NOTE: This validate method has side effects (the dependencies can change)!!!
|
||||
|
||||
self._validate_class(context, interfaces.configuration.parent_path(config_path))
|
||||
vollog.log(constants.LOGLEVEL_V, f"IndexError - No configuration provided: {config_path}")
|
||||
return {config_path: self}
|
||||
|
||||
return result
|
||||
|
||||
def construct(self, context: interfaces.context.ContextInterface, config_path: str) -> None:
|
||||
"""Constructs the appropriate layer and adds it based on the class parameter."""
|
||||
|
||||
@@ -11,3 +11,6 @@ KERNEL_NAME = "__kernel__"
|
||||
# arch/x86/include/asm/page_types.h
|
||||
PAGE_SHIFT = 12
|
||||
"""The value hard coded from the Linux Kernel (hence not extracted from the layer itself)"""
|
||||
|
||||
# include/linux/sched.h
|
||||
PF_KTHREAD = 0x00200000 # I'm a kernel thread
|
||||
|
||||
@@ -115,7 +115,8 @@ class ObjectInterface(metaclass = abc.ABCMeta):
|
||||
mask = context.layers[object_info.layer_name].address_mask
|
||||
normalized_offset = object_info.offset & mask
|
||||
|
||||
self._vol = collections.ChainMap({}, {'type_name': type_name, 'offset': normalized_offset}, object_info, kwargs)
|
||||
vol_info_dict = {'type_name': type_name, 'offset': normalized_offset}
|
||||
self._vol = collections.ChainMap({}, vol_info_dict, object_info, kwargs)
|
||||
self._context = context
|
||||
|
||||
def __getattr__(self, attr: str) -> Any:
|
||||
@@ -156,7 +157,7 @@ class ObjectInterface(metaclass = abc.ABCMeta):
|
||||
"""
|
||||
# TODO: Carefully consider the implications of casting and how it should work
|
||||
if constants.BANG not in new_type_name:
|
||||
symbol_table = self.vol['type_name'].split(constants.BANG)[0]
|
||||
symbol_table = self.get_symbol_table_name()
|
||||
new_type_name = symbol_table + constants.BANG + new_type_name
|
||||
object_template = self._context.symbol_space.get_type(new_type_name)
|
||||
object_template = object_template.clone()
|
||||
|
||||
@@ -167,6 +167,20 @@ class BaseSymbolTableInterface:
|
||||
"""
|
||||
raise NotImplementedError("Abstract method set_type_class not implemented yet.")
|
||||
|
||||
def optional_set_type_class(self, name: str, clazz: Type[objects.ObjectInterface]) -> bool:
|
||||
"""Calls the set_type_class function but does not throw an exception.
|
||||
Returns whether setting the type class was successfull.
|
||||
Args:
|
||||
name: The name of the type to override the class for
|
||||
clazz: The actual class to override for the provided type name
|
||||
"""
|
||||
try:
|
||||
self.set_type_class(name, clazz)
|
||||
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
def get_type_class(self, name: str) -> Type[objects.ObjectInterface]:
|
||||
"""Returns the class associated with a Symbol type."""
|
||||
raise NotImplementedError("Abstract method get_type_class not implemented yet.")
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#
|
||||
import functools
|
||||
import json
|
||||
import math
|
||||
from typing import Optional, Dict, Any, Tuple, List, Set
|
||||
|
||||
from volatility3.framework import interfaces, exceptions, constants
|
||||
@@ -131,8 +130,6 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer):
|
||||
index = 8
|
||||
section_info = dict()
|
||||
current_section_id = -1
|
||||
version_id = -1
|
||||
name = None
|
||||
while section_byte != self.QEVM_EOF and index <= base_layer.maximum_address:
|
||||
section_byte = self.context.object(self._qemu_table_name + constants.BANG + 'unsigned char',
|
||||
offset = index,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# This file is Copyright 2021 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 Callable, Iterable, List, Any
|
||||
from typing import Callable, Iterable, List, Any, Tuple
|
||||
|
||||
from volatility3.framework import renderers, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
|
||||
|
||||
class PsList(interfaces.plugins.PluginInterface):
|
||||
@@ -13,7 +14,7 @@ class PsList(interfaces.plugins.PluginInterface):
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
_version = (2, 0, 0)
|
||||
_version = (2, 1, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
@@ -23,7 +24,15 @@ class PsList(interfaces.plugins.PluginInterface):
|
||||
requirements.ListRequirement(name = 'pid',
|
||||
description = 'Filter on specific process IDs',
|
||||
element_type = int,
|
||||
optional = True)
|
||||
optional = True),
|
||||
requirements.BooleanRequirement(name="threads",
|
||||
description="Include user threads",
|
||||
optional=True,
|
||||
default=False),
|
||||
requirements.BooleanRequirement(name="decorate_comm",
|
||||
description="Show `user threads` comm in curly brackets, and `kernel threads` comm in square brackets",
|
||||
optional=True,
|
||||
default=False),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@@ -48,31 +57,76 @@ class PsList(interfaces.plugins.PluginInterface):
|
||||
else:
|
||||
return lambda _: False
|
||||
|
||||
def _generator(self):
|
||||
def _get_task_fields(
|
||||
self,
|
||||
task: interfaces.objects.ObjectInterface,
|
||||
decorate_comm: bool = False) -> Tuple[int, int, int, str]:
|
||||
"""Extract the fields needed for the final output
|
||||
|
||||
Args:
|
||||
task: A task object from where to get the fields.
|
||||
decorate_comm: If True, it decorates the comm string of
|
||||
- User threads: in curly brackets,
|
||||
- Kernel threads: in square brackets
|
||||
Defaults to False.
|
||||
Returns:
|
||||
A tuple with the fields to show in the plugin output.
|
||||
"""
|
||||
pid = task.tgid
|
||||
tid = task.pid
|
||||
ppid = task.parent.tgid if task.parent else 0
|
||||
name = utility.array_to_string(task.comm)
|
||||
if decorate_comm:
|
||||
if task.is_kernel_thread:
|
||||
name = f"[{name}]"
|
||||
elif task.is_user_thread:
|
||||
name = f"{{{name}}}"
|
||||
|
||||
task_fields = (format_hints.Hex(task.vol.offset), pid, tid, ppid, name)
|
||||
return task_fields
|
||||
|
||||
def _generator(
|
||||
self,
|
||||
pid_filter: Callable[[Any], bool],
|
||||
include_threads: bool = False,
|
||||
decorate_comm: bool = False):
|
||||
"""Generates the tasks list.
|
||||
|
||||
Args:
|
||||
pid_filter: A function which takes a process object and returns True if the process should be ignored/filtered
|
||||
include_threads: If True, the output will also show the user threads
|
||||
If False, only the thread group leaders will be shown
|
||||
Defaults to False.
|
||||
decorate_comm: If True, it decorates the comm string of
|
||||
- User threads: in curly brackets,
|
||||
- Kernel threads: in square brackets
|
||||
Defaults to False.
|
||||
Yields:
|
||||
Each rows
|
||||
"""
|
||||
for task in self.list_tasks(self.context,
|
||||
self.config['kernel'],
|
||||
filter_func = self.create_pid_filter(self.config.get('pid', None))):
|
||||
pid = task.pid
|
||||
ppid = 0
|
||||
if task.parent:
|
||||
ppid = task.parent.pid
|
||||
name = utility.array_to_string(task.comm)
|
||||
yield (0, (pid, ppid, name))
|
||||
pid_filter,
|
||||
include_threads):
|
||||
row = self._get_task_fields(task, decorate_comm)
|
||||
yield (0, row)
|
||||
|
||||
@classmethod
|
||||
def list_tasks(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
vmlinux_module_name: str,
|
||||
filter_func: Callable[[int], bool] = lambda _: False) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
filter_func: Callable[[int], bool] = lambda _: False,
|
||||
include_threads: bool = False) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Lists all the tasks in the primary layer.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
vmlinux_module_name: The name of the kernel module on which to operate
|
||||
|
||||
filter_func: A function which takes a process object and returns True if the process should be ignored/filtered
|
||||
include_threads: If True, it will also return user threads.
|
||||
Yields:
|
||||
Process objects
|
||||
Task objects
|
||||
"""
|
||||
vmlinux = context.modules[vmlinux_module_name]
|
||||
|
||||
@@ -80,8 +134,19 @@ class PsList(interfaces.plugins.PluginInterface):
|
||||
|
||||
# Note that the init_task itself is not yielded, since "ps" also never shows it.
|
||||
for task in init_task.tasks:
|
||||
if not filter_func(task):
|
||||
yield task
|
||||
if filter_func(task):
|
||||
continue
|
||||
|
||||
yield task
|
||||
|
||||
if include_threads:
|
||||
yield from task.get_threads()
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str)], self._generator())
|
||||
pids = self.config.get('pid')
|
||||
include_threads = self.config.get('threads')
|
||||
decorate_comm = self.config.get('decorate_comm')
|
||||
filter_func = self.create_pid_filter(pids)
|
||||
|
||||
columns = [("OFFSET (V)", format_hints.Hex), ("PID", int), ("TID", int), ("PPID", int), ("COMM", str)]
|
||||
return renderers.TreeGrid(columns, self._generator(filter_func, include_threads, decorate_comm))
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# This file is Copyright 2021 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.plugins.linux import pslist
|
||||
|
||||
|
||||
@@ -12,44 +11,74 @@ class PsTree(pslist.PsList):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._processes = {}
|
||||
self._tasks = {}
|
||||
self._levels = {}
|
||||
self._children = {}
|
||||
|
||||
def find_level(self, pid):
|
||||
"""Finds how deep the pid is in the processes list."""
|
||||
seen = set([])
|
||||
seen.add(pid)
|
||||
level = 0
|
||||
proc = self._processes.get(pid, None)
|
||||
while proc is not None and proc.parent != 0 and proc.parent.pid not in seen:
|
||||
ppid = int(proc.parent.pid)
|
||||
def find_level(self, pid: int) -> None:
|
||||
"""Finds how deep the PID is in the tasks hierarchy.
|
||||
|
||||
child_list = self._children.get(ppid, set([]))
|
||||
Args:
|
||||
pid: PID to find the level in the hierachy
|
||||
"""
|
||||
seen = set([pid])
|
||||
level = 0
|
||||
proc = self._tasks.get(pid)
|
||||
while proc and proc.parent and proc.parent.pid not in seen:
|
||||
if proc.is_thread_group_leader:
|
||||
parent_pid = proc.parent.pid
|
||||
else:
|
||||
parent_pid = proc.tgid
|
||||
|
||||
child_list = self._children.setdefault(parent_pid, set())
|
||||
child_list.add(proc.pid)
|
||||
self._children[ppid] = child_list
|
||||
proc = self._processes.get(ppid, None)
|
||||
|
||||
proc = self._tasks.get(parent_pid)
|
||||
level += 1
|
||||
|
||||
self._levels[pid] = level
|
||||
|
||||
def _generator(self):
|
||||
"""Generates the."""
|
||||
def _generator(
|
||||
self,
|
||||
pid_filter,
|
||||
include_threads: bool = False,
|
||||
decorate_com: bool = False):
|
||||
"""Generates the tasks hierarchy tree.
|
||||
|
||||
Args:
|
||||
pid_filter: A function which takes a process object and returns True if the process should be ignored/filtered
|
||||
include_threads: If True, the output will also show the user threads
|
||||
If False, only the thread group leaders will be shown
|
||||
Defaults to False.
|
||||
decorate_comm: If True, it decorates the comm string of
|
||||
- User threads: in curly brackets,
|
||||
- Kernel threads: in square brackets
|
||||
Defaults to False.
|
||||
Yields:
|
||||
Each rows
|
||||
"""
|
||||
vmlinux = self.context.modules[self.config['kernel']]
|
||||
for proc in self.list_tasks(self.context, vmlinux.name):
|
||||
self._processes[proc.pid] = proc
|
||||
for proc in self.list_tasks(self.context,
|
||||
vmlinux.name,
|
||||
filter_func=pid_filter,
|
||||
include_threads=include_threads):
|
||||
self._tasks[proc.pid] = proc
|
||||
|
||||
# Build the child/level maps
|
||||
for pid in self._processes:
|
||||
for pid in self._tasks:
|
||||
self.find_level(pid)
|
||||
|
||||
def yield_processes(pid):
|
||||
proc = self._processes[pid]
|
||||
row = (proc.pid, proc.parent.pid, utility.array_to_string(proc.comm))
|
||||
task = self._tasks[pid]
|
||||
|
||||
yield (self._levels[pid] - 1, row)
|
||||
for child_pid in self._children.get(pid, []):
|
||||
row = self._get_task_fields(task, decorate_com)
|
||||
|
||||
tid = task.pid
|
||||
yield (self._levels[tid] - 1, row)
|
||||
|
||||
for child_pid in sorted(self._children.get(tid, [])):
|
||||
yield from yield_processes(child_pid)
|
||||
|
||||
for pid in self._levels:
|
||||
if self._levels[pid] == 1:
|
||||
for pid, level in self._levels.items():
|
||||
if level == 1:
|
||||
yield from yield_processes(pid)
|
||||
|
||||
@@ -11,7 +11,6 @@ from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows import versions
|
||||
from volatility3.plugins.windows import ssdt
|
||||
from volatility3.plugins.windows import svcscan
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
@@ -28,7 +27,6 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
requirements.ModuleRequirement(name = 'kernel', description = 'Windows kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.PluginRequirement(name = 'ssdt', plugin = ssdt.SSDT, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'svcscan', plugin = svcscan.SvcScan, version = (1, 0, 0))
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
@@ -111,30 +109,19 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
yield symbol_name, callback.Callback, None
|
||||
|
||||
@classmethod
|
||||
def list_registry_callbacks(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str,
|
||||
callback_table_name: str) -> Iterable[Tuple[str, int, None]]:
|
||||
"""Lists all registry callbacks.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
callback_table_name: The nae of the table containing the callback symbols
|
||||
|
||||
Yields:
|
||||
A name, location and optional detail string
|
||||
def _list_registry_callbacks_legacy(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str,
|
||||
callback_table_name: str) -> Iterable[Tuple[str, int, None]]:
|
||||
"""
|
||||
Lists all registry callbacks from the old format via the CmpCallBackVector.
|
||||
"""
|
||||
|
||||
kvo = context.layers[layer_name].config['kernel_virtual_offset']
|
||||
ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo)
|
||||
full_type_name = callback_table_name + constants.BANG + "_EX_CALLBACK_ROUTINE_BLOCK"
|
||||
|
||||
try:
|
||||
symbol_offset = ntkrnlmp.get_symbol("CmpCallBackVector").address
|
||||
symbol_count_offset = ntkrnlmp.get_symbol("CmpCallBackCount").address
|
||||
except exceptions.SymbolError:
|
||||
vollog.debug("Cannot find CmpCallBackVector or CmpCallBackCount")
|
||||
return
|
||||
symbol_offset = ntkrnlmp.get_symbol("CmpCallBackVector").address
|
||||
symbol_count_offset = ntkrnlmp.get_symbol("CmpCallBackCount").address
|
||||
|
||||
|
||||
callback_count = ntkrnlmp.object(object_type = "unsigned int", offset = symbol_count_offset)
|
||||
|
||||
@@ -155,6 +142,62 @@ class Callbacks(interfaces.plugins.PluginInterface):
|
||||
if callback.Function != 0:
|
||||
yield "CmRegisterCallback", callback.Function, None
|
||||
|
||||
@classmethod
|
||||
def _list_registry_callbacks_new(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str,
|
||||
callback_table_name: str) -> Iterable[Tuple[str, int, None]]:
|
||||
"""
|
||||
Lists all registry callbacks via the CallbackListHead.
|
||||
"""
|
||||
|
||||
kvo = context.layers[layer_name].config['kernel_virtual_offset']
|
||||
ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo)
|
||||
full_type_name = callback_table_name + constants.BANG + "_CM_CALLBACK_ENTRY"
|
||||
|
||||
symbol_offset = ntkrnlmp.get_symbol("CallbackListHead").address
|
||||
symbol_count_offset = ntkrnlmp.get_symbol("CmpCallBackCount").address
|
||||
|
||||
callback_count = ntkrnlmp.object(object_type = "unsigned int", offset = symbol_count_offset)
|
||||
|
||||
if callback_count == 0:
|
||||
return
|
||||
|
||||
callback_list = ntkrnlmp.object(object_type = "_LIST_ENTRY", offset = symbol_offset)
|
||||
for callback in callback_list.to_list(full_type_name, "Link"):
|
||||
yield "CmRegisterCallbackEx", callback.Function, f"Altitude: {callback.Altitude.String}"
|
||||
|
||||
@classmethod
|
||||
def list_registry_callbacks(cls, context: interfaces.context.ContextInterface, layer_name: str, symbol_table: str,
|
||||
callback_table_name: str) -> Iterable[Tuple[str, int, None]]:
|
||||
"""Lists all registry callbacks.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
symbol_table: The name of the table containing the kernel symbols
|
||||
callback_table_name: The nae of the table containing the callback symbols
|
||||
|
||||
Yields:
|
||||
A name, location and optional detail string
|
||||
"""
|
||||
|
||||
kvo = context.layers[layer_name].config['kernel_virtual_offset']
|
||||
ntkrnlmp = context.module(symbol_table, layer_name = layer_name, offset = kvo)
|
||||
|
||||
if ntkrnlmp.has_symbol("CmpCallBackVector") and ntkrnlmp.has_symbol("CmpCallBackCount"):
|
||||
yield from cls._list_registry_callbacks_legacy(context, layer_name, symbol_table, callback_table_name)
|
||||
elif ntkrnlmp.has_symbol("CallbackListHead") and ntkrnlmp.has_symbol("CmpCallBackCount"):
|
||||
yield from cls._list_registry_callbacks_new(context, layer_name, symbol_table, callback_table_name)
|
||||
else:
|
||||
symbols_to_check = ["CmpCallBackVector", "CmpCallBackCount", "CallbackListHead"]
|
||||
vollog.debug("Failed to get registry callbacks!")
|
||||
for symbol_name in symbols_to_check:
|
||||
symbol_status = "does not exist"
|
||||
if ntkrnlmp.has_symbol(symbol_name):
|
||||
symbol_status = "exists"
|
||||
vollog.debug(f"symbol {symbol_name} {symbol_status}.")
|
||||
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def list_bugcheck_reason_callbacks(cls, context: interfaces.context.ContextInterface, layer_name: str,
|
||||
symbol_table: str, callback_table_name: str) -> Iterable[Tuple[str, int, str]]:
|
||||
|
||||
@@ -65,7 +65,7 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
try:
|
||||
name = dll_entry.FullDllName.get_string()
|
||||
except exceptions.InvalidAddressException:
|
||||
name = 'UnreadbleDLLName'
|
||||
name = 'UnreadableDLLName'
|
||||
|
||||
if layer_name is None:
|
||||
layer_name = dll_entry.vol.layer_name
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from volatility3.framework import interfaces, constants
|
||||
from volatility3.framework import renderers, interfaces, exceptions
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows.extensions import pe
|
||||
|
||||
@@ -267,4 +267,4 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface):
|
||||
while list_start:
|
||||
list_struct = vmlinux.object(object_type = struct_name, offset = list_start.vol.offset)
|
||||
yield list_struct
|
||||
list_start = getattr(list_struct, list_member)
|
||||
list_start = getattr(list_struct, list_member)
|
||||
@@ -201,6 +201,33 @@ class task_struct(generic.GenericIntelProcess):
|
||||
|
||||
yield (start, end - start)
|
||||
|
||||
@property
|
||||
def is_kernel_thread(self) -> bool:
|
||||
"""Checks if this task is a kernel thread.
|
||||
|
||||
Returns:
|
||||
bool: True, if this task is a kernel thread. Otherwise, False.
|
||||
"""
|
||||
return (self.flags & constants.linux.PF_KTHREAD) != 0
|
||||
|
||||
@property
|
||||
def is_thread_group_leader(self) -> bool:
|
||||
"""Checks if this task is a thread group leader.
|
||||
|
||||
Returns:
|
||||
bool: True, if this task is a thread group leader. Otherwise, False.
|
||||
"""
|
||||
return self.tgid == self.pid
|
||||
|
||||
@property
|
||||
def is_user_thread(self) -> bool:
|
||||
"""Checks if this task is a user thread.
|
||||
|
||||
Returns:
|
||||
bool: True, if this task is a user thread. Otherwise, False.
|
||||
"""
|
||||
return not self.is_kernel_thread and self.tgid != self.pid
|
||||
|
||||
def get_threads(self) -> Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Returns a list of the task_struct based on the list_head
|
||||
thread_node structure."""
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
from volatility3.framework.symbols import intermed
|
||||
from volatility3.framework.symbols.windows import extensions
|
||||
from volatility3.framework.symbols.windows.extensions import registry, pool
|
||||
from volatility3.framework.symbols.windows.extensions import registry, pool, pe
|
||||
|
||||
|
||||
class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable):
|
||||
@@ -38,6 +38,11 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable):
|
||||
self.set_type_class('_SHARED_CACHE_MAP', extensions.SHARED_CACHE_MAP)
|
||||
self.set_type_class('_VACB', extensions.VACB)
|
||||
self.set_type_class('_POOL_TRACKER_BIG_PAGES', pool.POOL_TRACKER_BIG_PAGES)
|
||||
self.set_type_class('_IMAGE_DOS_HEADER', pe.IMAGE_DOS_HEADER)
|
||||
|
||||
# Might not necessarily defined in every version of windows
|
||||
self.optional_set_type_class('_IMAGE_NT_HEADERS', pe.IMAGE_NT_HEADERS)
|
||||
self.optional_set_type_class('_IMAGE_NT_HEADERS64', pe.IMAGE_NT_HEADERS)
|
||||
|
||||
# This doesn't exist in very specific versions of windows
|
||||
try:
|
||||
@@ -49,19 +54,11 @@ class WindowsKernelIntermedSymbols(intermed.IntermediateSymbolTable):
|
||||
pass
|
||||
|
||||
# these don't exist in windows XP
|
||||
try:
|
||||
self.set_type_class('_MMADDRESS_NODE', extensions.MMVAD_SHORT)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self.optional_set_type_class('_MMADDRESS_NODE', extensions.MMVAD_SHORT)
|
||||
|
||||
# these were introduced starting in windows 8
|
||||
try:
|
||||
self.set_type_class('_MM_AVL_NODE', extensions.MMVAD_SHORT)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
self.optional_set_type_class('_MM_AVL_NODE', extensions.MMVAD_SHORT)
|
||||
|
||||
# these were introduced starting in windows 7
|
||||
try:
|
||||
self.set_type_class('_RTL_BALANCED_NODE', extensions.MMVAD_SHORT)
|
||||
except ValueError:
|
||||
pass
|
||||
self.optional_set_type_class('_RTL_BALANCED_NODE', extensions.MMVAD_SHORT)
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned char": {
|
||||
"kind": "char",
|
||||
"size": 1,
|
||||
@@ -137,6 +143,43 @@
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 64
|
||||
},
|
||||
"_CM_CALLBACK_ENTRY": {
|
||||
"fields": {
|
||||
"Link": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"Cookie": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
},
|
||||
"offset": 24
|
||||
},
|
||||
"Function": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 40
|
||||
},
|
||||
"Altitude": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
},
|
||||
"offset": 48
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 64
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned long long": {
|
||||
"kind": "int",
|
||||
"size": 8,
|
||||
"signed": false,
|
||||
"endian": "little"
|
||||
},
|
||||
"unsigned char": {
|
||||
"kind": "char",
|
||||
"size": 1,
|
||||
@@ -137,6 +143,43 @@
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 28
|
||||
},
|
||||
"_CM_CALLBACK_ENTRY": {
|
||||
"fields": {
|
||||
"Link": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_LIST_ENTRY"
|
||||
},
|
||||
"offset": 0
|
||||
},
|
||||
"Cookie": {
|
||||
"type": {
|
||||
"kind": "base",
|
||||
"name": "unsigned long long"
|
||||
},
|
||||
"offset": 16
|
||||
},
|
||||
"Function": {
|
||||
"type": {
|
||||
"kind": "pointer",
|
||||
"subtype": {
|
||||
"kind": "base",
|
||||
"name": "void"
|
||||
}
|
||||
},
|
||||
"offset": 28
|
||||
},
|
||||
"Altitude": {
|
||||
"type": {
|
||||
"kind": "struct",
|
||||
"name": "nt_symbols!_UNICODE_STRING"
|
||||
},
|
||||
"offset": 32
|
||||
}
|
||||
},
|
||||
"kind": "struct",
|
||||
"size": 40
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
|
||||
@@ -574,9 +574,9 @@ class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject):
|
||||
proc_layer = self._context.layers[proc_layer_name]
|
||||
if not proc_layer.is_valid(self.Peb):
|
||||
raise exceptions.InvalidAddressException(proc_layer_name, self.Peb,
|
||||
f"Invalid address at {self.Peb:0x}")
|
||||
f"Invalid Peb address at {self.Peb:0x}")
|
||||
|
||||
sym_table = self.vol.type_name.split(constants.BANG)[0]
|
||||
sym_table = self.get_symbol_table_name()
|
||||
peb = self._context.object(f"{sym_table}{constants.BANG}_PEB",
|
||||
layer_name = proc_layer_name,
|
||||
offset = self.Peb)
|
||||
|
||||
Reference in New Issue
Block a user