# This file is Copyright 2022 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
from typing import Iterator, List, Tuple
from volatility3.framework import exceptions, interfaces, renderers
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
vollog = logging.getLogger(__name__)
[docs]class JobLinks(interfaces.plugins.PluginInterface):
"""Print process job link information"""
_required_framework_version = (2, 0, 0)
_version = (1, 0, 0)
[docs] @classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
requirements.ModuleRequirement(name = 'kernel',
description = 'Windows kernel',
architectures = ["Intel32", "Intel64"]),
requirements.BooleanRequirement(name = 'physical',
description = "Display physical offset instead of virtual",
default = False,
optional = True),
requirements.VersionRequirement(name = 'pslist', component = pslist.PsList, version = (2, 0, 0))
]
def _generator(self) -> Iterator[Tuple]:
kernel = self.context.modules[self.config['kernel']]
memory = self.context.layers[kernel.layer_name]
for proc in pslist.PsList.list_processes(self.context, kernel.layer_name, kernel.symbol_table_name):
try:
if not self.config['physical']:
offset = proc.vol.offset
else:
(_, _, offset, _, _) = list(memory.mapping(offset = proc.vol.offset, length = 0))[0]
job = proc.Job.dereference()
yield (0, (format_hints.Hex(offset), utility.array_to_string(proc.ImageFileName), proc.UniqueProcessId,
proc.InheritedFromUniqueProcessId, proc.get_session_id(), job.SessionId, proc.get_is_wow64(),
job.TotalProcesses, job.ActiveProcesses, job.TotalTerminatedProcesses,
renderers.NotApplicableValue(), "(Original Process)"))
for entry in job.ProcessListHead.to_list(proc.vol.type_name, "JobLinks"):
if not self.config['physical']:
offset = entry.vol.offset
else:
(_, _, offset, _, _) = list(memory.mapping(offset = entry.vol.offset, length = 0))[0]
yield (1, (format_hints.Hex(offset), utility.array_to_string(entry.ImageFileName),
entry.UniqueProcessId, entry.InheritedFromUniqueProcessId, entry.get_session_id(), 0,
entry.get_is_wow64(), 0, 0, 0, "Yes",
entry.get_peb().ProcessParameters.ImagePathName.get_string()))
except (exceptions.InvalidAddressException):
continue
[docs] def run(self) -> renderers.TreeGrid:
offsettype = "(V)" if not self.config.get('physical', pslist.PsList.PHYSICAL_DEFAULT) else "(P)"
return renderers.TreeGrid([(f"Offset{offsettype}", format_hints.Hex), ("Name", str),
("PID", int), ("PPID", int), ("Sess", int), ("JobSess", int), ("Wow64", bool),
("Total", int), ("Active", int), ("Term", int), ("JobLink", str), ("Process", str)],
self._generator())