Windows: Add filtering by offset to psscan

Add capability to the psscan plugin to filter by specific offset.
The filter would be used by other plugins to find specific offset using the psscan capabilities, like find
hidden processes as a result of some dkom for example.
The `--offset`  flag argument should represent a physical address space.

The flag is included in the following plugins:
  - dlllist
  - handles
This commit is contained in:
Alejandro Diego
2024-02-19 13:35:31 -05:00
parent 3703e119ce
commit cd08cb95fd
3 changed files with 97 additions and 21 deletions
@@ -13,7 +13,7 @@ from volatility3.framework.renderers import conversion, format_hints
from volatility3.framework.symbols import intermed
from volatility3.framework.symbols.windows.extensions import pe
from volatility3.plugins import timeliner
from volatility3.plugins.windows import info, pslist
from volatility3.plugins.windows import info, pslist, psscan
vollog = logging.getLogger(__name__)
@@ -36,6 +36,9 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
requirements.VersionRequirement(
name="pslist", component=pslist.PsList, version=(2, 0, 0)
),
requirements.VersionRequirement(
name="psscan", component=psscan.PsScan, version=(1, 1, 0)
),
requirements.VersionRequirement(
name="info", component=info.Info, version=(1, 0, 0)
),
@@ -45,6 +48,11 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
description="Process IDs to include (all other processes are excluded)",
optional=True,
),
requirements.IntRequirement(
name="offset",
description="Process offset in the physical address space",
optional=True,
),
requirements.BooleanRequirement(
name="dump",
description="Extract listed DLLs",
@@ -221,6 +229,24 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
kernel = self.context.modules[self.config["kernel"]]
if self.config["offset"]:
procs = psscan.PsScan.scan_processes(
self.context,
kernel.layer_name,
kernel.symbol_table_name,
filter_func=psscan.PsScan.create_offset_filter(
self.context.layers[kernel.layer_name],
self.config["offset"],
),
)
else:
procs = pslist.PsList.list_processes(
context=self.context,
layer_name=kernel.layer_name,
symbol_table=kernel.symbol_table_name,
filter_func=filter_func,
)
return renderers.TreeGrid(
[
("PID", int),
@@ -232,12 +258,5 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
("LoadTime", datetime.datetime),
("File output", str),
],
self._generator(
pslist.PsList.list_processes(
context=self.context,
layer_name=kernel.layer_name,
symbol_table=kernel.symbol_table_name,
filter_func=filter_func,
)
),
self._generator(procs=procs),
)
@@ -9,7 +9,7 @@ from volatility3.framework import constants, exceptions, renderers, interfaces,
from volatility3.framework.configuration import requirements
from volatility3.framework.objects import utility
from volatility3.framework.renderers import format_hints
from volatility3.plugins.windows import pslist
from volatility3.plugins.windows import pslist, psscan
vollog = logging.getLogger(__name__)
@@ -43,14 +43,22 @@ class Handles(interfaces.plugins.PluginInterface):
description="Windows kernel",
architectures=["Intel32", "Intel64"],
),
requirements.PluginRequirement(
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
),
requirements.VersionRequirement(
name="psscan", component=psscan.PsScan, version=(1, 1, 0)
),
requirements.ListRequirement(
name="pid",
element_type=int,
description="Process IDs to include (all other processes are excluded)",
optional=True,
),
requirements.PluginRequirement(
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
requirements.IntRequirement(
name="offset",
description="Process offset in the physical address space",
optional=True,
),
]
@@ -416,6 +424,24 @@ class Handles(interfaces.plugins.PluginInterface):
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
kernel = self.context.modules[self.config["kernel"]]
if self.config["offset"]:
procs = psscan.PsScan.scan_processes(
self.context,
kernel.layer_name,
kernel.symbol_table_name,
filter_func=psscan.PsScan.create_offset_filter(
self.context.layers[kernel.layer_name],
self.config["offset"],
),
)
else:
procs = pslist.PsList.list_processes(
context=self.context,
layer_name=kernel.layer_name,
symbol_table=kernel.symbol_table_name,
filter_func=filter_func,
)
return renderers.TreeGrid(
[
("PID", int),
@@ -426,12 +452,5 @@ class Handles(interfaces.plugins.PluginInterface):
("GrantedAccess", format_hints.Hex),
("Name", str),
],
self._generator(
pslist.PsList.list_processes(
self.context,
kernel.layer_name,
kernel.symbol_table_name,
filter_func=filter_func,
)
),
self._generator(procs=procs),
)
@@ -4,7 +4,7 @@
import datetime
import logging
from typing import Iterable, Callable, Optional, Tuple
from typing import Iterable, Callable, List, Optional, Tuple
from volatility3.framework import renderers, interfaces, layers, exceptions
from volatility3.framework.configuration import requirements
@@ -59,6 +59,44 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
),
]
@classmethod
def create_offset_filter(
cls,
memory: interfaces.layers.DataLayerInterface,
offset: int = None,
exclude: bool = False,
) -> Callable[[interfaces.objects.ObjectInterface], bool]:
"""A factory for producing filter functions that filter based on the physical offset of the process.
Args:
offset: A number that is the physical offset to be filtered out
memory: Memory object needed to do the offset mapping to physical.
exclude: Accept only tasks that are not the offset argument
Returns:
Filter function to be passed to the list of processes.
"""
if not isinstance(memory, interfaces.layers.DataLayerInterface):
raise TypeError("memory object requires an instance of DataLayerInterface")
filter_func = lambda _: False
# return physical offset in tuple -> (_, _, physical_offset, _, _)
# from the first item in the memory mapping list
virtual_to_physical_offset = lambda virtual_offset, memory: list(
memory.mapping(offset=virtual_offset, length=0)
)[0][2]
if offset:
if exclude:
filter_func = (
lambda x: virtual_to_physical_offset(x.vol.offset, memory) == offset
)
else:
filter_func = (
lambda x: virtual_to_physical_offset(x.vol.offset, memory) != offset
)
return filter_func
@classmethod
def scan_processes(
cls,