Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Linux - Boottime support #1317

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
18f7f03
linux: fix datetime import and remove unused ones
gcmoreira Oct 18, 2024
f822468
linux: Implement boot time support in the Volatility 3 core framework
gcmoreira Oct 18, 2024
69512dc
Linux: pslist: Add creation time column and timeline support to the l…
gcmoreira Oct 18, 2024
d25df23
Linux: pslist: Add the boottime plugin
gcmoreira Oct 18, 2024
1651ecb
linux: boottime api: Fix explicit returns mixed with implicit (fall t…
gcmoreira Oct 18, 2024
4895af4
Linux: Boottime timeliner: Rollback timeliner event type changes and …
gcmoreira Oct 29, 2024
c4274c9
Linux: Fix exception message in TimespecVol3::__sub__()
gcmoreira Oct 29, 2024
57ffd5b
Linux: Boottime API: Refactor TimespecVol3::negate() to return a new …
gcmoreira Oct 29, 2024
bb6dc9a
Merge branch 'develop' into linux_boottime_support
gcmoreira Oct 29, 2024
bee2a39
Linux: Minor: Add comment/header on each set of constants
gcmoreira Oct 29, 2024
42d918c
Linux: Boottime API: Refactor Timespec Methods.
gcmoreira Oct 30, 2024
7abe92c
Linux: Boottime API: Minor. Move negate() up
gcmoreira Oct 30, 2024
e197fba
Linux: Boottime API: Refactor __sub__ to operate through __add__() an…
gcmoreira Oct 30, 2024
a05397e
Linux: Boottime API: Minor. Fix docstring typo
gcmoreira Oct 30, 2024
2efb4e7
Merge branch 'develop' into linux_boottime_support
gcmoreira Nov 5, 2024
c0fa2cf
Linux: Boottime API: User linux_constanst import
gcmoreira Nov 5, 2024
f05a169
Merge branch 'develop' into linux_boottime_support
gcmoreira Nov 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions volatility3/framework/constants/linux/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ class ELF_CLASS(IntEnum):
ELFCLASS64 = 2


# PTrace
PT_OPT_FLAG_SHIFT = 3

PTRACE_EVENT_FORK = 1
Expand Down Expand Up @@ -341,6 +342,10 @@ def flags(self) -> str:
return str(self).replace(self.__class__.__name__ + ".", "")


# Boot time
NSEC_PER_SEC = 1e9


# Valid sizes for modules. Note that the Linux kernel does not define these values; they
# are based on empirical observations of typical memory allocations for kernel modules.
# We use this to verify that the found module falls within reasonable limits.
Expand Down
98 changes: 98 additions & 0 deletions volatility3/framework/plugins/linux/boottime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# This file is Copyright 2024 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
import datetime
from typing import List, Tuple, Iterable


from volatility3.framework import interfaces, renderers
from volatility3.framework.configuration import requirements
from volatility3.plugins import timeliner
from volatility3.plugins.linux import pslist


class Boottime(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
"""Shows the time the system was started"""

_required_framework_version = (2, 11, 0)

_version = (1, 0, 0)

@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
return [
requirements.ModuleRequirement(
name="kernel",
description="Linux kernel",
architectures=["Intel32", "Intel64"],
),
requirements.PluginRequirement(
name="pslist", plugin=pslist.PsList, version=(2, 3, 0)
),
]

@classmethod
def get_time_namespaces_bootime(
cls,
context: interfaces.context.ContextInterface,
vmlinux_module_name: str,
) -> Iterable[Tuple[int, int, int, str, datetime.datetime]]:
"""Enumerates tasks' boot times based on their time namespaces.

Args:
context: The context to retrieve required elements (layers, symbol tables) from
vmlinux_module_name: The name of the kernel module on which to operate
pids: Pid list
unique: Filter unique time namespaces

Yields:
A tuple with the fields to show in the plugin output.
"""
time_namespace_ids = set()
for task in pslist.PsList.list_tasks(context, vmlinux_module_name):
time_namespace_id = task.get_time_namespace_id()
# If it cannot get the time namespace i.e. kernels < 5.6, this still works
# using None to just get the first tasks
if time_namespace_id in time_namespace_ids:
continue
time_namespace_ids.add(time_namespace_id)
boottime = task.get_boottime(root_time_namespace=False)

fields = (
time_namespace_id,
boottime,
)
yield fields

def _generator(self):
for (
time_namespace_id,
boottime,
) in self.get_time_namespaces_bootime(
self.context,
self.config["kernel"],
):
fields = [
time_namespace_id or renderers.NotAvailableValue(),
boottime,
]
yield 0, fields

def generate_timeline(self):
for (
time_namespace_id,
boottime,
) in self.get_time_namespaces_bootime(
self.context,
self.config["kernel"],
):
description = f"System boot time for time namespace {time_namespace_id}"

yield description, timeliner.TimeLinerType.CREATED, boottime

def run(self):
columns = [
("TIME NS", int),
("Boot Time", datetime.datetime),
]
return renderers.TreeGrid(columns, self._generator())
31 changes: 26 additions & 5 deletions volatility3/framework/plugins/linux/pslist.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# This file is Copyright 2021 Volatility Foundation and licensed under the Volatility Software License 1.0
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
#
import datetime
from typing import Any, Callable, Iterable, List, Tuple

from volatility3.framework import interfaces, renderers
Expand All @@ -9,15 +10,16 @@
from volatility3.framework.renderers import format_hints
from volatility3.framework.symbols import intermed
from volatility3.framework.symbols.linux.extensions import elf
from volatility3.plugins import timeliner
from volatility3.plugins.linux import elfs


class PsList(interfaces.plugins.PluginInterface):
class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface):
"""Lists the processes present in a particular linux memory image."""

_required_framework_version = (2, 0, 0)

_version = (2, 2, 1)
_version = (2, 3, 0)

@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
Expand Down Expand Up @@ -81,7 +83,7 @@ def filter_func(x):
@classmethod
def get_task_fields(
cls, task: interfaces.objects.ObjectInterface, decorate_comm: bool = False
) -> Tuple[int, int, int, str]:
) -> Tuple[int, int, int, int, str, datetime.datetime]:
"""Extract the fields needed for the final output

Args:
Expand All @@ -96,13 +98,14 @@ def get_task_fields(
tid = task.pid
ppid = task.parent.tgid if task.parent else 0
name = utility.array_to_string(task.comm)
start_time = task.get_create_time()
if decorate_comm:
if task.is_kernel_thread:
name = f"[{name}]"
elif task.is_user_thread:
name = f"{{{name}}}"

task_fields = (task.vol.offset, pid, tid, ppid, name)
task_fields = (task.vol.offset, pid, tid, ppid, name, start_time)
return task_fields

def _get_file_output(self, task: interfaces.objects.ObjectInterface) -> str:
Expand Down Expand Up @@ -177,14 +180,17 @@ def _generator(
else:
file_output = "Disabled"

offset, pid, tid, ppid, name = self.get_task_fields(task, decorate_comm)
offset, pid, tid, ppid, name, creation_time = self.get_task_fields(
task, decorate_comm
)

yield 0, (
format_hints.Hex(offset),
pid,
tid,
ppid,
name,
creation_time or renderers.NotAvailableValue(),
file_output,
)

Expand Down Expand Up @@ -233,8 +239,23 @@ def run(self):
("TID", int),
("PPID", int),
("COMM", str),
("CREATION TIME", datetime.datetime),
("File output", str),
]
return renderers.TreeGrid(
columns, self._generator(filter_func, include_threads, decorate_comm, dump)
)

def generate_timeline(self):
pids = self.config.get("pid")
filter_func = self.create_pid_filter(pids)
for task in self.list_tasks(
self.context, self.config["kernel"], filter_func, include_threads=True
):
offset, user_pid, user_tid, _user_ppid, name, creation_time = (
self.get_task_fields(task)
)

description = f"Process {user_pid}/{user_tid} {name} ({offset})"

yield (description, timeliner.TimeLinerType.CREATED, creation_time)
Loading
Loading