Merge branch 'volatilityfoundation:develop' into develop

This commit is contained in:
Arcuri Davide
2024-07-25 08:32:01 +02:00
committed by GitHub
3 changed files with 61 additions and 30 deletions
@@ -25,7 +25,7 @@ class Handles(interfaces.plugins.PluginInterface):
"""Lists process open handles."""
_required_framework_version = (2, 0, 0)
_version = (1, 0, 1)
_version = (1, 0, 2)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@@ -142,6 +142,7 @@ class Handles(interfaces.plugins.PluginInterface):
pointers in the _HANDLE_TABLE_ENTRY which allows us to find the
associated _OBJECT_HEADER.
"""
DEFAULT_SAR_VALUE = 0x10 # to be used only when decoding fails
if self._sar_value is None:
if not has_capstone:
@@ -175,10 +176,11 @@ class Handles(interfaces.plugins.PluginInterface):
virtual_layer_name, func_addr_to_read, num_bytes_to_read
)
except exceptions.InvalidAddressException:
vollog.debug(
f"Failed to read {hex(num_bytes_to_read)} bytes at symbol {hex(func_addr_to_read)}"
vollog.warning(
f"Failed to read {hex(num_bytes_to_read)} bytes at symbol {hex(func_addr_to_read)}. Unable to decode SAR value. Failing back to a common value of 0x10"
)
return None
self._sar_value = DEFAULT_SAR_VALUE
return self._sar_value
md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64)
@@ -198,9 +200,10 @@ class Handles(interfaces.plugins.PluginInterface):
break
if self._sar_value is None:
vollog.debug(
f"Failed to to locate SAR value having parsed {instruction_count} instructions"
vollog.warning(
f"Failed to to locate SAR value having parsed {instruction_count} instructions, failing back to a common value of 0x10"
)
self._sar_value = DEFAULT_SAR_VALUE
return self._sar_value
@@ -3,7 +3,7 @@
##
import logging
import datetime
from typing import Iterable
from typing import Callable, Iterable
from volatility3.framework import renderers, interfaces, exceptions
from volatility3.framework.configuration import requirements
@@ -19,7 +19,11 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
# version 2.6.0 adds support for scanning for 'Ethread' structures by pool tags
_required_framework_version = (2, 6, 0)
_version = (1, 0, 0)
_version = (1, 1, 0)
def __init__(self, *args, **kwargs):
self.implementation = self.scan_threads
super().__init__(*args, **kwargs)
@classmethod
def get_requirements(cls):
@@ -38,8 +42,7 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
def scan_threads(
cls,
context: interfaces.context.ContextInterface,
layer_name: str,
symbol_table: str,
module_name: str,
) -> Iterable[interfaces.objects.ObjectInterface]:
"""Scans for threads using the poolscanner module and constraints.
@@ -52,6 +55,10 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
A list of _ETHREAD objects found by scanning memory for the "Thre" / "Thr\\xE5" pool signatures
"""
module = context.modules[module_name]
layer_name = module.layer_name
symbol_table = module.symbol_table_name
constraints = poolscanner.PoolScanner.builtin_constraints(
symbol_table, [b"Thr\xe5", b"Thre"]
)
@@ -76,7 +83,7 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
ethread.get_exit_time()
) # datetime.datetime object / volatility3.framework.renderers.UnparsableValue object
except exceptions.InvalidAddressException:
vollog.debug("Thread invalid address {:#x}".format(thread.vol.offset))
vollog.debug("Thread invalid address {:#x}".format(ethread.vol.offset))
return None
return (
@@ -88,18 +95,19 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
thread_exit_time,
)
def _generator(self):
kernel = self.context.modules[self.config["kernel"]]
def _generator(self, filter_func: Callable):
kernel_name = self.config["kernel"]
for ethread in self.scan_threads(
self.context, kernel.layer_name, kernel.symbol_table_name
):
for ethread in self.implementation(self.context, kernel_name):
info = self.gather_thread_info(ethread)
if info:
yield (0, info)
def generate_timeline(self):
for row in self._generator():
filt_func = self.filter_func(self.config)
for row in self._generator(filt_func):
_depth, row_data = row
row_dict = {}
(
@@ -126,7 +134,14 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
row_dict["ExitTime"],
)
@classmethod
def filter_func(cls, config: interfaces.configuration.HierarchicalDict) -> Callable:
"""Returns a function that can filter this plugin's implementation method based on the config"""
return lambda x: False
def run(self):
filt_func = self.filter_func(self.config)
return renderers.TreeGrid(
[
("Offset", format_hints.Hex),
@@ -136,5 +151,5 @@ class ThrdScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface)
("CreateTime", datetime.datetime),
("ExitTime", datetime.datetime),
],
self._generator(),
self._generator(filt_func),
)
@@ -3,7 +3,7 @@
#
import logging
from typing import List, Generator
from typing import Callable, Iterable, List, Generator
from volatility3.framework import interfaces, constants
from volatility3.framework.configuration import requirements
@@ -18,6 +18,10 @@ class Threads(thrdscan.ThrdScan):
_required_framework_version = (2, 4, 0)
_version = (1, 0, 0)
def __init__(self):
self.implementation = self.list_process_threads
super().__init__()
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
# Since we're calling the plugin, make sure we have the plugin's requirements
@@ -34,7 +38,7 @@ class Threads(thrdscan.ThrdScan):
optional=True,
),
requirements.PluginRequirement(
name="thrdscan", plugin=thrdscan.ThrdScan, version=(1, 0, 0)
name="thrdscan", plugin=thrdscan.ThrdScan, version=(1, 1, 0)
),
]
@@ -60,18 +64,27 @@ class Threads(thrdscan.ThrdScan):
seen.add(thread.vol.offset)
yield thread
def _generator(self):
kernel = self.context.modules[self.config["kernel"]]
@classmethod
def filter_func(cls, config: interfaces.configuration.HierarchicalDict) -> Callable:
return pslist.PsList.create_pid_filter(config.get("pid", None))
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
@classmethod
def list_process_threads(
cls,
context: interfaces.context.ContextInterface,
module_name: str,
filter_func: Callable,
) -> Iterable[interfaces.objects.ObjectInterface]:
"""Runs through all processes and lists threads for each process"""
module = context.modules[module_name]
layer_name = module.layer_name
symbol_table_name = module.symbol_table_name
for proc in pslist.PsList.list_processes(
context=self.context,
layer_name=kernel.layer_name,
symbol_table=kernel.symbol_table_name,
context=context,
layer_name=layer_name,
symbol_table=symbol_table_name,
filter_func=filter_func,
):
for thread in self.list_threads(kernel, proc):
info = self.gather_thread_info(thread)
if info:
yield (0, info)
for thread in cls.list_threads(module, proc):
yield thread