Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
7 changes: 5 additions & 2 deletions volatility3/framework/objects/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,23 @@ def array_to_string(
) -> interfaces.objects.ObjectInterface:
"""Takes a volatility Array of characters and returns a string."""
# TODO: Consider checking the Array's target is a native char
if count is None:
count = array.vol.count
if not isinstance(array, objects.Array):
raise TypeError("Array_to_string takes an Array of char")

if count is None:
count = array.vol.count

return array.cast("string", max_length=count, errors=errors)


def pointer_to_string(pointer: "objects.Pointer", count: int, errors: str = "replace"):
"""Takes a volatility Pointer to characters and returns a string."""
if not isinstance(pointer, objects.Pointer):
raise TypeError("pointer_to_string takes a Pointer")

if count < 1:
raise ValueError("pointer_to_string requires a positive count")

char = pointer.dereference()
return char.cast("string", max_length=count, errors=errors)

Expand Down
6 changes: 4 additions & 2 deletions volatility3/framework/plugins/linux/mountinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class MountInfo(plugins.PluginInterface):
"""Lists mount points on processes mount namespaces"""

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

@classmethod
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
Expand Down Expand Up @@ -152,9 +152,11 @@ def _get_tasks_mountpoints(
if not (
task
and task.fs
and task.fs.root
and task.fs.is_readable()
and task.nsproxy
and task.nsproxy.is_readable()
and task.nsproxy.mnt_ns
and task.nsproxy.mnt_ns.is_readable()
):
# This task doesn't have all the information required.
# It should be a kernel < 2.6.30
Expand Down
30 changes: 23 additions & 7 deletions volatility3/framework/symbols/linux/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import math
import contextlib
from abc import ABC, abstractmethod
from typing import Iterator, List, Tuple, Optional, Union
from typing import Iterator, List, Tuple, Optional

from volatility3 import framework
from volatility3.framework import constants, exceptions, interfaces, objects
Expand Down Expand Up @@ -76,7 +76,7 @@ def __init__(self, *args, **kwargs) -> None:
class LinuxUtilities(interfaces.configuration.VersionableInterface):
"""Class with multiple useful linux functions."""

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

framework.require_interface_version(*_required_framework_version)
Expand Down Expand Up @@ -106,8 +106,8 @@ def get_path_mnt(cls, task, mnt) -> str:
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'
- kernels < 3.3 type is 'vfsmount'
- kernels >= 3.3 type is 'mount'

Returns:
str: Pathname of the mount point relative to the task's root directory.
Expand All @@ -121,22 +121,34 @@ def get_path_mnt(cls, task, mnt) -> str:
return cls.do_get_path(rdentry, rmnt, dentry, vfsmnt)

@classmethod
def do_get_path(cls, rdentry, rmnt, dentry, vfsmnt) -> Union[None, str]:
def do_get_path(cls, rdentry, rmnt, dentry, vfsmnt) -> 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
vfsmnt (vfsmount/vfsmount *): A vfsmount object (kernels >= 3.3) or a
vfsmount pointer (kernels < 3.3)

Returns:
str: Pathname of the mount point or file
"""

if not (rdentry and rdentry.is_readable() and rmnt and rmnt.is_readable()):
return ""

if isinstance(vfsmnt, objects.Pointer) and not (rmnt and rmnt.is_readable()):
# vfsmnt can be the vfsmount object itself (>=3.3) or a vfsmount * (<3.3)
return ""

path_reversed = []
while dentry != rdentry or not vfsmnt.is_equal(rmnt):
while (
dentry
and dentry.is_readable()
and (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():
Expand Down Expand Up @@ -449,6 +461,10 @@ def container_of(
type_dec = vmlinux.get_type(type_name)
member_offset = type_dec.relative_child_offset(member_name)
container_addr = addr - member_offset
layer = vmlinux.context.layers[vmlinux.layer_name]
if not layer.is_valid(container_addr):
return None

return vmlinux.object(
object_type=type_name, offset=container_addr, absolute=True
)
Expand Down
62 changes: 31 additions & 31 deletions volatility3/framework/symbols/linux/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1152,19 +1152,15 @@ def get_dentry(self) -> interfaces.objects.ObjectInterface:
"""Returns a pointer to the dentry associated with this file"""
if self.has_member("f_path"):
return self.f_path.dentry
elif self.has_member("f_dentry"):
return self.f_dentry
else:
raise AttributeError("Unable to find file -> dentry")

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_path"):
return self.f_path.mnt
elif self.has_member("f_vfsmnt"):
return self.f_vfsmnt
else:
raise AttributeError("Unable to find file -> vfs mount")

raise AttributeError("Unable to find file -> vfs mount")

def get_inode(self) -> interfaces.objects.ObjectInterface:
"""Returns an inode associated with this file"""
Expand Down Expand Up @@ -1394,9 +1390,9 @@ def get_dentry_current(self):
A dentry pointer
"""
vfsmnt = self.get_vfsmnt_current()
dentry = vfsmnt.mnt_root
dentry_pointer = vfsmnt.mnt_root

return dentry
return dentry_pointer

def get_dentry_parent(self):
"""Returns the parent root of the mounted tree
Expand Down Expand Up @@ -1504,39 +1500,40 @@ def is_valid(self):
)

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.
"""Helper to distinguish between kernels prior to version 3.3 which lacked the
'mount' struct, versus later versions that include it.
See 7d6fec45a5131918b51dcd76da52f2ec86a85be6.

The 'mnt_parent' member was moved from struct 'vfsmount' to struct
'mount' when the latter was introduced.
# Following that commit, also in kernel version 3.3 (3376f34fff5be9954fd9a9c4fd68f4a0a36d480e),
# the 'mnt_parent' member was relocated from the 'vfsmount' struct to the newly
# introduced 'mount' struct.

Alternatively, vmlinux.has_type('mount') can be used here but it is faster.

Returns:
bool: 'True' if the kernel
'True' if the kernel lacks the 'mount' struct, typically indicating kernel < 3.3.
"""

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 \\*'.
Depending on the kernel version, see 3376f34fff5be9954fd9a9c4fd68f4a0a36d480e,
the calling object (self) could be a 'vfsmount *' (<3.3) or a 'vfsmount' (>=3.3).
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'
vfsmount_ptr: A pointer to a 'vfsmount'

Raises:
exceptions.VolatilityException: If vfsmount_ptr is not a 'vfsmount \\*'
exceptions.VolatilityException: If vfsmount_ptr is not a 'vfsmount *'

Returns:
bool: 'True' if the given argument points to the the same 'vfsmount'
as 'self'.
'True' if the given argument points to the same 'vfsmount' as 'self'.
"""
if isinstance(vfsmount_ptr, objects.Pointer):
return self.vol.offset == vfsmount_ptr
Expand All @@ -1545,13 +1542,14 @@ def is_equal(self, vfsmount_ptr) -> bool:
"Unexpected argument type. It has to be a 'vfsmount *'"
)

