Skip to content

Commit c08dda8

Browse files
committed
Linux: Update pslist plugin so that it can be used from pstree, and update pstree to support dumping of processes
1 parent a08b780 commit c08dda8

File tree

2 files changed

+84
-43
lines changed

2 files changed

+84
-43
lines changed

volatility3/framework/plugins/linux/pslist.py

Lines changed: 73 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This file is Copyright 2021 Volatility Foundation and licensed under the Volatility Software License 1.0
22
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
33
#
4-
from typing import Any, Callable, Iterable, List
4+
from typing import Any, Callable, Iterable, List, Tuple
55

66
from volatility3.framework import interfaces, renderers
77
from volatility3.framework.configuration import requirements
@@ -17,7 +17,7 @@ class PsList(interfaces.plugins.PluginInterface):
1717

1818
_required_framework_version = (2, 0, 0)
1919

20-
_version = (2, 1, 0)
20+
_version = (2, 1, 1)
2121

2222
@classmethod
2323
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -78,6 +78,71 @@ def filter_func(x):
7878
else:
7979
return lambda _: False
8080

81+
def _get_task_fields(
82+
self, task: interfaces.objects.ObjectInterface, decorate_comm: bool = False
83+
) -> Tuple[int, int, int, str]:
84+
"""Extract the fields needed for the final output
85+
Args:
86+
task: A task object from where to get the fields.
87+
decorate_comm: If True, it decorates the comm string of
88+
- User threads: in curly brackets,
89+
- Kernel threads: in square brackets
90+
Defaults to False.
91+
Returns:
92+
A tuple with the fields to show in the plugin output.
93+
"""
94+
pid = task.tgid
95+
tid = task.pid
96+
ppid = task.parent.tgid if task.parent else 0
97+
name = utility.array_to_string(task.comm)
98+
if decorate_comm:
99+
if task.is_kernel_thread:
100+
name = f"[{name}]"
101+
elif task.is_user_thread:
102+
name = f"{{{name}}}"
103+
104+
task_fields = (format_hints.Hex(task.vol.offset), pid, tid, ppid, name)
105+
return task_fields
106+
107+
def _get_file_output(self, task: interfaces.objects.ObjectInterface) -> str:
108+
"""Extract the elf for the process if requested
109+
Args:
110+
task: A task object to extract from.
111+
Returns:
112+
A string showing the results of the extraction, either
113+
the filename used or an error.
114+
"""
115+
elf_table_name = intermed.IntermediateSymbolTable.create(
116+
self.context,
117+
self.config_path,
118+
"linux",
119+
"elf",
120+
class_types=elf.class_types,
121+
)
122+
proc_layer_name = task.add_process_layer()
123+
if not proc_layer_name:
124+
# if we can't build a proc layer we can't
125+
# extract the elf
126+
return renderers.NotApplicableValue()
127+
else:
128+
# Find the vma that belongs to the main ELF of the process
129+
file_output = "Error outputting file"
130+
for v in task.mm.get_mmap_iter():
131+
if v.vm_start == task.mm.start_code:
132+
file_handle = elfs.Elfs.elf_dump(
133+
self.context,
134+
proc_layer_name,
135+
elf_table_name,
136+
v,
137+
task,
138+
self.open,
139+
)
140+
if file_handle:
141+
file_output = str(file_handle.preferred_filename)
142+
file_handle.close()
143+
break
144+
return file_output
145+
81146
def _generator(
82147
self,
83148
pid_filter: Callable[[Any], bool],
@@ -104,49 +169,15 @@ def _generator(
104169
for task in self.list_tasks(
105170
self.context, self.config["kernel"], pid_filter, include_threads
106171
):
107-
elf_table_name = intermed.IntermediateSymbolTable.create(
108-
self.context,
109-
self.config_path,
110-
"linux",
111-
"elf",
112-
class_types=elf.class_types,
113-
)
114-
file_output = "Disabled"
115172
if dump:
116-
proc_layer_name = task.add_process_layer()
117-
if not proc_layer_name:
118-
continue
119-
120-
# Find the vma that belongs to the main ELF of the process
121-
file_output = "Error outputting file"
122-
123-
for v in task.mm.get_mmap_iter():
124-
if v.vm_start == task.mm.start_code:
125-
file_handle = elfs.Elfs.elf_dump(
126-
self.context,
127-
proc_layer_name,
128-
elf_table_name,
129-
v,
130-
task,
131-
self.open,
132-
)
133-
if file_handle:
134-
file_output = str(file_handle.preferred_filename)
135-
file_handle.close()
136-
break
137-
138-
pid = task.tgid
139-
tid = task.pid
140-
ppid = task.parent.tgid if task.parent else 0
141-
name = utility.array_to_string(task.comm)
142-
if decorate_comm:
143-
if task.is_kernel_thread:
144-
name = f"[{name}]"
145-
elif task.is_user_thread:
146-
name = f"{{{name}}}"
173+
file_output = self._get_file_output(task)
174+
else:
175+
file_output = "Disabled"
176+
177+
offset, pid, tid, ppid, name = self._get_task_fields(task, decorate_comm)
147178

148179
yield 0, (
149-
format_hints.Hex(task.vol.offset),
180+
offset,
150181
pid,
151182
tid,
152183
ppid,

volatility3/framework/plugins/linux/pstree.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ def find_level(self, pid: int) -> None:
3939
self._levels[pid] = level
4040

4141
def _generator(
42-
self, pid_filter, include_threads: bool = False, decorate_com: bool = False
42+
self,
43+
pid_filter,
44+
include_threads: bool = False,
45+
decorate_com: bool = False,
46+
dump: bool = False,
4347
):
4448
"""Generates the tasks hierarchy tree.
4549
@@ -72,6 +76,12 @@ def yield_processes(pid):
7276
task = self._tasks[pid]
7377

7478
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
7585

7686
tid = task.pid
7787
yield (self._levels[tid] - 1, row)

0 commit comments

Comments
 (0)