Skip to content

Commit 3d9b020

Browse files
committed
Linux: change pstree to a basic plugin rather than inheriting from pslist
1 parent 19499d5 commit 3d9b020

File tree

1 file changed

+74
-28
lines changed

1 file changed

+74
-28
lines changed

volatility3/framework/plugins/linux/pstree.py

Lines changed: 74 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,49 @@
22
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
33
#
44

5+
from volatility3.framework import interfaces, renderers
6+
from volatility3.framework.configuration import requirements
7+
from volatility3.framework.renderers import format_hints
58
from volatility3.plugins.linux import pslist
69

710

8-
class PsTree(pslist.PsList):
11+
class PsTree(interfaces.plugins.PluginInterface):
912
"""Plugin for listing processes in a tree based on their parent process
1013
ID."""
1114

12-
def __init__(self, *args, **kwargs):
13-
super().__init__(*args, **kwargs)
14-
self._tasks = {}
15-
self._levels = {}
16-
self._children = {}
15+
_required_framework_version = (2, 0, 0)
16+
17+
@classmethod
18+
def get_requirements(cls):
19+
# Since we're calling the plugin, make sure we have the plugin's requirements
20+
return [
21+
requirements.ModuleRequirement(
22+
name="kernel",
23+
description="Linux kernel",
24+
architectures=["Intel32", "Intel64"],
25+
),
26+
requirements.PluginRequirement(
27+
name="pslist", plugin=pslist.PsList, version=(2, 2, 0)
28+
),
29+
requirements.ListRequirement(
30+
name="pid",
31+
description="Filter on specific process IDs",
32+
element_type=int,
33+
optional=True,
34+
),
35+
requirements.BooleanRequirement(
36+
name="threads",
37+
description="Include user threads",
38+
optional=True,
39+
default=False,
40+
),
41+
requirements.BooleanRequirement(
42+
name="decorate_comm",
43+
description="Show `user threads` comm in curly brackets, and `kernel threads` comm in square brackets",
44+
optional=True,
45+
default=False,
46+
),
47+
]
1748

1849
def find_level(self, pid: int) -> None:
1950
"""Finds how deep the PID is in the tasks hierarchy.
@@ -40,32 +71,26 @@ def find_level(self, pid: int) -> None:
4071

4172
def _generator(
4273
self,
43-
pid_filter,
44-
include_threads: bool = False,
45-
decorate_com: bool = False,
46-
dump: bool = False,
74+
tasks: list,
75+
decorate_comm: bool = False,
4776
):
4877
"""Generates the tasks hierarchy tree.
4978
5079
Args:
51-
pid_filter: A function which takes a process object and returns True if the process should be ignored/filtered
52-
include_threads: If True, the output will also show the user threads
53-
If False, only the thread group leaders will be shown
54-
Defaults to False.
80+
tasks: A list of task objects to be displayed
5581
decorate_comm: If True, it decorates the comm string of
5682
- User threads: in curly brackets,
5783
- Kernel threads: in square brackets
5884
Defaults to False.
5985
Yields:
6086
Each rows
6187
"""
62-
vmlinux = self.context.modules[self.config["kernel"]]
63-
for proc in self.list_tasks(
64-
self.context,
65-
vmlinux.name,
66-
filter_func=pid_filter,
67-
include_threads=include_threads,
68-
):
88+
89+
self._tasks = {}
90+
self._levels = {}
91+
self._children = {}
92+
93+
for proc in tasks:
6994
self._tasks[proc.pid] = proc
7095

7196
# Build the child/level maps
@@ -75,13 +100,10 @@ def _generator(
75100
def yield_processes(pid):
76101
task = self._tasks[pid]
77102

78-
row = self._get_task_fields(task, decorate_com)
79-
if dump:
80-
file_output = self._get_file_output(task)
81-
else:
82-
file_output = "Disabled"
83-
row = self._get_task_fields(task, decorate_com)
84-
row += (file_output,) # also add the file output column
103+
row = pslist.PsList.get_task_fields(task, decorate_comm)
104+
# update the first element, the offset, in the row tuple to use format_hints.Hex
105+
# as a simple int is returned from get_task_fields.
106+
row = (format_hints.Hex(row[0]),) + row[1:]
85107

86108
tid = task.pid
87109
yield (self._levels[tid] - 1, row)
@@ -92,3 +114,27 @@ def yield_processes(pid):
92114
for pid, level in self._levels.items():
93115
if level == 1:
94116
yield from yield_processes(pid)
117+
118+
def run(self):
119+
filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None))
120+
include_threads = self.config.get("threads")
121+
decorate_comm = self.config.get("decorate_comm")
122+
123+
return renderers.TreeGrid(
124+
[
125+
("OFFSET (V)", format_hints.Hex),
126+
("PID", int),
127+
("TID", int),
128+
("PPID", int),
129+
("COMM", str),
130+
],
131+
self._generator(
132+
pslist.PsList.list_tasks(
133+
self.context,
134+
self.config["kernel"],
135+
filter_func=filter_func,
136+
include_threads=include_threads,
137+
),
138+
decorate_comm=decorate_comm,
139+
),
140+
)

0 commit comments

Comments
 (0)