Add the Mac kevents plugin

This commit is contained in:
Andrew Case
2020-10-24 23:12:57 +01:00
committed by ikelos
parent 1c709aa09c
commit b5d50c055b
2 changed files with 194 additions and 1 deletions
+184
View File
@@ -0,0 +1,184 @@
# 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
#
import logging
from typing import Iterable, Callable, Tuple
from volatility.framework import renderers, interfaces, constants, 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 """
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.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())
+10 -1
View File
@@ -36,8 +36,9 @@ class MacUtilities(interfaces.configuration.VersionableInterface):
"""
Version History:
1.1.0 -> added walk_list_head API
1.2.0 -> added walk_slist API
"""
_version = (1, 1, 0)
_version = (1, 2, 0)
@classmethod
def mask_mods_list(cls, context: interfaces.context.ContextInterface, layer_name: str,
@@ -203,4 +204,12 @@ class MacUtilities(interfaces.configuration.VersionableInterface):
for element in cls._walk_iterable(queue, "lh_first", "le_next", next_member, max_elements):
yield element
@classmethod
def walk_slist(cls,
queue : interfaces.objects.ObjectInterface,
next_member: str,
max_elements: int = 4096) -> Iterable[interfaces.objects.ObjectInterface]:
for element in cls._walk_iterable(queue, "slh_first", "sle_next", next_member, max_elements):
yield element