mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-06 17:57:38 +02:00
@@ -66,7 +66,7 @@ class IOMem(interfaces.plugins.PluginInterface):
|
||||
name = utility.pointer_to_string(resource.name, 128)
|
||||
except exceptions.InvalidAddressException:
|
||||
vollog.warning(
|
||||
"Unable to follow pointer to name for resource object at {resource_offset:#x}, "
|
||||
f"Unable to follow pointer to name for resource object at {resource_offset:#x}, "
|
||||
"replaced with UnreadableValue"
|
||||
)
|
||||
name = renderers.UnreadableValue()
|
||||
|
||||
@@ -9,8 +9,10 @@ from typing import Tuple, List, Iterable, Union
|
||||
from volatility3.framework import renderers, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.symbols import linux
|
||||
from volatility3.plugins.linux import pslist
|
||||
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
MountInfoData = namedtuple(
|
||||
@@ -48,6 +50,9 @@ class MountInfo(plugins.PluginInterface):
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 0, 0)
|
||||
),
|
||||
requirements.VersionRequirement(
|
||||
name="linuxutils", component=linux.LinuxUtilities, version=(2, 1, 0)
|
||||
),
|
||||
requirements.ListRequirement(
|
||||
name="pids",
|
||||
description="Filter on specific process IDs.",
|
||||
@@ -71,37 +76,6 @@ class MountInfo(plugins.PluginInterface):
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _do_get_path(cls, mnt, fs_root) -> Union[None, str]:
|
||||
"""It mimics the Linux kernel prepend_path function."""
|
||||
vfsmnt = mnt.mnt
|
||||
dentry = vfsmnt.get_mnt_root()
|
||||
|
||||
path_reversed = []
|
||||
while dentry != fs_root.dentry or vfsmnt.vol.offset != fs_root.mnt:
|
||||
if dentry == vfsmnt.get_mnt_root() or dentry.is_root():
|
||||
parent = mnt.get_mnt_parent().dereference()
|
||||
# Escaped?
|
||||
if dentry != vfsmnt.get_mnt_root():
|
||||
return None
|
||||
|
||||
# Global root?
|
||||
if mnt.vol.offset != parent.vol.offset:
|
||||
dentry = mnt.get_mnt_mountpoint()
|
||||
mnt = parent
|
||||
vfsmnt = mnt.mnt
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
parent = dentry.d_parent
|
||||
dname = dentry.d_name.name_as_str()
|
||||
path_reversed.append(dname.strip("/"))
|
||||
dentry = parent
|
||||
|
||||
path = "/" + "/".join(reversed(path_reversed))
|
||||
return path
|
||||
|
||||
@classmethod
|
||||
def get_mountinfo(
|
||||
cls, mnt, task
|
||||
@@ -115,8 +89,8 @@ class MountInfo(plugins.PluginInterface):
|
||||
if not mnt_root:
|
||||
return None
|
||||
|
||||
path_root = cls._do_get_path(mnt, task.fs.root)
|
||||
if path_root is None:
|
||||
path_root = linux.LinuxUtilities.get_path_mnt(task, mnt)
|
||||
if not path_root:
|
||||
return None
|
||||
|
||||
mnt_root_path = mnt_root.path()
|
||||
@@ -170,9 +144,11 @@ class MountInfo(plugins.PluginInterface):
|
||||
)
|
||||
|
||||
def _get_tasks_mountpoints(
|
||||
self, tasks: Iterable[interfaces.objects.ObjectInterface], per_namespace: bool
|
||||
self,
|
||||
tasks: Iterable[interfaces.objects.ObjectInterface],
|
||||
filtered_by_pids: bool,
|
||||
):
|
||||
seen_namespaces = set()
|
||||
seen_mountpoints = set()
|
||||
for task in tasks:
|
||||
if not (
|
||||
task
|
||||
@@ -181,19 +157,27 @@ class MountInfo(plugins.PluginInterface):
|
||||
and task.nsproxy
|
||||
and task.nsproxy.mnt_ns
|
||||
):
|
||||
# This task doesn't have all the information required
|
||||
# This task doesn't have all the information required.
|
||||
# It should be a kernel < 2.6.30
|
||||
continue
|
||||
|
||||
mnt_namespace = task.nsproxy.mnt_ns
|
||||
mnt_ns_id = mnt_namespace.get_inode()
|
||||
|
||||
if per_namespace:
|
||||
if mnt_ns_id in seen_namespaces:
|
||||
continue
|
||||
else:
|
||||
seen_namespaces.add(mnt_ns_id)
|
||||
try:
|
||||
mnt_ns_id = mnt_namespace.get_inode()
|
||||
except AttributeError:
|
||||
mnt_ns_id = renderers.NotAvailableValue()
|
||||
|
||||
for mount in mnt_namespace.get_mount_points():
|
||||
# When PIDs are filtered, it makes sense that the user want to
|
||||
# see each of those processes mount points. So we don't filter
|
||||
# by mount id in this case.
|
||||
if not filtered_by_pids:
|
||||
mnt_id = int(mount.mnt_id)
|
||||
if mnt_id in seen_mountpoints:
|
||||
continue
|
||||
else:
|
||||
seen_mountpoints.add(mnt_id)
|
||||
|
||||
yield task, mount, mnt_ns_id
|
||||
|
||||
def _generator(
|
||||
@@ -201,10 +185,20 @@ class MountInfo(plugins.PluginInterface):
|
||||
tasks: Iterable[interfaces.objects.ObjectInterface],
|
||||
mnt_ns_ids: List[int],
|
||||
mount_format: bool,
|
||||
per_namespace: bool,
|
||||
filtered_by_pids: 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:
|
||||
show_filter_warning = False
|
||||
for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(
|
||||
tasks, filtered_by_pids
|
||||
):
|
||||
if mnt_ns_ids and isinstance(mnt_ns_id, renderers.NotAvailableValue):
|
||||
show_filter_warning = True
|
||||
|
||||
if (
|
||||
not isinstance(mnt_ns_id, renderers.NotAvailableValue)
|
||||
and mnt_ns_ids
|
||||
and mnt_ns_id not in mnt_ns_ids
|
||||
):
|
||||
continue
|
||||
|
||||
mnt_info = self.get_mountinfo(mnt, task)
|
||||
@@ -242,12 +236,17 @@ class MountInfo(plugins.PluginInterface):
|
||||
]
|
||||
|
||||
fields_values = [mnt_ns_id]
|
||||
if not per_namespace:
|
||||
if filtered_by_pids:
|
||||
fields_values.append(task.pid)
|
||||
fields_values.extend(extra_fields_values)
|
||||
|
||||
yield (0, fields_values)
|
||||
|
||||
if show_filter_warning:
|
||||
vollog.warning(
|
||||
"Could not filter by mount namespace id. This field is not available in this kernel."
|
||||
)
|
||||
|
||||
def run(self):
|
||||
pids = self.config.get("pids")
|
||||
mount_ns_ids = self.config.get("mntns")
|
||||
@@ -263,9 +262,9 @@ class MountInfo(plugins.PluginInterface):
|
||||
# to displays the mountpoints per namespace.
|
||||
if pids:
|
||||
columns.append(("PID", int))
|
||||
per_namespace = False
|
||||
filtered_by_pids = True
|
||||
else:
|
||||
per_namespace = True
|
||||
filtered_by_pids = False
|
||||
|
||||
if self.config.get("mount-format"):
|
||||
extra_columns = [
|
||||
@@ -292,5 +291,6 @@ class MountInfo(plugins.PluginInterface):
|
||||
columns.extend(extra_columns)
|
||||
|
||||
return renderers.TreeGrid(
|
||||
columns, self._generator(tasks, mount_ns_ids, mount_format, per_namespace)
|
||||
columns,
|
||||
self._generator(tasks, mount_ns_ids, mount_format, filtered_by_pids),
|
||||
)
|
||||
|
||||
@@ -28,7 +28,11 @@ class SockHandlers(interfaces.configuration.VersionableInterface):
|
||||
self._vmlinux = vmlinux
|
||||
self._task = task
|
||||
|
||||
netns_id = task.nsproxy.net_ns.get_inode()
|
||||
try:
|
||||
netns_id = task.nsproxy.net_ns.get_inode()
|
||||
except AttributeError:
|
||||
netns_id = NotAvailableValue()
|
||||
|
||||
self._netdevices = self._build_network_devices_map(netns_id)
|
||||
|
||||
self._sock_family_handlers = {
|
||||
@@ -61,7 +65,10 @@ class SockHandlers(interfaces.configuration.VersionableInterface):
|
||||
self._vmlinux.symbol_table_name + constants.BANG + "net_device"
|
||||
)
|
||||
for net_dev in net.dev_base_head.to_list(net_device_symname, "dev_list"):
|
||||
if net.get_inode() != netns_id:
|
||||
if (
|
||||
isinstance(netns_id, NotAvailableValue)
|
||||
or net.get_inode() != netns_id
|
||||
):
|
||||
continue
|
||||
dev_name = utility.array_to_string(net_dev.name)
|
||||
netdevices_map[net_dev.ifindex] = dev_name
|
||||
@@ -143,19 +150,32 @@ class SockHandlers(interfaces.configuration.VersionableInterface):
|
||||
return
|
||||
|
||||
bpfprog = sock_filter.prog
|
||||
if bpfprog.type == 0:
|
||||
# BPF_PROG_TYPE_UNSPEC = 0
|
||||
|
||||
BPF_PROG_TYPE_UNSPEC = 0 # cBPF filter
|
||||
try:
|
||||
bpfprog_type = bpfprog.get_type()
|
||||
if bpfprog_type == BPF_PROG_TYPE_UNSPEC:
|
||||
return # cBPF filter
|
||||
except AttributeError:
|
||||
# kernel < 3.18.140, it's a cBPF filter
|
||||
return
|
||||
|
||||
BPF_PROG_TYPE_SOCKET_FILTER = 1 # eBPF filter
|
||||
if bpfprog_type != BPF_PROG_TYPE_SOCKET_FILTER:
|
||||
socket_filter["bpf_filter_type"] = f"UNK({bpfprog_type})"
|
||||
vollog.warning(f"Unexpected BPF type {bpfprog_type} for a socket")
|
||||
return
|
||||
|
||||
socket_filter["bpf_filter_type"] = "eBPF"
|
||||
if not bpfprog.has_member("aux") or not bpfprog.aux:
|
||||
return
|
||||
return # kernel < 3.18.140
|
||||
bpfprog_aux = bpfprog.aux
|
||||
|
||||
if bpfprog_aux.has_member("id"):
|
||||
# `id` member was added to `bpf_prog_aux` in kernels 4.13
|
||||
# `id` member was added to `bpf_prog_aux` in kernels 4.13.16
|
||||
socket_filter["bpf_filter_id"] = str(bpfprog_aux.id)
|
||||
if bpfprog_aux.has_member("name"):
|
||||
# `name` was added to `bpf_prog_aux` in kernels 4.15
|
||||
# `name` was added to `bpf_prog_aux` in kernels 4.15.18
|
||||
bpfprog_name = utility.array_to_string(bpfprog_aux.name)
|
||||
if bpfprog_name:
|
||||
socket_filter["bpf_filter_name"] = bpfprog_name
|
||||
@@ -227,14 +247,22 @@ class SockHandlers(interfaces.configuration.VersionableInterface):
|
||||
if netlink_sock.groups:
|
||||
groups_bitmap = netlink_sock.groups.dereference()
|
||||
src_addr = f"groups:0x{groups_bitmap:08x}"
|
||||
src_port = netlink_sock.portid
|
||||
|
||||
try:
|
||||
# Kernel >= 3.7.10
|
||||
src_port = netlink_sock.get_portid()
|
||||
except AttributeError:
|
||||
src_port = NotAvailableValue()
|
||||
|
||||
dst_addr = f"group:0x{netlink_sock.dst_group:08x}"
|
||||
module = netlink_sock.module
|
||||
if module and module.name:
|
||||
module_name_str = utility.array_to_string(module.name)
|
||||
dst_addr = f"{dst_addr},lkm:{module_name_str}"
|
||||
dst_port = netlink_sock.dst_portid
|
||||
try:
|
||||
dst_port = netlink_sock.get_dst_portid()
|
||||
except AttributeError:
|
||||
dst_port = NotAvailableValue()
|
||||
|
||||
state = netlink_sock.get_state()
|
||||
|
||||
@@ -518,7 +546,11 @@ class Sockstat(plugins.PluginInterface):
|
||||
protocol = child_sock.get_protocol()
|
||||
|
||||
net = task.nsproxy.net_ns
|
||||
netns_id = net.get_inode()
|
||||
try:
|
||||
netns_id = net.get_inode()
|
||||
except AttributeError:
|
||||
netns_id = NotAvailableValue()
|
||||
|
||||
yield task, netns_id, fd_num, family, sock_type, protocol, sock_fields
|
||||
|
||||
def _format_fields(self, sock_stat, protocol):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This file is Copyright 2019 Volatility Foundation and licensed under the Volatility Software License 1.0
|
||||
# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0
|
||||
#
|
||||
from typing import Iterator, List, Tuple, Optional
|
||||
from typing import Iterator, List, Tuple, Optional, Union
|
||||
|
||||
from volatility3 import framework
|
||||
from volatility3.framework import constants, exceptions, interfaces, objects
|
||||
@@ -30,6 +30,7 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable):
|
||||
self.set_type_class("kobject", extensions.kobject)
|
||||
# Might not exist in the current symbols
|
||||
self.optional_set_type_class("module", extensions.module)
|
||||
self.optional_set_type_class("bpf_prog", extensions.bpf_prog)
|
||||
|
||||
# Mount
|
||||
self.set_type_class("vfsmount", extensions.vfsmount)
|
||||
@@ -54,88 +55,106 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable):
|
||||
class LinuxUtilities(interfaces.configuration.VersionableInterface):
|
||||
"""Class with multiple useful linux functions."""
|
||||
|
||||
_version = (2, 0, 0)
|
||||
_version = (2, 1, 0)
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
framework.require_interface_version(*_required_framework_version)
|
||||
|
||||
# based on __d_path from the Linux kernel
|
||||
@classmethod
|
||||
def _do_get_path(cls, rdentry, rmnt, dentry, vfsmnt) -> str:
|
||||
ret_path: List[str] = []
|
||||
def _get_path_file(cls, task, filp) -> str:
|
||||
"""Returns the file pathname relative to the task's root directory.
|
||||
|
||||
while dentry != rdentry or vfsmnt != rmnt:
|
||||
dname = dentry.path()
|
||||
if dname == "":
|
||||
break
|
||||
Args:
|
||||
task (task_struct): A reference task
|
||||
filp (file *): A pointer to an open file
|
||||
|
||||
ret_path.insert(0, dname.strip("/"))
|
||||
if dentry == vfsmnt.get_mnt_root() or dentry == dentry.d_parent:
|
||||
if vfsmnt.get_mnt_parent() == vfsmnt:
|
||||
Returns:
|
||||
str: File pathname relative to the task's root directory.
|
||||
"""
|
||||
rdentry = task.fs.get_root_dentry()
|
||||
rmnt = task.fs.get_root_mnt()
|
||||
vfsmnt = filp.get_vfsmnt()
|
||||
dentry = filp.get_dentry()
|
||||
|
||||
return cls.do_get_path(rdentry, rmnt, dentry, vfsmnt)
|
||||
|
||||
@classmethod
|
||||
def get_path_mnt(cls, task, mnt) -> str:
|
||||
"""Returns the mount point pathname relative to the task's root directory.
|
||||
|
||||
Args:
|
||||
task (task_struct): A reference task
|
||||
mnt (vfsmount or mount): A mounted filesystem or a mount point.
|
||||
- kernels < 3.3.8 type is 'vfsmount'
|
||||
- kernels >= 3.3.8 type is 'mount'
|
||||
|
||||
Returns:
|
||||
str: Pathname of the mount point relative to the task's root directory.
|
||||
"""
|
||||
rdentry = task.fs.get_root_dentry()
|
||||
rmnt = task.fs.get_root_mnt()
|
||||
|
||||
vfsmnt = mnt.get_vfsmnt_current()
|
||||
dentry = mnt.get_dentry_current()
|
||||
|
||||
return cls.do_get_path(rdentry, rmnt, dentry, vfsmnt)
|
||||
|
||||
@classmethod
|
||||
def do_get_path(cls, rdentry, rmnt, dentry, vfsmnt) -> Union[None, str]:
|
||||
"""Returns a pathname of the mount point or file
|
||||
It mimics the Linux kernel prepend_path function.
|
||||
|
||||
Args:
|
||||
rdentry (dentry *): A pointer to the root dentry
|
||||
rmnt (vfsmount *): A pointer to the root vfsmount
|
||||
dentry (dentry *): A pointer to the dentry
|
||||
vfsmnt (vfsmount *): A pointer to the vfsmount
|
||||
|
||||
Returns:
|
||||
str: Pathname of the mount point or file
|
||||
"""
|
||||
|
||||
path_reversed = []
|
||||
while dentry != rdentry or not vfsmnt.is_equal(rmnt):
|
||||
if dentry == vfsmnt.get_mnt_root() or dentry.is_root():
|
||||
# Escaped?
|
||||
if dentry != vfsmnt.get_mnt_root():
|
||||
break
|
||||
|
||||
dentry = vfsmnt.get_mnt_mountpoint()
|
||||
vfsmnt = vfsmnt.get_mnt_parent()
|
||||
# Global root?
|
||||
if not vfsmnt.has_parent():
|
||||
break
|
||||
|
||||
dentry = vfsmnt.get_dentry_parent()
|
||||
vfsmnt = vfsmnt.get_vfsmnt_parent()
|
||||
|
||||
continue
|
||||
|
||||
parent = dentry.d_parent
|
||||
dname = dentry.d_name.name_as_str()
|
||||
path_reversed.append(dname.strip("/"))
|
||||
dentry = parent
|
||||
|
||||
# if we did not gather any valid dentrys in the path, then the entire file is
|
||||
# either 1) smeared out of memory or 2) de-allocated and corresponding structures overwritten
|
||||
# we return an empty string in this case to avoid confusion with something like a handle to the root
|
||||
# directory (e.g., "/")
|
||||
if not ret_path:
|
||||
return ""
|
||||
|
||||
ret_val = "/".join([str(p) for p in ret_path if p != ""])
|
||||
|
||||
if ret_val.startswith(("socket:", "pipe:")):
|
||||
if ret_val.find("]") == -1:
|
||||
try:
|
||||
inode = dentry.d_inode
|
||||
ino = inode.i_ino
|
||||
except exceptions.InvalidAddressException:
|
||||
ino = 0
|
||||
|
||||
ret_val = ret_val[:-1] + f":[{ino}]"
|
||||
else:
|
||||
ret_val = ret_val.replace("/", "")
|
||||
|
||||
elif ret_val != "inotify":
|
||||
ret_val = "/" + ret_val
|
||||
|
||||
return ret_val
|
||||
|
||||
# method used by 'older' kernels
|
||||
# TODO: lookup when dentry_operations->d_name was merged into the mainline kernel for exact version
|
||||
@classmethod
|
||||
def _get_path_file(cls, task, filp) -> str:
|
||||
rdentry = task.fs.get_root_dentry()
|
||||
rmnt = task.fs.get_root_mnt()
|
||||
dentry = filp.get_dentry()
|
||||
vfsmnt = filp.get_vfsmnt()
|
||||
|
||||
return LinuxUtilities._do_get_path(rdentry, rmnt, dentry, vfsmnt)
|
||||
path = "/" + "/".join(reversed(path_reversed))
|
||||
return path
|
||||
|
||||
@classmethod
|
||||
def _get_new_sock_pipe_path(cls, context, task, filp) -> str:
|
||||
"""Returns the sock pipe pathname relative to the task's root directory.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
task (task_struct): A reference task
|
||||
filp (file *): A pointer to a sock pipe open file
|
||||
|
||||
Returns:
|
||||
str: Sock pipe pathname relative to the task's root directory.
|
||||
"""
|
||||
dentry = filp.get_dentry()
|
||||
|
||||
kernel_module = cls.get_module_from_volobj_type(context, dentry)
|
||||
|
||||
sym_addr = dentry.d_op.d_dname
|
||||
|
||||
symbol_table_arr = sym_addr.vol.type_name.split("!")
|
||||
symbol_table = None
|
||||
if len(symbol_table_arr) == 2:
|
||||
symbol_table = symbol_table_arr[0]
|
||||
|
||||
for module_name in context.modules.get_modules_by_symbol_tables(symbol_table):
|
||||
kernel_module = context.modules[module_name]
|
||||
break
|
||||
else:
|
||||
raise ValueError(f"No module using the symbol table {symbol_table}")
|
||||
|
||||
symbs = list(kernel_module.get_symbols_by_absolute_location(sym_addr))
|
||||
|
||||
if len(symbs) == 1:
|
||||
@@ -163,10 +182,21 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface):
|
||||
|
||||
return ret
|
||||
|
||||
# a 'file' structure doesn't have enough information to properly restore its full path
|
||||
# we need the root mount information from task_struct to determine this
|
||||
@classmethod
|
||||
def path_for_file(cls, context, task, filp) -> str:
|
||||
"""Returns a file (or sock pipe) pathname relative to the task's root directory.
|
||||
|
||||
A 'file' structure doesn't have enough information to properly restore its
|
||||
full path we need the root mount information from task_struct to determine this
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
task (task_struct): A reference task
|
||||
filp (file *): A pointer to an open file
|
||||
|
||||
Returns:
|
||||
str: A file (or sock pipe) pathname relative to the task's root directory.
|
||||
"""
|
||||
try:
|
||||
dentry = filp.get_dentry()
|
||||
except exceptions.InvalidAddressException:
|
||||
@@ -348,3 +378,35 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface):
|
||||
return vmlinux.object(
|
||||
object_type=type_name, offset=container_addr, absolute=True
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_module_from_volobj_type(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
volobj: interfaces.objects.ObjectInterface,
|
||||
) -> interfaces.context.ModuleInterface:
|
||||
"""Get the vmlinux from a vol obj
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
volobj (vol object): A vol object
|
||||
|
||||
Raises:
|
||||
ValueError: If it cannot obtain any module from the symbol table
|
||||
|
||||
Returns:
|
||||
A kernel object (vmlinux)
|
||||
"""
|
||||
symbol_table_arr = volobj.vol.type_name.split("!", 1)
|
||||
symbol_table = symbol_table_arr[0] if len(symbol_table_arr) == 2 else None
|
||||
|
||||
module_names = context.modules.get_modules_by_symbol_tables(symbol_table)
|
||||
module_names = list(module_names)
|
||||
|
||||
if not module_names:
|
||||
raise ValueError(f"No module using the symbol table '{symbol_table}'")
|
||||
|
||||
kernel_module_name = module_names[0]
|
||||
kernel = context.modules[kernel_module_name]
|
||||
|
||||
return kernel
|
||||
|
||||
@@ -544,6 +544,7 @@ class struct_file(objects.StructType):
|
||||
raise AttributeError("Unable to find file -> dentry")
|
||||
|
||||
def get_vfsmnt(self) -> interfaces.objects.ObjectInterface:
|
||||
"""Returns the fs (vfsmount) where this file is mounted"""
|
||||
if self.has_member("f_vfsmnt"):
|
||||
return self.f_vfsmnt
|
||||
elif self.has_member("f_path"):
|
||||
@@ -675,11 +676,70 @@ class mount(objects.StructType):
|
||||
raise AttributeError("Unable to find mount -> mount flags")
|
||||
|
||||
def get_mnt_parent(self):
|
||||
"""Gets the fs where we are mounted on
|
||||
|
||||
Returns:
|
||||
A 'mount *'
|
||||
"""
|
||||
return self.mnt_parent
|
||||
|
||||
def get_mnt_mountpoint(self):
|
||||
"""Gets the dentry of the mountpoint
|
||||
|
||||
Returns:
|
||||
A 'dentry *'
|
||||
"""
|
||||
|
||||
return self.mnt_mountpoint
|
||||
|
||||
def get_parent_mount(self):
|
||||
return self.mnt.get_parent_mount()
|
||||
|
||||
def has_parent(self) -> bool:
|
||||
"""Checks if this mount has a parent
|
||||
|
||||
Returns:
|
||||
bool: 'True' if this mount has a parent
|
||||
"""
|
||||
return self.mnt_parent != self.vol.offset
|
||||
|
||||
def get_vfsmnt_current(self):
|
||||
"""Returns the fs where we are mounted on
|
||||
|
||||
Returns:
|
||||
A 'vfsmount'
|
||||
"""
|
||||
return self.mnt
|
||||
|
||||
def get_vfsmnt_parent(self):
|
||||
"""Gets the parent fs (vfsmount) to where it's mounted on
|
||||
|
||||
Returns:
|
||||
A 'vfsmount'
|
||||
"""
|
||||
|
||||
return self.get_mnt_parent().get_vfsmnt_current()
|
||||
|
||||
def get_dentry_current(self):
|
||||
"""Returns the root of the mounted tree
|
||||
|
||||
Returns:
|
||||
A 'dentry *'
|
||||
"""
|
||||
vfsmnt = self.get_vfsmnt_current()
|
||||
dentry = vfsmnt.mnt_root
|
||||
|
||||
return dentry
|
||||
|
||||
def get_dentry_parent(self):
|
||||
"""Returns the parent root of the mounted tree
|
||||
|
||||
Returns:
|
||||
A 'dentry *'
|
||||
"""
|
||||
|
||||
return self.get_mnt_parent().get_dentry_current()
|
||||
|
||||
def get_flags_access(self) -> str:
|
||||
return "ro" if self.get_mnt_flags() & self.MNT_READONLY else "rw"
|
||||
|
||||
@@ -703,9 +763,6 @@ class mount(objects.StructType):
|
||||
def get_devname(self) -> str:
|
||||
return utility.pointer_to_string(self.mnt_devname, count=255)
|
||||
|
||||
def has_parent(self) -> bool:
|
||||
return self.vol.offset != self.mnt_parent
|
||||
|
||||
def get_dominating_id(self, root) -> int:
|
||||
"""Get ID of closest dominating peer group having a representative under the given root."""
|
||||
mnt_seen = set()
|
||||
@@ -783,24 +840,121 @@ class vfsmount(objects.StructType):
|
||||
and self.get_mnt_parent() != 0
|
||||
)
|
||||
|
||||
def _get_real_mnt(self):
|
||||
table_name = self.vol.type_name.split(constants.BANG)[0]
|
||||
mount_struct = f"{table_name}{constants.BANG}mount"
|
||||
offset = self._context.symbol_space.get_type(
|
||||
mount_struct
|
||||
).relative_child_offset("mnt")
|
||||
def _is_kernel_prior_to_struct_mount(self) -> bool:
|
||||
"""Helper to distinguish between kernels prior to version 3.3.8 that
|
||||
lacked the 'mount' structure and later versions that have it.
|
||||
|
||||
return self._context.object(
|
||||
mount_struct, self.vol.layer_name, offset=self.vol.offset - offset
|
||||
The 'mnt_parent' member was moved from struct 'vfsmount' to struct
|
||||
'mount' when the latter was introduced.
|
||||
|
||||
Alternatively, vmlinux.has_type('mount') can be used here but it is faster.
|
||||
|
||||
Returns:
|
||||
bool: 'True' if the kernel
|
||||
"""
|
||||
|
||||
return self.has_member("mnt_parent")
|
||||
|
||||
def is_equal(self, vfsmount_ptr) -> bool:
|
||||
"""Helper to make sure it is comparing two pointers to 'vfsmount'.
|
||||
|
||||
Depending on the kernel version, the calling object (self) could be
|
||||
a 'vfsmount *' (<3.3.8) or a 'vfsmount' (>=3.3.8). This way we trust
|
||||
in the framework "auto" dereferencing ability to assure that when we
|
||||
reach this point 'self' will be a 'vfsmount' already and self.vol.offset
|
||||
a 'vfsmount *' and not a 'vfsmount **'. The argument must be a 'vfsmount *'.
|
||||
Typically, it's called from do_get_path().
|
||||
|
||||
Args:
|
||||
vfsmount_ptr (vfsmount *): A pointer to a 'vfsmount'
|
||||
|
||||
Raises:
|
||||
exceptions.VolatilityException: If vfsmount_ptr is not a 'vfsmount *'
|
||||
|
||||
Returns:
|
||||
bool: 'True' if the given argument points to the the same 'vfsmount'
|
||||
as 'self'.
|
||||
"""
|
||||
if type(vfsmount_ptr) == objects.Pointer:
|
||||
return self.vol.offset == vfsmount_ptr
|
||||
else:
|
||||
raise exceptions.VolatilityException(
|
||||
"Unexpected argument type. It has to be a 'vfsmount *'"
|
||||
)
|
||||
|
||||
def _get_real_mnt(self):
|
||||
"""Gets the struct 'mount' containing this 'vfsmount'.
|
||||
|
||||
It should be only called from kernels >= 3.3.8 when 'struct mount' was introduced.
|
||||
|
||||
Returns:
|
||||
mount: the struct 'mount' containing this 'vfsmount'.
|
||||
"""
|
||||
vmlinux = linux.LinuxUtilities.get_module_from_volobj_type(self._context, self)
|
||||
return linux.LinuxUtilities.container_of(
|
||||
self.vol.offset, "mount", "mnt", vmlinux
|
||||
)
|
||||
|
||||
def get_vfsmnt_current(self):
|
||||
"""Returns the current fs where we are mounted on
|
||||
|
||||
Returns:
|
||||
A 'vfsmount *'
|
||||
"""
|
||||
return self.get_mnt_parent()
|
||||
|
||||
def get_vfsmnt_parent(self):
|
||||
"""Gets the parent fs (vfsmount) to where it's mounted on
|
||||
|
||||
Returns:
|
||||
For kernels < 3.3.8: A 'vfsmount *'
|
||||
For kernels >= 3.3.8: A 'vfsmount'
|
||||
"""
|
||||
if self._is_kernel_prior_to_struct_mount():
|
||||
return self.get_mnt_parent()
|
||||
else:
|
||||
return self._get_real_mnt().get_vfsmnt_parent()
|
||||
|
||||
def get_dentry_current(self):
|
||||
"""Returns the root of the mounted tree
|
||||
|
||||
Returns:
|
||||
A 'dentry *'
|
||||
"""
|
||||
if self._is_kernel_prior_to_struct_mount():
|
||||
return self.get_mnt_mountpoint()
|
||||
else:
|
||||
return self._get_real_mnt().get_dentry_current()
|
||||
|
||||
def get_dentry_parent(self):
|
||||
"""Returns the parent root of the mounted tree
|
||||
|
||||
Returns:
|
||||
A 'dentry *'
|
||||
"""
|
||||
if self._is_kernel_prior_to_struct_mount():
|
||||
return self.get_mnt_mountpoint()
|
||||
else:
|
||||
return self._get_real_mnt().get_mnt_mountpoint()
|
||||
|
||||
def get_mnt_parent(self):
|
||||
if self.has_member("mnt_parent"):
|
||||
"""Gets the mnt_parent member.
|
||||
|
||||
Returns:
|
||||
For kernels < 3.3.8: A 'vfsmount *'
|
||||
For kernels >= 3.3.8: A 'mount *'
|
||||
"""
|
||||
if self._is_kernel_prior_to_struct_mount():
|
||||
return self.mnt_parent
|
||||
else:
|
||||
return self._get_real_mnt().mnt_parent
|
||||
return self._get_real_mnt().get_mnt_parent()
|
||||
|
||||
def get_mnt_mountpoint(self):
|
||||
"""Gets the dentry of the mountpoint
|
||||
|
||||
Returns:
|
||||
A 'dentry *'
|
||||
"""
|
||||
if self.has_member("mnt_mountpoint"):
|
||||
return self.mnt_mountpoint
|
||||
else:
|
||||
@@ -809,6 +963,41 @@ class vfsmount(objects.StructType):
|
||||
def get_mnt_root(self):
|
||||
return self.mnt_root
|
||||
|
||||
def has_parent(self) -> bool:
|
||||
if self._is_kernel_prior_to_struct_mount():
|
||||
return self.mnt_parent != self.vol.offset
|
||||
else:
|
||||
return self._get_real_mnt().has_parent()
|
||||
|
||||
def get_mnt_sb(self):
|
||||
return self.mnt_sb
|
||||
|
||||
def get_flags_access(self) -> str:
|
||||
return "ro" if self.mnt_flags & mount.MNT_READONLY else "rw"
|
||||
|
||||
def get_flags_opts(self) -> Iterable[str]:
|
||||
flags = [
|
||||
mntflagtxt
|
||||
for mntflag, mntflagtxt in mount.MNT_FLAGS.items()
|
||||
if mntflag & self.mnt_flags != 0
|
||||
]
|
||||
return flags
|
||||
|
||||
def get_mnt_flags(self):
|
||||
return self.mnt_flags
|
||||
|
||||
def is_shared(self) -> bool:
|
||||
return self.get_mnt_flags() & mount.MNT_SHARED
|
||||
|
||||
def is_unbindable(self) -> bool:
|
||||
return self.get_mnt_flags() & mount.MNT_UNBINDABLE
|
||||
|
||||
def is_slave(self) -> bool:
|
||||
return self.mnt_master and self.mnt_master.vol.offset != 0
|
||||
|
||||
def get_devname(self) -> str:
|
||||
return utility.pointer_to_string(self.mnt_devname, count=255)
|
||||
|
||||
|
||||
class kobject(objects.StructType):
|
||||
def reference_count(self):
|
||||
@@ -825,7 +1014,7 @@ class mnt_namespace(objects.StructType):
|
||||
def get_inode(self):
|
||||
if self.has_member("proc_inum"):
|
||||
return self.proc_inum
|
||||
elif self.ns.has_member("inum"):
|
||||
elif self.has_member("ns") and self.ns.has_member("inum"):
|
||||
return self.ns.inum
|
||||
else:
|
||||
raise AttributeError("Unable to find mnt_namespace inode")
|
||||
@@ -844,10 +1033,13 @@ class mnt_namespace(objects.StructType):
|
||||
class net(objects.StructType):
|
||||
def get_inode(self):
|
||||
if self.has_member("proc_inum"):
|
||||
# 3.8.13 <= kernel < 3.19.8
|
||||
return self.proc_inum
|
||||
elif self.ns.has_member("inum"):
|
||||
elif self.has_member("ns") and self.ns.has_member("inum"):
|
||||
# kernel >= 3.19.8
|
||||
return self.ns.inum
|
||||
else:
|
||||
# kernel < 3.8.13
|
||||
raise AttributeError("Unable to find net_namespace inode")
|
||||
|
||||
|
||||
@@ -1055,6 +1247,26 @@ class netlink_sock(objects.StructType):
|
||||
# Return the generic socket state
|
||||
return self.sk.sk_socket.get_state()
|
||||
|
||||
def get_portid(self):
|
||||
if self.has_member("pid"):
|
||||
# kernel < 3.7.10
|
||||
return self.pid
|
||||
if self.has_member("portid"):
|
||||
# kernel >= 3.7.10
|
||||
return self.portid
|
||||
else:
|
||||
raise AttributeError("Unable to find a source port id")
|
||||
|
||||
def get_dst_portid(self):
|
||||
if self.has_member("dst_pid"):
|
||||
# kernel < 3.7.10
|
||||
return self.dst_pid
|
||||
if self.has_member("dst_portid"):
|
||||
# kernel >= 3.7.10
|
||||
return self.dst_portid
|
||||
else:
|
||||
raise AttributeError("Unable to find a destination port id")
|
||||
|
||||
|
||||
class vsock_sock(objects.StructType):
|
||||
def get_protocol(self):
|
||||
@@ -1101,3 +1313,20 @@ class xdp_sock(objects.StructType):
|
||||
def get_state(self):
|
||||
# xdp_sock.state is an enum
|
||||
return self.state.lookup()
|
||||
|
||||
|
||||
class bpf_prog(objects.StructType):
|
||||
def get_type(self):
|
||||
# The program type was in `bpf_prog_aux::prog_type` from 3.18.140 to
|
||||
# 4.1.52 before it was moved to `bpf_prog::type`
|
||||
if self.has_member("type"):
|
||||
# kernel >= 4.1.52
|
||||
return self.type
|
||||
|
||||
if self.has_member("aux") and self.aux:
|
||||
if self.aux.has_member("prog_type"):
|
||||
# 3.18.140 <= kernel < 4.1.52
|
||||
return self.aux.prog_type
|
||||
|
||||
# kernel < 3.18.140
|
||||
raise AttributeError("Unable to find the BPF type")
|
||||
|
||||
Reference in New Issue
Block a user