mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-11 12:17:38 +02:00
Merge pull request #1288 from gcmoreira/linux_ptrace
Add Linux ptrace plugin
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
Linux-specific values that aren't found in debug symbols
|
||||
"""
|
||||
from enum import IntEnum
|
||||
from enum import IntEnum, Flag
|
||||
|
||||
KERNEL_NAME = "__kernel__"
|
||||
|
||||
@@ -302,3 +302,40 @@ class ELF_CLASS(IntEnum):
|
||||
ELFCLASSNONE = 0
|
||||
ELFCLASS32 = 1
|
||||
ELFCLASS64 = 2
|
||||
|
||||
|
||||
PT_OPT_FLAG_SHIFT = 3
|
||||
|
||||
PTRACE_EVENT_FORK = 1
|
||||
PTRACE_EVENT_VFORK = 2
|
||||
PTRACE_EVENT_CLONE = 3
|
||||
PTRACE_EVENT_EXEC = 4
|
||||
PTRACE_EVENT_VFORK_DONE = 5
|
||||
PTRACE_EVENT_EXIT = 6
|
||||
PTRACE_EVENT_SECCOMP = 7
|
||||
|
||||
PTRACE_O_EXITKILL = 1 << 20
|
||||
PTRACE_O_SUSPEND_SECCOMP = 1 << 21
|
||||
|
||||
|
||||
class PT_FLAGS(Flag):
|
||||
"PTrace flags"
|
||||
PT_PTRACED = 0x00001
|
||||
PT_SEIZED = 0x10000
|
||||
|
||||
PT_TRACESYSGOOD = 1 << (PT_OPT_FLAG_SHIFT + 0)
|
||||
PT_TRACE_FORK = 1 << (PT_OPT_FLAG_SHIFT + PTRACE_EVENT_FORK)
|
||||
PT_TRACE_VFORK = 1 << (PT_OPT_FLAG_SHIFT + PTRACE_EVENT_VFORK)
|
||||
PT_TRACE_CLONE = 1 << (PT_OPT_FLAG_SHIFT + PTRACE_EVENT_CLONE)
|
||||
PT_TRACE_EXEC = 1 << (PT_OPT_FLAG_SHIFT + PTRACE_EVENT_EXEC)
|
||||
PT_TRACE_VFORK_DONE = 1 << (PT_OPT_FLAG_SHIFT + PTRACE_EVENT_VFORK_DONE)
|
||||
PT_TRACE_EXIT = 1 << (PT_OPT_FLAG_SHIFT + PTRACE_EVENT_EXIT)
|
||||
PT_TRACE_SECCOMP = 1 << (PT_OPT_FLAG_SHIFT + PTRACE_EVENT_SECCOMP)
|
||||
|
||||
PT_EXITKILL = PTRACE_O_EXITKILL << PT_OPT_FLAG_SHIFT
|
||||
PT_SUSPEND_SECCOMP = PTRACE_O_SUSPEND_SECCOMP << PT_OPT_FLAG_SHIFT
|
||||
|
||||
@property
|
||||
def flags(self) -> str:
|
||||
"""Returns the ptrace flags string"""
|
||||
return str(self).replace(self.__class__.__name__ + ".", "")
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
# 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 logging
|
||||
from typing import List, Iterator
|
||||
|
||||
from volatility3.framework import renderers, interfaces
|
||||
from volatility3.framework.constants.architectures import LINUX_ARCHS
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.plugins.linux import pslist
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Ptrace(plugins.PluginInterface):
|
||||
"""Enumerates ptrace's tracer and tracee tasks"""
|
||||
|
||||
_required_framework_version = (2, 10, 0)
|
||||
_version = (1, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.ModuleRequirement(
|
||||
name="kernel",
|
||||
description="Linux kernel",
|
||||
architectures=LINUX_ARCHS,
|
||||
),
|
||||
requirements.PluginRequirement(
|
||||
name="pslist", plugin=pslist.PsList, version=(2, 2, 0)
|
||||
),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def enumerate_ptrace_tasks(
|
||||
cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
vmlinux_module_name: str,
|
||||
) -> Iterator[interfaces.objects.ObjectInterface]:
|
||||
"""Enumerates ptrace's tracer and tracee tasks
|
||||
|
||||
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
|
||||
|
||||
Yields:
|
||||
A task_struct object
|
||||
"""
|
||||
|
||||
tasks = pslist.PsList.list_tasks(
|
||||
context,
|
||||
vmlinux_module_name,
|
||||
filter_func=pslist.PsList.create_pid_filter(),
|
||||
include_threads=True,
|
||||
)
|
||||
|
||||
for task in tasks:
|
||||
if task.is_being_ptraced or task.is_ptracing:
|
||||
yield task
|
||||
|
||||
def _generator(self, vmlinux_module_name):
|
||||
for task in self.enumerate_ptrace_tasks(self.context, vmlinux_module_name):
|
||||
task_comm = utility.array_to_string(task.comm)
|
||||
user_pid = task.tgid
|
||||
user_tid = task.pid
|
||||
tracer_tid = task.get_ptrace_tracer_tid() or renderers.NotAvailableValue()
|
||||
tracee_tids = task.get_ptrace_tracee_tids() or [
|
||||
renderers.NotAvailableValue()
|
||||
]
|
||||
flags = task.get_ptrace_tracee_flags() or renderers.NotAvailableValue()
|
||||
|
||||
for level, tracee_tid in enumerate(tracee_tids):
|
||||
fields = [
|
||||
task_comm,
|
||||
user_pid,
|
||||
user_tid,
|
||||
tracer_tid,
|
||||
tracee_tid,
|
||||
flags,
|
||||
]
|
||||
yield (level, fields)
|
||||
|
||||
def run(self):
|
||||
vmlinux_module_name = self.config["kernel"]
|
||||
|
||||
headers = [
|
||||
("Process", str),
|
||||
("PID", int),
|
||||
("TID", int),
|
||||
("Tracer TID", int),
|
||||
("Tracee TID", int),
|
||||
("Flags", str),
|
||||
]
|
||||
return renderers.TreeGrid(headers, self._generator(vmlinux_module_name))
|
||||
@@ -13,13 +13,12 @@ from typing import Generator, Iterable, Iterator, Optional, Tuple, List, Union,
|
||||
|
||||
from volatility3.framework import constants, exceptions, objects, interfaces, symbols
|
||||
from volatility3.framework.renderers import conversion
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.constants.linux import SOCK_TYPES, SOCK_FAMILY
|
||||
from volatility3.framework.constants.linux import IP_PROTOCOLS, IPV6_PROTOCOLS
|
||||
from volatility3.framework.constants.linux import TCP_STATES, NETLINK_PROTOCOLS
|
||||
from volatility3.framework.constants.linux import ETH_PROTOCOLS, BLUETOOTH_STATES
|
||||
from volatility3.framework.constants.linux import BLUETOOTH_PROTOCOLS, SOCKET_STATES
|
||||
from volatility3.framework.constants.linux import CAPABILITIES
|
||||
from volatility3.framework.constants.linux import CAPABILITIES, PT_FLAGS
|
||||
from volatility3.framework.layers import linear
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.symbols import generic, linux, intermed
|
||||
@@ -383,6 +382,41 @@ class task_struct(generic.GenericIntelProcess):
|
||||
threads_seen.add(task.vol.offset)
|
||||
yield task
|
||||
|
||||
@property
|
||||
def is_being_ptraced(self) -> bool:
|
||||
"""Returns True if this task is being traced using ptrace"""
|
||||
return self.ptrace != 0
|
||||
|
||||
@property
|
||||
def is_ptracing(self) -> bool:
|
||||
"""Returns True if this task is tracing other tasks using ptrace"""
|
||||
is_tracing = (
|
||||
self.ptraced.next.is_readable()
|
||||
and self.ptraced.next.dereference().vol.offset != self.ptraced.vol.offset
|
||||
)
|
||||
return is_tracing
|
||||
|
||||
def get_ptrace_tracer_tid(self) -> Optional[int]:
|
||||
"""Returns the tracer's TID tracing this task"""
|
||||
return self.parent.pid if self.is_being_ptraced else None
|
||||
|
||||
def get_ptrace_tracee_tids(self) -> List[int]:
|
||||
"""Returns the list of TIDs being traced by this task"""
|
||||
task_symbol_table_name = self.get_symbol_table_name()
|
||||
|
||||
task_struct_symname = f"{task_symbol_table_name}{constants.BANG}task_struct"
|
||||
tracing_tid_list = [
|
||||
task_being_traced.pid
|
||||
for task_being_traced in self.ptraced.to_list(
|
||||
task_struct_symname, "ptrace_entry"
|
||||
)
|
||||
]
|
||||
return tracing_tid_list
|
||||
|
||||
def get_ptrace_tracee_flags(self) -> Optional[str]:
|
||||
"""Returns a string with the ptrace flags"""
|
||||
return PT_FLAGS(self.ptrace).flags if self.is_being_ptraced else None
|
||||
|
||||
|
||||
class fs_struct(objects.StructType):
|
||||
def get_root_dentry(self):
|
||||
|
||||
Reference in New Issue
Block a user