Files
volatility3/volatility/framework/plugins/mac/kevents.py
T

175 lines
6.7 KiB
Python

# This file is opyright 2020 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 Iterable, Callable, Tuple
from volatility.framework import renderers, interfaces, exceptions, contexts
from volatility.framework.configuration import requirements
from volatility.framework.objects import utility
from volatility.framework.symbols import mac
from volatility.plugins.mac import pslist
class Kevents(interfaces.plugins.PluginInterface):
""" Lists event handlers registered by processes """
_required_framework_version = (2, 0, 0)
event_types = {
1: "EVFILT_READ",
2: "EVFILT_WRITE",
3: "EVFILT_AIO",
4: "EVFILT_VNODE",
5: "EVFILT_PROC",
6: "EVFILT_SIGNAL",
7: "EVFILT_TIMER",
8: "EVFILT_MACHPORT",
9: "EVFILT_FS",
10: "EVFILT_USER",
12: "EVFILT_VM"
}
vnode_filters = [("NOTE_DELETE", 1), ("NOTE_WRITE", 2), ("NOTE_EXTEND", 4), ("NOTE_ATTRIB", 8), ("NOTE_LINK", 0x10),
("NOTE_RENAME", 0x20), ("NOTE_REVOKE", 0x40)]
proc_filters = [("NOTE_EXIT", 0x80000000), ("NOTE_EXITSTATUS", 0x04000000), ("NOTE_FORK", 0x40000000),
("NOTE_EXEC", 0x20000000), ("NOTE_SIGNAL", 0x08000000), ("NOTE_REAP", 0x10000000)]
timer_filters = [("NOTE_SECONDS", 1), ("NOTE_USECONDS", 2), ("NOTE_NSECONDS", 4), ("NOTE_ABSOLUTE", 8)]
all_filters = {
4: vnode_filters, # EVFILT_VNODE
5: proc_filters, # EVFILT_PROC
7: timer_filters # EVFILT_TIMER
}
@classmethod
def get_requirements(cls):
return [
requirements.TranslationLayerRequirement(name = 'primary',
description = 'Memory layer for the kernel',
architectures = ["Intel32", "Intel64"]),
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel"),
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0)),
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 2, 0)),
requirements.ListRequirement(name = 'pid',
description = 'Filter on specific process IDs',
element_type = int,
optional = True)
]
def _parse_flags(self, filter_index, filter_flags):
if filter_flags == 0 or filter_index not in self.all_filters:
return ""
context = []
filters = self.all_filters[filter_index]
for flag, index in filters:
if filter_flags & index == index:
context.append(flag)
return ",".join(context)
@classmethod
def _walk_klist_array(cls, kernel, fdp, array_pointer_member, array_size_member):
"""
Convience wrapper for walking an array of lists of kernel events
Handles invalid address references
"""
try:
klist_array_pointer = getattr(fdp, array_pointer_member)
array_size = getattr(fdp, array_size_member)
klist_array = kernel.object(object_type = "array",
offset = klist_array_pointer,
count = array_size + 1,
subtype = kernel.get_type("klist"))
except exceptions.InvalidAddressException:
return
for klist in klist_array:
for kn in mac.MacUtilities.walk_slist(klist, "kn_link"):
yield kn
@classmethod
def _get_task_kevents(cls, kernel, task):
"""
Enumerates event filters per task.
Uses smear-safe APIs throughout as these data structures
see a signifcant amount of smear
"""
fdp = task.p_fd
for kn in cls._walk_klist_array(kernel, fdp, "fd_knlist", "fd_knlistsize"):
yield kn
for kn in cls._walk_klist_array(kernel, fdp, "fd_knhash", "fd_knhashmask"):
yield kn
try:
p_klist = task.p_klist
except exceptions.InvalidAddressException:
return
for kn in mac.MacUtilities.walk_slist(p_klist, "kn_link"):
yield kn
@classmethod
def list_kernel_events(cls,
context: interfaces.context.ContextInterface,
layer_name: str,
darwin_symbols: str,
filter_func: Callable[[int], bool] = lambda _: False) -> \
Iterable[Tuple[interfaces.objects.ObjectInterface,
interfaces.objects.ObjectInterface,
interfaces.objects.ObjectInterface]]:
"""
Returns the kernel event filters registered
Return values:
A tuple of 3 elements:
1) The name of the process that registered the filter
2) The process ID of the process that registered the filter
3) The object of the associated kernel event filter
"""
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
list_tasks = pslist.PsList.get_list_tasks(pslist.PsList.pslist_methods[0])
for task in list_tasks(context, layer_name, darwin_symbols, filter_func):
task_name = utility.array_to_string(task.p_comm)
pid = task.p_pid
for kn in cls._get_task_kevents(kernel, task):
yield task_name, pid, kn
def _generator(self):
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
for task_name, pid, kn in self.list_kernel_events(self.context,
self.config['primary'],
self.config['darwin'],
filter_func = filter_func):
filter_index = kn.kn_kevent.filter * -1
if filter_index in self.event_types:
filter_name = self.event_types[filter_index]
else:
continue
try:
ident = kn.kn_kevent.ident
except exceptions.InvalidAddressException:
continue
context = self._parse_flags(filter_index, kn.kn_sfflags)
yield (0, (pid, task_name, ident, filter_name, context))
def run(self):
return renderers.TreeGrid([("PID", int), ("Process", str), ("Ident", int), ("Filter", str), ("Context", str)],
self._generator())