Skip to content

Commit 69512dc

Browse files
committed
Linux: pslist: Add creation time column and timeline support to the linux.pslist plugin
1 parent f822468 commit 69512dc

File tree

1 file changed

+26
-5
lines changed

1 file changed

+26
-5
lines changed

volatility3/framework/plugins/linux/pslist.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +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+
import datetime
45
from typing import Any, Callable, Iterable, List, Tuple
56

67
from volatility3.framework import interfaces, renderers
@@ -9,15 +10,16 @@
910
from volatility3.framework.renderers import format_hints
1011
from volatility3.framework.symbols import intermed
1112
from volatility3.framework.symbols.linux.extensions import elf
13+
from volatility3.plugins import timeliner
1214
from volatility3.plugins.linux import elfs
1315

1416

15-
class PsList(interfaces.plugins.PluginInterface):
17+
class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
1618
"""Lists the processes present in a particular linux memory image."""
1719

1820
_required_framework_version = (2, 0, 0)
1921

20-
_version = (2, 2, 1)
22+
_version = (2, 3, 0)
2123

2224
@classmethod
2325
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -81,7 +83,7 @@ def filter_func(x):
8183
@classmethod
8284
def get_task_fields(
8385
cls, task: interfaces.objects.ObjectInterface, decorate_comm: bool = False
84-
) -> Tuple[int, int, int, str]:
86+
) -> Tuple[int, int, int, int, str, datetime.datetime]:
8587
"""Extract the fields needed for the final output
8688
8789
Args:
@@ -96,13 +98,14 @@ def get_task_fields(
9698
tid = task.pid
9799
ppid = task.parent.tgid if task.parent else 0
98100
name = utility.array_to_string(task.comm)
101+
start_time = task.get_create_time()
99102
if decorate_comm:
100103
if task.is_kernel_thread:
101104
name = f"[{name}]"
102105
elif task.is_user_thread:
103106
name = f"{{{name}}}"
104107

105-
task_fields = (task.vol.offset, pid, tid, ppid, name)
108+
task_fields = (task.vol.offset, pid, tid, ppid, name, start_time)
106109
return task_fields
107110

108111
def _get_file_output(self, task: interfaces.objects.ObjectInterface) -> str:
@@ -177,14 +180,17 @@ def _generator(
177180
else:
178181
file_output = "Disabled"
179182

180-
offset, pid, tid, ppid, name = self.get_task_fields(task, decorate_comm)
183+
offset, pid, tid, ppid, name, creation_time = self.get_task_fields(
184+
task, decorate_comm
185+
)
181186

182187
yield 0, (
183188
format_hints.Hex(offset),
184189
pid,
185190
tid,
186191
ppid,
187192
name,
193+
creation_time or renderers.NotAvailableValue(),
188194
file_output,
189195
)
190196

@@ -233,8 +239,23 @@ def run(self):
233239
("TID", int),
234240
("PPID", int),
235241
("COMM", str),
242+
("CREATION TIME", datetime.datetime),
236243
("File output", str),
237244
]
238245
return renderers.TreeGrid(
239246
columns, self._generator(filter_func, include_threads, decorate_comm, dump)
240247
)
248+
249+
def generate_timeline(self):
250+
pids = self.config.get("pid")
251+
filter_func = self.create_pid_filter(pids)
252+
for task in self.list_tasks(
253+
self.context, self.config["kernel"], filter_func, include_threads=True
254+
):
255+
offset, user_pid, user_tid, _user_ppid, name, creation_time = (
256+
self.get_task_fields(task)
257+
)
258+
259+
description = f"Process {user_pid}/{user_tid} {name} ({offset})"
260+
261+
yield (description, timeliner.TimeLinerType.CREATED, creation_time)

0 commit comments

Comments
 (0)