Removed --all-process and added --mntns.

This commit is contained in:
Gustavo Moreira
2021-12-15 13:48:46 +11:00
parent a1d145c34f
commit fcf9983d51
2 changed files with 55 additions and 52 deletions
@@ -7,7 +7,7 @@ import logging
from collections import namedtuple
from typing import Tuple, List, Iterable, Union
from volatility3.framework import renderers, interfaces, constants
from volatility3.framework import renderers, interfaces
from volatility3.framework.configuration import requirements
from volatility3.framework.interfaces import plugins
from volatility3.plugins.linux import pslist
@@ -18,11 +18,11 @@ MountInfoData = namedtuple("MountInfoData", ("mnt_id", "parent_id", "st_dev", "m
"mnt_opts", "fields", "mnt_type", "devname", "sb_opts"))
class MountInfo(plugins.PluginInterface):
"""Lists mount points in processes mount namespaces"""
"""Lists mount points on processes mount namespaces"""
_required_framework_version = (2, 0, 0)
_version = (2, 0, 0)
_version = (1, 0, 0)
@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
@@ -35,25 +35,19 @@ class MountInfo(plugins.PluginInterface):
description="Filter on specific process IDs.",
element_type=int,
optional=True),
requirements.BooleanRequirement(name="all-processes",
description="Shows information about mount points for each process mount "
"namespace. It could take a while depending on the number of processes "
"running. Note that if this argument is not specified it uses the root "
"mount namespace based on pid 1.",
optional=True,
default=False),
requirements.ListRequirement(name="mntns",
description="Filter results by mount namespace. "
"Otherwise, all of them are shown.",
element_type=int,
optional=True),
requirements.BooleanRequirement(name="mount-format",
description="Shows a brief summary of a process mount points information "
description="Shows a brief summary of the mount points information "
"with similar output format to the older /proc/[pid]/mounts or the "
"user-land command 'mount -l'.",
optional=True,
default=False),
]
def _get_symbol_fullname(self, symbol_basename: str) -> str:
"""Given a short symbol or type name, it returns its full name"""
return self._vmlinux.symbol_table_name + constants.BANG + symbol_basename
@classmethod
def _do_get_path(cls, mnt, fs_root) -> Union[None, str]:
"""It mimics the Linux kernel prepend_path function."""
@@ -139,21 +133,7 @@ class MountInfo(plugins.PluginInterface):
return MountInfoData(mnt_id, parent_id, st_dev, mnt_root_path, path_root, mnt_opts, fields,
mnt_type, devname, sb_opts)
def _get_mnt_namespace_mountpoints(self, mnt_namespace):
mnt_type = self._get_symbol_fullname("mount")
if not self.context.symbol_space.has_type(mnt_type):
# Old kernels ~ 2.6
mnt_type = self._get_symbol_fullname("vfsmount")
for mount in mnt_namespace.list.to_list(mnt_type, "mnt_list"):
yield mount
def _get_tasks_mountpoints(self, pids: Iterable[int]):
self._vmlinux = self.context.modules[self.config['kernel']]
pid_filter = pslist.PsList.create_pid_filter(pids)
tasks = pslist.PsList.list_tasks(self.context, self.config['kernel'], filter_func=pid_filter)
def _get_tasks_mountpoints(self, tasks: Iterable[interfaces.objects.ObjectInterface], per_namespace: bool):
seen_namespaces = set()
for task in tasks:
if not (task and task.fs and task.fs.root and task.nsproxy and task.nsproxy.mnt_ns):
@@ -161,26 +141,33 @@ class MountInfo(plugins.PluginInterface):
continue
mnt_namespace = task.nsproxy.mnt_ns
mount_ns_id = mnt_namespace.get_inode()
mnt_ns_id = mnt_namespace.get_inode()
if self._show_mountpoints_per_namespace:
if mount_ns_id in seen_namespaces:
if per_namespace:
if mnt_ns_id in seen_namespaces:
continue
else:
seen_namespaces.add(mount_ns_id)
seen_namespaces.add(mnt_ns_id)
for mount in self._get_mnt_namespace_mountpoints(mnt_namespace):
yield task, mount, mount_ns_id
for mount in mnt_namespace.get_mount_points():
yield task, mount, mnt_ns_id
def _generator(self):
pids = self.config.get('pids')
def _generator(
self,
tasks: Iterable[interfaces.objects.ObjectInterface],
mnt_ns_ids: List[int],
mount_format: bool,
per_namespace: bool) -> Iterable[Tuple[int, Tuple]]:
for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(tasks, per_namespace):
if mnt_ns_ids and mnt_ns_id not in mnt_ns_ids:
continue
for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(pids=pids):
mnt_info = self.get_mountinfo(mnt, task)
if mnt_info is None:
continue
if self.config.get('mount-format'):
if mount_format:
all_opts = set()
all_opts.update(mnt_info.mnt_opts)
all_opts.update(mnt_info.sb_opts)
@@ -197,22 +184,28 @@ class MountInfo(plugins.PluginInterface):
mnt_info.devname, sb_opts_str]
fields_values = [mnt_ns_id]
if not self._show_mountpoints_per_namespace:
if not per_namespace:
fields_values.append(task.pid)
fields_values.extend(extra_fields_values)
yield (0, fields_values)
def run(self):
if self.config.get('all-processes') and self.config.get('pids'):
raise ValueError("Unable to use --all-processes and specified a pid")
pids = self.config.get('pids')
mount_ns_ids = self.config.get('mntns')
mount_format = self.config.get('mount-format')
# When no arguments are specified, it displays the mountpoints per namespace
self._show_mountpoints_per_namespace = not any([self.config.get('pids'), self.config.get('all-processes')])
pid_filter = pslist.PsList.create_pid_filter(pids)
tasks = pslist.PsList.list_tasks(self.context, self.config['kernel'], filter_func=pid_filter)
columns = [("MNT_NS_ID", int)]
if not self._show_mountpoints_per_namespace:
# The PID column does not make sense when a PID filter is not specified. In that case, the default behavior is
# to displays the mountpoints per namespace.
if pids:
columns.append(("PID", int))
per_namespace = False
else:
per_namespace = True
if self.config.get('mount-format'):
extra_columns = [("DEVNAME", str), ("PATH", str), ("FSTYPE", str), ("MNT_OPTS", str)]
@@ -224,4 +217,4 @@ class MountInfo(plugins.PluginInterface):
columns.extend(extra_columns)
return renderers.TreeGrid(columns, self._generator())
return renderers.TreeGrid(columns, self._generator(tasks, mount_ns_ids, mount_format, per_namespace))
@@ -555,12 +555,12 @@ class mount(objects.StructType):
MNT_UNBINDABLE = 0x2000
MNT_FLAGS = {
MNT_NOSUID: "nosuid",
MNT_NODEV: "nodev",
MNT_NOEXEC: "noexec",
MNT_NOATIME: "noatime",
MNT_NOSUID: "nosuid",
MNT_NODEV: "nodev",
MNT_NOEXEC: "noexec",
MNT_NOATIME: "noatime",
MNT_NODIRATIME: "nodiratime",
MNT_RELATIME: "relatime",
MNT_RELATIME: "relatime",
}
def get_mnt_sb(self):
@@ -703,3 +703,13 @@ class mnt_namespace(objects.StructType):
return self.ns.inum
else:
raise AttributeError("Unable to find mnt_namespace inode")
def get_mount_points(self):
table_name = self.vol.type_name.split(constants.BANG)[0]
mnt_type = table_name + constants.BANG + "mount"
if not self._context.symbol_space.has_type(mnt_type):
# Old kernels ~ 2.6
mnt_type = table_name + constants.BANG + "vfsmount"
for mount in self.list.to_list(mnt_type, "mnt_list"):
yield mount