def _get_real_mnt(self):
def _get_real_mnt(self) -> interfaces.objects.ObjectInterface:
"""Gets the struct 'mount' containing this 'vfsmount'.

It should be only called from kernels >= 3.3.8 when 'struct mount' was introduced.
It should be only called from kernels >= 3.3 when 'struct mount' was introduced.
See 7d6fec45a5131918b51dcd76da52f2ec86a85be6

Returns:
mount: the struct 'mount' containing this 'vfsmount'.
The 'mount' object containing this 'vfsmount'.
"""
vmlinux = linux.LinuxUtilities.get_module_from_volobj_type(self._context, self)
return linux.LinuxUtilities.container_of(
Expand All @@ -1570,8 +1568,8 @@ def get_vfsmnt_parent(self):
"""Gets the parent fs (vfsmount) to where it's mounted on

Returns:
For kernels < 3.3.8: A vfsmount pointer
For kernels >= 3.3.8: A vfsmount object
For kernels < 3.3: A vfsmount pointer
For kernels >= 3.3: A vfsmount object
"""
if self._is_kernel_prior_to_struct_mount():
return self.get_mnt_parent()
Expand Down Expand Up @@ -1604,8 +1602,8 @@ def get_mnt_parent(self):
"""Gets the mnt_parent member.

Returns:
For kernels < 3.3.8: A vfsmount pointer
For kernels >= 3.3.8: A mount pointer
For kernels < 3.3: A vfsmount pointer
For kernels >= 3.3: A mount pointer
"""
if self._is_kernel_prior_to_struct_mount():
return self.mnt_parent
Expand Down Expand Up @@ -1676,8 +1674,10 @@ def reference_count(self):
class mnt_namespace(objects.StructType):
def get_inode(self):
if self.has_member("proc_inum"):
# 98f842e675f96ffac96e6c50315790912b2812be 3.8 <= kernels < 3.19
return self.proc_inum
elif self.has_member("ns") and self.ns.has_member("inum"):
# kernels >= 3.19 435d5f4bb2ccba3b791d9ef61d2590e30b8e806e
return self.ns.inum
else:
raise AttributeError("Unable to find mnt_namespace inode")
Expand Down