Skip to content

Commit d25df23

Browse files
committed
Linux: pslist: Add the boottime plugin
1 parent 69512dc commit d25df23

File tree

2 files changed

+109
-2
lines changed

2 files changed

+109
-2
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0
2+
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
3+
#
4+
import datetime
5+
from typing import List, Tuple, Iterable
6+
7+
8+
from volatility3.framework import interfaces, renderers
9+
from volatility3.framework.configuration import requirements
10+
from volatility3.plugins import timeliner
11+
from volatility3.plugins.linux import pslist
12+
13+
14+
class Boottime(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
15+
"""Shows the time the system was started"""
16+
17+
_required_framework_version = (2, 11, 0)
18+
19+
_version = (1, 0, 0)
20+
21+
@classmethod
22+
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
23+
return [
24+
requirements.ModuleRequirement(
25+
name="kernel",
26+
description="Linux kernel",
27+
architectures=["Intel32", "Intel64"],
28+
),
29+
requirements.PluginRequirement(
30+
name="pslist", plugin=pslist.PsList, version=(2, 3, 0)
31+
),
32+
]
33+
34+
@classmethod
35+
def get_time_namespaces_bootime(
36+
cls,
37+
context: interfaces.context.ContextInterface,
38+
vmlinux_module_name: str,
39+
) -> Iterable[Tuple[int, int, int, str, datetime.datetime]]:
40+
"""Enumerates tasks' boot times based on their time namespaces.
41+
42+
Args:
43+
context: The context to retrieve required elements (layers, symbol tables) from
44+
vmlinux_module_name: The name of the kernel module on which to operate
45+
pids: Pid list
46+
unique: Filter unique time namespaces
47+
48+
Yields:
49+
A tuple with the fields to show in the plugin output.
50+
"""
51+
time_namespace_ids = set()
52+
for task in pslist.PsList.list_tasks(context, vmlinux_module_name):
53+
time_namespace_id = task.get_time_namespace_id()
54+
# If it cannot get the time namespace i.e. kernels < 5.6, this still works
55+
# using None to just get the first tasks
56+
if time_namespace_id in time_namespace_ids:
57+
continue
58+
time_namespace_ids.add(time_namespace_id)
59+
boottime = task.get_boottime(root_time_namespace=False)
60+
61+
fields = (
62+
time_namespace_id,
63+
boottime,
64+
)
65+
yield fields
66+
67+
def _generator(self):
68+
for (
69+
time_namespace_id,
70+
boottime,
71+
) in self.get_time_namespaces_bootime(
72+
self.context,
73+
self.config["kernel"],
74+
):
75+
fields = [
76+
time_namespace_id or renderers.NotAvailableValue(),
77+
boottime,
78+
]
79+
yield 0, fields
80+
81+
def generate_timeline(self):
82+
for (
83+
time_namespace_id,
84+
boottime,
85+
) in self.get_time_namespaces_bootime(
86+
self.context,
87+
self.config["kernel"],
88+
):
89+
description = f"System boot time for time namespace {time_namespace_id}"
90+
91+
yield description, timeliner.TimeLinerType.BOOTTIME, boottime
92+
93+
def run(self):
94+
columns = [
95+
("TIME NS", int),
96+
("Boot Time", datetime.datetime),
97+
]
98+
return renderers.TreeGrid(columns, self._generator())

volatility3/framework/plugins/timeliner.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class TimeLinerType(enum.IntEnum):
2323
MODIFIED = 2
2424
ACCESSED = 3
2525
CHANGED = 4
26+
BOOTTIME = 5
2627

2728

2829
class TimeLinerInterface(metaclass=abc.ABCMeta):
@@ -171,18 +172,22 @@ def _generator(
171172
TimeLinerType.CHANGED,
172173
renderers.NotApplicableValue(),
173174
),
175+
times.get(
176+
TimeLinerType.BOOTTIME,
177+
renderers.NotApplicableValue(),
178+
),
174179
],
175180
)
176181
)
177182

178183
# Write each entry because the body file doesn't need to be sorted
179184
if fp:
180185
times = self.timeline[(plugin_name, item)]
181-
# Body format is: MD5|name|inode|mode_as_string|UID|GID|size|atime|mtime|ctime|crtime
186+
# Body format is: MD5|name|inode|mode_as_string|UID|GID|size|atime|mtime|ctime|crtime|boottime
182187

183188
if self._any_time_present(times):
184189
fp.write(
185-
"|{} - {}|0|0|0|0|0|{}|{}|{}|{}\n".format(
190+
"|{} - {}|0|0|0|0|0|{}|{}|{}|{}|{}\n".format(
186191
plugin_name,
187192
self._sanitize_body_format(item),
188193
self._text_format(
@@ -197,6 +202,9 @@ def _generator(
197202
self._text_format(
198203
times.get(TimeLinerType.CREATED, "0")
199204
),
205+
self._text_format(
206+
times.get(TimeLinerType.BOOTTIME, "0")
207+
),
200208
)
201209
)
202210
except Exception as e:
@@ -320,6 +328,7 @@ def run(self):
320328
("Modified Date", datetime.datetime),
321329
("Accessed Date", datetime.datetime),
322330
("Changed Date", datetime.datetime),
331+
("Boot Date", datetime.datetime),
323332
],
324333
generator=self._generator(plugins_to_run),
325334
)

0 commit comments

Comments
 (0)