mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-17 15:17:39 +02:00
Core: Rename the top level namespace
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# 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
|
||||
#
|
||||
"""A module containing a collection of plugins that produce data typically
|
||||
found in mac's /proc file system."""
|
||||
|
||||
import datetime
|
||||
import struct
|
||||
|
||||
from volatility3.framework import constants, renderers, symbols
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.layers import scanners
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.symbols.linux.bash import BashIntermedSymbols
|
||||
from volatility3.plugins import timeliner
|
||||
from volatility3.plugins.mac import pslist
|
||||
|
||||
|
||||
class Bash(plugins.PluginInterface, timeliner.TimeLinerInterface):
|
||||
"""Recovers bash command history from memory."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@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 symbols"),
|
||||
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 _generator(self, tasks):
|
||||
is_32bit = not symbols.symbol_table_is_64bit(self.context, self.config["darwin"])
|
||||
if is_32bit:
|
||||
pack_format = "I"
|
||||
bash_json_file = "bash32"
|
||||
else:
|
||||
pack_format = "Q"
|
||||
bash_json_file = "bash64"
|
||||
|
||||
bash_table_name = BashIntermedSymbols.create(self.context, self.config_path, "linux", bash_json_file)
|
||||
|
||||
ts_offset = self.context.symbol_space.get_type(bash_table_name + constants.BANG +
|
||||
"hist_entry").relative_child_offset("timestamp")
|
||||
|
||||
for task in tasks:
|
||||
task_name = utility.array_to_string(task.p_comm)
|
||||
if task_name not in ["bash", "sh", "dash"]:
|
||||
continue
|
||||
|
||||
proc_layer_name = task.add_process_layer()
|
||||
if proc_layer_name is None:
|
||||
continue
|
||||
|
||||
proc_layer = self.context.layers[proc_layer_name]
|
||||
|
||||
bang_addrs = []
|
||||
|
||||
# find '#' values on the heap
|
||||
for address in proc_layer.scan(self.context,
|
||||
scanners.BytesScanner(b"#"),
|
||||
sections = task.get_process_memory_sections(self.context,
|
||||
self.config['darwin'],
|
||||
rw_no_file = True)):
|
||||
bang_addrs.append(struct.pack(pack_format, address))
|
||||
|
||||
history_entries = []
|
||||
|
||||
for address, _ in proc_layer.scan(self.context,
|
||||
scanners.MultiStringScanner(bang_addrs),
|
||||
sections = task.get_process_memory_sections(self.context,
|
||||
self.config['darwin'],
|
||||
rw_no_file = True)):
|
||||
hist = self.context.object(bash_table_name + constants.BANG + "hist_entry",
|
||||
offset = address - ts_offset,
|
||||
layer_name = proc_layer_name)
|
||||
|
||||
if hist.is_valid():
|
||||
history_entries.append(hist)
|
||||
|
||||
for hist in sorted(history_entries, key = lambda x: x.get_time_as_integer()):
|
||||
yield (0, (int(task.p_pid), task_name, hist.get_time_object(), hist.get_command()))
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
|
||||
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
|
||||
|
||||
return renderers.TreeGrid([("PID", int), ("Process", str), ("CommandTime", datetime.datetime),
|
||||
("Command", str)],
|
||||
self._generator(
|
||||
list_tasks(self.context,
|
||||
self.config['primary'],
|
||||
self.config['darwin'],
|
||||
filter_func = filter_func)))
|
||||
|
||||
def generate_timeline(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
|
||||
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
|
||||
|
||||
for row in self._generator(
|
||||
list_tasks(self.context, self.config['primary'], self.config['darwin'], filter_func = filter_func)):
|
||||
_depth, row_data = row
|
||||
description = "{} ({}): \"{}\"".format(row_data[0], row_data[1], row_data[3])
|
||||
yield (description, timeliner.TimeLinerType.CREATED, row_data[2])
|
||||
@@ -0,0 +1,66 @@
|
||||
# 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
|
||||
#
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from volatility3.framework import exceptions, interfaces
|
||||
from volatility3.framework import renderers, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import lsmod
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Check_syscall(plugins.PluginInterface):
|
||||
"""Check system call table for hooks."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
|
||||
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'lsmod', plugin = lsmod.Lsmod, version = (1, 0, 0))
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
kernel = contexts.Module(self._context, self.config['darwin'], self.config['primary'], 0)
|
||||
|
||||
mods = lsmod.Lsmod.list_modules(self.context, self.config['primary'], self.config['darwin'])
|
||||
|
||||
handlers = mac.MacUtilities.generate_kernel_handler_info(self.context, self.config['primary'], kernel, mods)
|
||||
|
||||
nsysent = kernel.object_from_symbol(symbol_name = "nsysent")
|
||||
table = kernel.object_from_symbol(symbol_name = "sysent")
|
||||
|
||||
# smear help
|
||||
num_ents = min(nsysent, table.count)
|
||||
if num_ents > 1024:
|
||||
num_ents = 1024
|
||||
|
||||
for (i, ent) in enumerate(table):
|
||||
try:
|
||||
call_addr = ent.sy_call.dereference().vol.offset
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
if not call_addr or call_addr == 0:
|
||||
continue
|
||||
|
||||
module_name, symbol_name = mac.MacUtilities.lookup_module_address(self.context, handlers, call_addr)
|
||||
|
||||
yield (0, (format_hints.Hex(table.vol.offset), "SysCall", i, format_hints.Hex(call_addr), module_name,
|
||||
symbol_name))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Table Address", format_hints.Hex), ("Table Name", str), ("Index", int),
|
||||
("Handler Address", format_hints.Hex), ("Handler Module", str),
|
||||
("Handler Symbol", str)], self._generator())
|
||||
@@ -0,0 +1,140 @@
|
||||
# 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
|
||||
#
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
import volatility3
|
||||
from volatility3.framework import exceptions, interfaces
|
||||
from volatility3.framework import renderers, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import lsmod
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Check_sysctl(plugins.PluginInterface):
|
||||
"""Check sysctl handlers for hooks."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
|
||||
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'lsmod', plugin = lsmod.Lsmod, version = (1, 0, 0))
|
||||
]
|
||||
|
||||
def _parse_global_variable_sysctls(self, kernel, name):
|
||||
known_sysctls = {
|
||||
"hostname": "hostname",
|
||||
"nisdomainname": "domainname",
|
||||
}
|
||||
|
||||
var_str = ""
|
||||
|
||||
if name in known_sysctls:
|
||||
var_name = known_sysctls[name]
|
||||
|
||||
try:
|
||||
var_array = kernel.object_from_symbol(symbol_name = var_name)
|
||||
except exceptions.SymbolError:
|
||||
var_array = None
|
||||
|
||||
if var_array is not None:
|
||||
var_str = utility.array_to_string(var_array)
|
||||
|
||||
return var_str
|
||||
|
||||
def _process_sysctl_list(self, kernel, sysctl_list, recursive = 0):
|
||||
if type(sysctl_list) == volatility3.framework.objects.Pointer:
|
||||
sysctl_list = sysctl_list.dereference().cast("sysctl_oid_list")
|
||||
|
||||
sysctl = sysctl_list.slh_first
|
||||
|
||||
if recursive != 0:
|
||||
try:
|
||||
sysctl = sysctl.oid_link.sle_next.dereference()
|
||||
except exceptions.InvalidAddressException:
|
||||
return
|
||||
|
||||
while sysctl:
|
||||
try:
|
||||
name = utility.pointer_to_string(sysctl.oid_name, 128)
|
||||
except exceptions.InvalidAddressException:
|
||||
name = ""
|
||||
|
||||
if len(name) == 0:
|
||||
break
|
||||
|
||||
ctltype = sysctl.get_ctltype()
|
||||
|
||||
try:
|
||||
arg1_ptr = sysctl.oid_arg1.dereference().vol.offset
|
||||
except exceptions.InvalidAddressException:
|
||||
arg1_ptr = 0
|
||||
|
||||
arg1 = sysctl.oid_arg1
|
||||
|
||||
if arg1 == 0 or arg1_ptr == 0:
|
||||
val = self._parse_global_variable_sysctls(kernel, name)
|
||||
elif ctltype == 'CTLTYPE_NODE':
|
||||
if sysctl.oid_handler == 0:
|
||||
for info in self._process_sysctl_list(kernel, sysctl.oid_arg1, recursive = 1):
|
||||
yield info
|
||||
|
||||
val = "Node"
|
||||
|
||||
elif ctltype in ['CTLTYPE_INT', 'CTLTYPE_QUAD', 'CTLTYPE_OPAQUE']:
|
||||
try:
|
||||
val = str(arg1.dereference().cast("int"))
|
||||
except exceptions.InvalidAddressException:
|
||||
val = "-1"
|
||||
|
||||
elif ctltype == 'CTLTYPE_STRING':
|
||||
try:
|
||||
val = utility.pointer_to_string(sysctl.oid_arg1, 64)
|
||||
except exceptions.InvalidAddressException:
|
||||
val = ""
|
||||
else:
|
||||
val = ctltype
|
||||
|
||||
yield (sysctl, name, val)
|
||||
|
||||
try:
|
||||
sysctl = sysctl.oid_link.sle_next
|
||||
except exceptions.InvalidAddressException:
|
||||
break
|
||||
|
||||
def _generator(self):
|
||||
kernel = contexts.Module(self._context, self.config['darwin'], self.config['primary'], 0)
|
||||
|
||||
mods = lsmod.Lsmod.list_modules(self.context, self.config['primary'], self.config['darwin'])
|
||||
|
||||
handlers = mac.MacUtilities.generate_kernel_handler_info(self.context, self.config['primary'], kernel, mods)
|
||||
|
||||
sysctl_list = kernel.object_from_symbol(symbol_name = "sysctl__children")
|
||||
|
||||
for sysctl, name, val in self._process_sysctl_list(kernel, sysctl_list):
|
||||
try:
|
||||
check_addr = sysctl.oid_handler
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
module_name, symbol_name = mac.MacUtilities.lookup_module_address(self.context, handlers, check_addr)
|
||||
|
||||
yield (0, (name, sysctl.oid_number, sysctl.get_perms(), format_hints.Hex(check_addr), val, module_name,
|
||||
symbol_name))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Name", str), ("Number", int), ("Perms", str),
|
||||
("Handler Address", format_hints.Hex), ("Value", str), ("Handler Module", str),
|
||||
("Handler Symbol", str)], self._generator())
|
||||
@@ -0,0 +1,61 @@
|
||||
# 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
|
||||
#
|
||||
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from volatility3.framework import exceptions, interfaces
|
||||
from volatility3.framework import renderers, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import lsmod
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Check_trap_table(plugins.PluginInterface):
|
||||
"""Check mach trap table for hooks."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
|
||||
requirements.PluginRequirement(name = 'lsmod', plugin = lsmod.Lsmod, version = (1, 0, 0)),
|
||||
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 0, 0)),
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
kernel = contexts.Module(self._context, self.config['darwin'], self.config['primary'], 0)
|
||||
|
||||
mods = lsmod.Lsmod.list_modules(self.context, self.config['primary'], self.config['darwin'])
|
||||
|
||||
handlers = mac.MacUtilities.generate_kernel_handler_info(self.context, self.config['primary'], kernel, mods)
|
||||
|
||||
table = kernel.object_from_symbol(symbol_name = "mach_trap_table")
|
||||
|
||||
for i, ent in enumerate(table):
|
||||
try:
|
||||
call_addr = ent.mach_trap_function.dereference().vol.offset
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
if not call_addr or call_addr == 0:
|
||||
continue
|
||||
|
||||
module_name, symbol_name = mac.MacUtilities.lookup_module_address(self.context, handlers, call_addr)
|
||||
|
||||
yield (0, (format_hints.Hex(table.vol.offset), "TrapTable", i, format_hints.Hex(call_addr), module_name,
|
||||
symbol_name))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Table Address", format_hints.Hex), ("Table Name", str), ("Index", int),
|
||||
("Handler Address", format_hints.Hex), ("Handler Module", str),
|
||||
("Handler Symbol", str)], self._generator())
|
||||
@@ -0,0 +1,52 @@
|
||||
# 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 volatility3.framework import exceptions, renderers, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.symbols import mac
|
||||
|
||||
|
||||
class Ifconfig(plugins.PluginInterface):
|
||||
"""Lists loaded kernel modules"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@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 symbols"),
|
||||
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 0, 0))
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
kernel = contexts.Module(self._context, self.config['darwin'], self.config['primary'], 0)
|
||||
|
||||
try:
|
||||
list_head = kernel.object_from_symbol(symbol_name = "ifnet_head")
|
||||
except exceptions.SymbolError:
|
||||
list_head = kernel.object_from_symbol(symbol_name = "dlil_ifnet_head")
|
||||
|
||||
for ifnet in mac.MacUtilities.walk_tailq(list_head, "if_link"):
|
||||
name = utility.pointer_to_string(ifnet.if_name, 32)
|
||||
unit = ifnet.if_unit
|
||||
prom = ifnet.if_flags & 0x100 == 0x100 # IFF_PROMISC
|
||||
|
||||
sock_addr_dl = ifnet.sockaddr_dl()
|
||||
if sock_addr_dl is None:
|
||||
mac_addr = renderers.UnreadableValue()
|
||||
else:
|
||||
mac_addr = str(sock_addr_dl)
|
||||
|
||||
for ifaddr in mac.MacUtilities.walk_tailq(ifnet.if_addrhead, "ifa_link"):
|
||||
ip = ifaddr.ifa_addr.get_address()
|
||||
|
||||
yield (0, ("{0}{1}".format(name, unit), ip, mac_addr, prom))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Interface", str), ("IP Address", str), ("Mac Address", str),
|
||||
("Promiscuous", bool)], self._generator())
|
||||
@@ -0,0 +1,59 @@
|
||||
# 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 volatility3.framework import renderers, interfaces, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import lsmod, kauth_scopes
|
||||
|
||||
|
||||
class Kauth_listeners(interfaces.plugins.PluginInterface):
|
||||
""" Lists kauth listeners and their status """
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@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.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 1, 0)),
|
||||
requirements.PluginRequirement(name = 'lsmod', plugin = lsmod.Lsmod, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'kauth_scopes',
|
||||
plugin = kauth_scopes.Kauth_scopes,
|
||||
version = (1, 0, 0))
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
"""
|
||||
Enumerates the listeners for each kauth scope
|
||||
"""
|
||||
kernel = contexts.Module(self.context, self.config['darwin'], self.config['primary'], 0)
|
||||
|
||||
mods = lsmod.Lsmod.list_modules(self.context, self.config['primary'], self.config['darwin'])
|
||||
|
||||
handlers = mac.MacUtilities.generate_kernel_handler_info(self.context, self.config['primary'], kernel, mods)
|
||||
|
||||
for scope in kauth_scopes.Kauth_scopes.list_kauth_scopes(self.context, self.config['primary'],
|
||||
self.config['darwin']):
|
||||
|
||||
scope_name = utility.pointer_to_string(scope.ks_identifier, 128)
|
||||
|
||||
for listener in scope.get_listeners():
|
||||
callback = listener.kll_callback
|
||||
if callback == 0:
|
||||
continue
|
||||
|
||||
module_name, symbol_name = mac.MacUtilities.lookup_module_address(self.context, handlers, callback)
|
||||
|
||||
yield (0, (scope_name, format_hints.Hex(listener.kll_idata), format_hints.Hex(callback), module_name,
|
||||
symbol_name))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Name", str), ("IData", format_hints.Hex), ("Callback Address", format_hints.Hex),
|
||||
("Module", str), ("Symbol", str)], self._generator())
|
||||
@@ -0,0 +1,76 @@
|
||||
# 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 volatility3.framework import renderers, interfaces, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import lsmod
|
||||
|
||||
|
||||
class Kauth_scopes(interfaces.plugins.PluginInterface):
|
||||
""" Lists kauth scopes and their status """
|
||||
|
||||
_version = (1, 0, 0)
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@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.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 1, 0)),
|
||||
requirements.PluginRequirement(name = 'lsmod', plugin = lsmod.Lsmod, version = (1, 0, 0))
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def list_kauth_scopes(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]]:
|
||||
"""
|
||||
Enumerates the registered kauth scopes and yields each object
|
||||
Uses smear-safe enumeration API
|
||||
"""
|
||||
|
||||
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
|
||||
|
||||
scopes = kernel.object_from_symbol("kauth_scopes")
|
||||
|
||||
for scope in mac.MacUtilities.walk_tailq(scopes, "ks_link"):
|
||||
yield scope
|
||||
|
||||
def _generator(self):
|
||||
kernel = contexts.Module(self.context, self.config['darwin'], self.config['primary'], 0)
|
||||
|
||||
mods = lsmod.Lsmod.list_modules(self.context, self.config['primary'], self.config['darwin'])
|
||||
|
||||
handlers = mac.MacUtilities.generate_kernel_handler_info(self.context, self.config['primary'], kernel, mods)
|
||||
|
||||
for scope in self.list_kauth_scopes(self.context, self.config['primary'], self.config['darwin']):
|
||||
|
||||
callback = scope.ks_callback
|
||||
if callback == 0:
|
||||
continue
|
||||
|
||||
module_name, symbol_name = mac.MacUtilities.lookup_module_address(self.context, handlers, callback)
|
||||
|
||||
identifier = utility.pointer_to_string(scope.ks_identifier, 128)
|
||||
|
||||
yield (0, (identifier, format_hints.Hex(scope.ks_idata), len([l for l in scope.get_listeners()]),
|
||||
format_hints.Hex(callback), module_name, symbol_name))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Name", str), ("IData", format_hints.Hex), ("Listeners", int),
|
||||
("Callback Address", format_hints.Hex), ("Module", str), ("Symbol", str)],
|
||||
self._generator())
|
||||
@@ -0,0 +1,174 @@
|
||||
# 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 volatility3.framework import renderers, interfaces, exceptions, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.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())
|
||||
@@ -0,0 +1,175 @@
|
||||
# This file is Copyright 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, Optional
|
||||
|
||||
from volatility3.framework import renderers, interfaces, exceptions
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import mount
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class List_Files(plugins.PluginInterface):
|
||||
"""Lists all open file descriptors for all processes."""
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "darwin", description = "Mac Kernel"),
|
||||
requirements.PluginRequirement(name = 'mount', plugin = mount.Mount, version = (1, 0, 0)),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def _vnode_name(cls, vnode: interfaces.objects.ObjectInterface) -> Optional[str]:
|
||||
# roots of mount points have special name handling
|
||||
if vnode.v_flag & 1 == 1:
|
||||
v_name = vnode.full_path()
|
||||
else:
|
||||
try:
|
||||
v_name = utility.pointer_to_string(vnode.v_name, 255)
|
||||
except exceptions.InvalidAddressException:
|
||||
v_name = None
|
||||
|
||||
return v_name
|
||||
|
||||
@classmethod
|
||||
def _get_parent(cls, vnode):
|
||||
parent = None
|
||||
|
||||
# root entries do not have parents
|
||||
# and parents of normal files can be smeared
|
||||
try:
|
||||
parent = vnode.v_parent
|
||||
except exceptions.InvalidAddressException:
|
||||
pass
|
||||
|
||||
return parent
|
||||
|
||||
@classmethod
|
||||
def _add_vnode(cls, vnode, loop_vnodes):
|
||||
"""
|
||||
Adds the given vnode to loop_vnodes.
|
||||
|
||||
loop_vnodes is key off the address of a vnode
|
||||
and holds its name, parent address, and object
|
||||
"""
|
||||
|
||||
key = vnode
|
||||
added = False
|
||||
|
||||
if not key in loop_vnodes:
|
||||
# We can't do anything with a no-name vnode
|
||||
v_name = cls._vnode_name(vnode)
|
||||
if v_name is None:
|
||||
return added
|
||||
|
||||
parent = cls._get_parent(vnode)
|
||||
if parent:
|
||||
parent_val = parent
|
||||
else:
|
||||
parent_val = None
|
||||
|
||||
loop_vnodes[key] = (v_name, parent_val, vnode)
|
||||
|
||||
added = True
|
||||
|
||||
return added
|
||||
|
||||
@classmethod
|
||||
def _walk_vnode(cls, vnode, loop_vnodes):
|
||||
"""
|
||||
Iterates over the list of vnodes associated with the given one.
|
||||
Also traverses the parent chain for the vnode and adds each one.
|
||||
"""
|
||||
while vnode:
|
||||
if not cls._add_vnode(vnode, loop_vnodes):
|
||||
break
|
||||
|
||||
parent = cls._get_parent(vnode)
|
||||
while parent:
|
||||
cls._walk_vnode(parent, loop_vnodes)
|
||||
parent = cls._get_parent(parent)
|
||||
|
||||
try:
|
||||
vnode = vnode.v_mntvnodes.tqe_next
|
||||
except exceptions.InvalidAddressException:
|
||||
break
|
||||
|
||||
@classmethod
|
||||
def _walk_vnodelist(cls, list_head, loop_vnodes):
|
||||
for vnode in mac.MacUtilities.walk_tailq(list_head, "v_mntvnodes"):
|
||||
cls._walk_vnode(vnode, loop_vnodes)
|
||||
|
||||
@classmethod
|
||||
def _walk_mounts(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
darwin_symbols: str) -> \
|
||||
Iterable[interfaces.objects.ObjectInterface]:
|
||||
|
||||
loop_vnodes = {}
|
||||
|
||||
# iterate each vnode source from each mount
|
||||
list_mounts = mount.Mount.list_mounts(context, layer_name, darwin_symbols)
|
||||
for mnt in list_mounts:
|
||||
cls._walk_vnodelist(mnt.mnt_vnodelist, loop_vnodes)
|
||||
cls._walk_vnodelist(mnt.mnt_workerqueue, loop_vnodes)
|
||||
cls._walk_vnodelist(mnt.mnt_newvnodes, loop_vnodes)
|
||||
|
||||
cls._walk_vnode(mnt.mnt_vnodecovered, loop_vnodes)
|
||||
cls._walk_vnode(mnt.mnt_realrootvp, loop_vnodes)
|
||||
cls._walk_vnode(mnt.mnt_devvp, loop_vnodes)
|
||||
|
||||
return loop_vnodes
|
||||
|
||||
@classmethod
|
||||
def _build_path(cls, vnodes, vnode_name, parent_offset):
|
||||
path = [vnode_name]
|
||||
|
||||
while parent_offset in vnodes:
|
||||
parent_name, parent_offset, _ = vnodes[parent_offset]
|
||||
if parent_offset is None:
|
||||
parent_offset = 0
|
||||
|
||||
path.insert(0, parent_name)
|
||||
|
||||
if len(path) > 1:
|
||||
path = "/".join(path)
|
||||
else:
|
||||
path = vnode_name
|
||||
|
||||
if path.startswith("//"):
|
||||
path = path[1:]
|
||||
|
||||
return path
|
||||
|
||||
@classmethod
|
||||
def list_files(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
darwin_symbols: str) -> \
|
||||
Iterable[interfaces.objects.ObjectInterface]:
|
||||
|
||||
vnodes = cls._walk_mounts(context, layer_name, darwin_symbols)
|
||||
|
||||
for voff, (vnode_name, parent_offset, vnode) in vnodes.items():
|
||||
full_path = cls._build_path(vnodes, vnode_name, parent_offset)
|
||||
|
||||
yield vnode, full_path
|
||||
|
||||
def _generator(self):
|
||||
for vnode, full_path in self.list_files(self.context, self.config['primary'], self.config['darwin']):
|
||||
|
||||
yield (0, (format_hints.Hex(vnode), full_path))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Address", format_hints.Hex), ("File Path", str)], self._generator())
|
||||
@@ -0,0 +1,60 @@
|
||||
# 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
|
||||
#
|
||||
"""A module containing a collection of plugins that produce data typically
|
||||
found in Mac's lsmod command."""
|
||||
from volatility3.framework import renderers, interfaces, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
|
||||
|
||||
class Lsmod(plugins.PluginInterface):
|
||||
"""Lists loaded kernel modules."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
_version = (1, 0, 0)
|
||||
|
||||
@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")
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def list_modules(cls, context: interfaces.context.ContextInterface, layer_name: str, darwin_symbols: str):
|
||||
"""Lists all the modules in the primary layer.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
darwin_symbols: The name of the table containing the kernel symbols
|
||||
|
||||
Returns:
|
||||
A list of modules from the `layer_name` layer
|
||||
"""
|
||||
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
|
||||
|
||||
kmod_ptr = kernel.object_from_symbol(symbol_name = "kmod")
|
||||
|
||||
# TODO - use smear-proof list walking API after dev release
|
||||
kmod = kmod_ptr.dereference().cast("kmod_info")
|
||||
while kmod != 0:
|
||||
yield kmod
|
||||
kmod = kmod.next
|
||||
|
||||
def _generator(self):
|
||||
for module in self.list_modules(self.context, self.config['primary'], self.config['darwin']):
|
||||
|
||||
mod_name = utility.array_to_string(module.name)
|
||||
mod_size = module.size
|
||||
|
||||
yield 0, (format_hints.Hex(module.vol.offset), mod_name, mod_size)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Offset", format_hints.Hex), ("Name", str), ("Size", int)], self._generator())
|
||||
@@ -0,0 +1,54 @@
|
||||
# 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
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from volatility3.framework import renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import pslist
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Lsof(plugins.PluginInterface):
|
||||
"""Lists all open file descriptors for all processes."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "darwin", description = "Mac Kernel"),
|
||||
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 0, 0)),
|
||||
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 _generator(self, tasks):
|
||||
for task in tasks:
|
||||
pid = task.p_pid
|
||||
|
||||
for _, filepath, fd in mac.MacUtilities.files_descriptors_for_process(self.context, self.config['darwin'],
|
||||
task):
|
||||
if filepath and len(filepath) > 0:
|
||||
yield (0, (pid, fd, filepath))
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
|
||||
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
|
||||
|
||||
return renderers.TreeGrid([("PID", int), ("File Descriptor", int), ("File Path", str)],
|
||||
self._generator(
|
||||
list_tasks(self.context,
|
||||
self.config['primary'],
|
||||
self.config['darwin'],
|
||||
filter_func = filter_func)))
|
||||
@@ -0,0 +1,80 @@
|
||||
# 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 volatility3.framework import constants
|
||||
from volatility3.framework import interfaces
|
||||
from volatility3.framework import renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.plugins.mac import pslist
|
||||
|
||||
|
||||
class Malfind(interfaces.plugins.PluginInterface):
|
||||
"""Lists process memory ranges that potentially contain injected code."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@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 _list_injections(self, task):
|
||||
"""Generate memory regions for a process that may contain injected
|
||||
code."""
|
||||
|
||||
proc_layer_name = task.add_process_layer()
|
||||
if proc_layer_name is None:
|
||||
return
|
||||
|
||||
proc_layer = self.context.layers[proc_layer_name]
|
||||
|
||||
for vma in task.get_map_iter():
|
||||
if not vma.is_suspicious(self.context, self.config['darwin']):
|
||||
data = proc_layer.read(vma.links.start, 64, pad = True)
|
||||
yield vma, data
|
||||
|
||||
def _generator(self, tasks):
|
||||
# determine if we're on a 32 or 64 bit kernel
|
||||
if self.context.symbol_space.get_type(self.config["darwin"] + constants.BANG + "pointer").size == 4:
|
||||
is_32bit_arch = True
|
||||
else:
|
||||
is_32bit_arch = False
|
||||
|
||||
for task in tasks:
|
||||
process_name = utility.array_to_string(task.p_comm)
|
||||
|
||||
for vma, data in self._list_injections(task):
|
||||
if is_32bit_arch:
|
||||
architecture = "intel"
|
||||
else:
|
||||
architecture = "intel64"
|
||||
|
||||
disasm = interfaces.renderers.Disassembly(data, vma.links.start, architecture)
|
||||
|
||||
yield (0, (task.p_pid, process_name, format_hints.Hex(vma.links.start), format_hints.Hex(vma.links.end),
|
||||
vma.get_perms(), format_hints.HexBytes(data), disasm))
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
|
||||
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
|
||||
|
||||
return renderers.TreeGrid([("PID", int), ("Process", str), ("Start", format_hints.Hex),
|
||||
("End", format_hints.Hex), ("Protection", str), ("Hexdump", format_hints.HexBytes),
|
||||
("Disasm", interfaces.renderers.Disassembly)],
|
||||
self._generator(
|
||||
list_tasks(self.context,
|
||||
self.config['primary'],
|
||||
self.config['darwin'],
|
||||
filter_func = filter_func)))
|
||||
@@ -0,0 +1,60 @@
|
||||
# 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
|
||||
#
|
||||
"""A module containing a collection of plugins that produce data typically
|
||||
found in Mac's mount command."""
|
||||
from volatility3.framework import renderers, interfaces, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.symbols import mac
|
||||
|
||||
|
||||
class Mount(plugins.PluginInterface):
|
||||
"""A module containing a collection of plugins that produce data typically
|
||||
foundin Mac's mount command"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
_version = (1, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 0, 0)),
|
||||
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols")
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def list_mounts(cls, context: interfaces.context.ContextInterface, layer_name: str, darwin_symbols: str):
|
||||
"""Lists all the mount structures in the primary layer.
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
darwin_symbols: The name of the table containing the kernel symbols
|
||||
|
||||
Returns:
|
||||
A list of mount structures from the `layer_name` layer
|
||||
"""
|
||||
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
|
||||
|
||||
list_head = kernel.object_from_symbol(symbol_name = "mountlist")
|
||||
|
||||
for mount in mac.MacUtilities.walk_tailq(list_head, "mnt_list"):
|
||||
yield mount
|
||||
|
||||
def _generator(self):
|
||||
for mount in self.list_mounts(self.context, self.config['primary'], self.config['darwin']):
|
||||
vfs = mount.mnt_vfsstat
|
||||
device_name = utility.array_to_string(vfs.f_mntonname)
|
||||
mount_point = utility.array_to_string(vfs.f_mntfromname)
|
||||
mount_type = utility.array_to_string(vfs.f_fstypename)
|
||||
|
||||
yield 0, (device_name, mount_point, mount_type)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Device", str), ("Mount Point", str), ("Type", str)], self._generator())
|
||||
@@ -0,0 +1,115 @@
|
||||
# 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
|
||||
#
|
||||
|
||||
import logging
|
||||
from typing import Iterable, Callable, Tuple
|
||||
|
||||
from volatility3.framework import exceptions, renderers, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import pslist
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Netstat(plugins.PluginInterface):
|
||||
"""Lists all network connections for all processes."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls):
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Kernel Address Space',
|
||||
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, 0, 0)),
|
||||
requirements.ListRequirement(name = 'pid',
|
||||
description = 'Filter on specific process IDs',
|
||||
element_type = int,
|
||||
optional = True)
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def list_sockets(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 open socket descriptors of a process
|
||||
|
||||
Return values:
|
||||
A tuple of 3 elements:
|
||||
1) The name of the process that opened the socket
|
||||
2) The process ID of the processed that opened the socket
|
||||
3) The address of the associated socket structure
|
||||
"""
|
||||
# This is hardcoded, since a change in the default method would change the expected results
|
||||
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 filp, _, _ in mac.MacUtilities.files_descriptors_for_process(context, darwin_symbols, task):
|
||||
try:
|
||||
ftype = filp.f_fglob.get_fg_type()
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
if ftype != 'SOCKET':
|
||||
continue
|
||||
|
||||
try:
|
||||
socket = filp.f_fglob.fg_data.dereference().cast("socket")
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
yield task_name, pid, socket
|
||||
|
||||
def _generator(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
|
||||
|
||||
for task_name, pid, socket in self.list_sockets(self.context,
|
||||
self.config['primary'],
|
||||
self.config['darwin'],
|
||||
filter_func = filter_func):
|
||||
|
||||
family = socket.get_family()
|
||||
|
||||
if family == 1:
|
||||
try:
|
||||
upcb = socket.so_pcb.dereference().cast("unpcb")
|
||||
path = utility.array_to_string(upcb.unp_addr.sun_path)
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
yield (0, (format_hints.Hex(socket.vol.offset), "UNIX", path, 0, "", 0, "",
|
||||
"{}/{:d}".format(task_name, pid)))
|
||||
|
||||
elif family in [2, 30]:
|
||||
state = socket.get_state()
|
||||
proto = socket.get_protocol_as_string()
|
||||
|
||||
vals = socket.get_converted_connection_info()
|
||||
|
||||
if vals:
|
||||
(lip, lport, rip, rport) = vals
|
||||
|
||||
yield (0, (format_hints.Hex(socket.vol.offset), proto, lip, lport, rip, rport, state,
|
||||
"{}/{:d}".format(task_name, pid)))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Offset", format_hints.Hex), ("Proto", str), ("Local IP", str), ("Local Port", int),
|
||||
("Remote IP", str), ("Remote Port", int), ("State", str), ("Process", str)],
|
||||
self._generator())
|
||||
@@ -0,0 +1,54 @@
|
||||
# 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 volatility3.framework import renderers, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.plugins.mac import pslist
|
||||
|
||||
|
||||
class Maps(interfaces.plugins.PluginInterface):
|
||||
"""Lists process memory ranges that potentially contain injected code."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@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 _generator(self, tasks):
|
||||
for task in tasks:
|
||||
process_name = utility.array_to_string(task.p_comm)
|
||||
process_pid = task.p_pid
|
||||
|
||||
for vma in task.get_map_iter():
|
||||
path = vma.get_path(self.context, self.config['darwin'])
|
||||
if path == "":
|
||||
path = vma.get_special_path()
|
||||
|
||||
yield (0, (process_pid, process_name, format_hints.Hex(vma.links.start),
|
||||
format_hints.Hex(vma.links.end), vma.get_perms(), path))
|
||||
|
||||
def run(self):
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
|
||||
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
|
||||
|
||||
return renderers.TreeGrid([("PID", int), ("Process", str), ("Start", format_hints.Hex),
|
||||
("End", format_hints.Hex), ("Protection", str), ("Map Name", str)],
|
||||
self._generator(
|
||||
list_tasks(self.context,
|
||||
self.config['primary'],
|
||||
self.config['darwin'],
|
||||
filter_func = filter_func)))
|
||||
@@ -0,0 +1,103 @@
|
||||
# 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
|
||||
#
|
||||
"""In-memory artifacts from OSX systems."""
|
||||
from typing import Iterator, Tuple, Any, Generator, List
|
||||
|
||||
from volatility3.framework import exceptions, renderers, interfaces
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.plugins.mac import pslist
|
||||
|
||||
|
||||
class Psaux(plugins.PluginInterface):
|
||||
"""Recovers program command line arguments."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
|
||||
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 _generator(self, tasks: Iterator[Any]) -> Generator[Tuple[int, Tuple[int, str, int, str]], None, None]:
|
||||
for task in tasks:
|
||||
proc_layer_name = task.add_process_layer()
|
||||
if proc_layer_name is None:
|
||||
continue
|
||||
|
||||
proc_layer = self.context.layers[proc_layer_name]
|
||||
|
||||
argsstart = task.user_stack - task.p_argslen
|
||||
|
||||
if not proc_layer.is_valid(argsstart) or task.p_argslen == 0 or task.p_argc == 0:
|
||||
continue
|
||||
|
||||
# Add one because the first two are usually duplicates
|
||||
argc = task.p_argc + 1
|
||||
|
||||
# smear protection
|
||||
if argc > 1024:
|
||||
continue
|
||||
|
||||
task_name = utility.array_to_string(task.p_comm)
|
||||
|
||||
args = [] # type: List[bytes]
|
||||
|
||||
while argc > 0:
|
||||
try:
|
||||
arg = proc_layer.read(argsstart, 256)
|
||||
except exceptions.InvalidAddressException:
|
||||
break
|
||||
|
||||
idx = arg.find(b'\x00')
|
||||
if idx != -1:
|
||||
arg = arg[:idx]
|
||||
|
||||
argsstart += len(str(arg)) + 1
|
||||
|
||||
# deal with the stupid alignment (leading nulls) and arg duplication
|
||||
if len(args) == 0:
|
||||
while argsstart < task.user_stack:
|
||||
try:
|
||||
check = proc_layer.read(argsstart, 1)
|
||||
except exceptions.InvalidAddressException:
|
||||
break
|
||||
|
||||
if check != b"\x00":
|
||||
break
|
||||
|
||||
argsstart = argsstart + 1
|
||||
|
||||
args.append(arg)
|
||||
|
||||
# also check for initial duplicates since OS X is painful
|
||||
elif arg != args[0]:
|
||||
args.append(arg)
|
||||
|
||||
argc = argc - 1
|
||||
|
||||
args_str = " ".join([s.decode("utf-8", errors = 'replace') for s in args])
|
||||
|
||||
yield (0, (task.p_pid, task_name, task.p_argc, args_str))
|
||||
|
||||
def run(self) -> renderers.TreeGrid:
|
||||
filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None))
|
||||
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
|
||||
|
||||
return renderers.TreeGrid([("PID", int), ("Process", str), ("Argc", int), ("Arguments", str)],
|
||||
self._generator(
|
||||
list_tasks(self.context,
|
||||
self.config['primary'],
|
||||
self.config['darwin'],
|
||||
filter_func = filter_func)))
|
||||
@@ -0,0 +1,290 @@
|
||||
# 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
|
||||
#
|
||||
|
||||
import logging
|
||||
from typing import Callable, Iterable, List, Dict
|
||||
|
||||
from volatility3.framework import renderers, interfaces, contexts, exceptions
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.symbols import mac
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PsList(interfaces.plugins.PluginInterface):
|
||||
"""Lists the processes present in a particular mac memory image."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
_version = (2, 0, 0)
|
||||
pslist_methods = ['tasks', 'allproc', 'process_group', 'sessions', 'pid_hash_table']
|
||||
|
||||
@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 symbols"),
|
||||
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 1, 0)),
|
||||
requirements.ChoiceRequirement(name = 'pslist_method',
|
||||
description = 'Method to determine for processes',
|
||||
choices = cls.pslist_methods,
|
||||
default = cls.pslist_methods[0],
|
||||
optional = True),
|
||||
requirements.ListRequirement(name = 'pid',
|
||||
description = 'Filter on specific process IDs',
|
||||
element_type = int,
|
||||
optional = True)
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_list_tasks(
|
||||
cls, method: str
|
||||
) -> Callable[[interfaces.context.ContextInterface, str, str, Callable[[int], bool]],
|
||||
Iterable[interfaces.objects.ObjectInterface]]:
|
||||
"""Returns the list_tasks method based on the selector
|
||||
|
||||
Args:
|
||||
method: Must be one fo the available methods in get_task_choices
|
||||
|
||||
Returns:
|
||||
list_tasks method for listing tasks
|
||||
"""
|
||||
# Ensure method is one of the suitable choices
|
||||
if method not in cls.pslist_methods:
|
||||
method = cls.pslist_methods[0]
|
||||
|
||||
if method == 'allproc':
|
||||
list_tasks = cls.list_tasks_allproc
|
||||
elif method == 'tasks':
|
||||
list_tasks = cls.list_tasks_tasks
|
||||
elif method == 'process_group':
|
||||
list_tasks = cls.list_tasks_process_group
|
||||
elif method == 'sessions':
|
||||
list_tasks = cls.list_tasks_sessions
|
||||
elif method == 'pid_hash_table':
|
||||
list_tasks = cls.list_tasks_pid_hash_table
|
||||
else:
|
||||
raise ValueError("Impossible method choice chosen")
|
||||
vollog.debug("Using method {}".format(method))
|
||||
|
||||
return list_tasks
|
||||
|
||||
@classmethod
|
||||
def create_pid_filter(cls, pid_list: List[int] = None) -> Callable[[int], bool]:
|
||||
|
||||
filter_func = lambda _: False
|
||||
# FIXME: mypy #4973 or #2608
|
||||
pid_list = pid_list or []
|
||||
filter_list = [x for x in pid_list if x is not None]
|
||||
if filter_list:
|
||||
|
||||
def list_filter(x):
|
||||
return x.p_pid not in filter_list
|
||||
|
||||
filter_func = list_filter
|
||||
return filter_func
|
||||
|
||||
def _generator(self):
|
||||
list_tasks = self.get_list_tasks(self.config.get('pslist_method', self.pslist_methods[0]))
|
||||
|
||||
for task in list_tasks(self.context,
|
||||
self.config['primary'],
|
||||
self.config['darwin'],
|
||||
filter_func = self.create_pid_filter(self.config.get('pid', None))):
|
||||
pid = task.p_pid
|
||||
ppid = task.p_ppid
|
||||
name = utility.array_to_string(task.p_comm)
|
||||
yield (0, (pid, ppid, name))
|
||||
|
||||
@classmethod
|
||||
def list_tasks_allproc(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
darwin_symbols: str,
|
||||
filter_func: Callable[[int], bool] = lambda _: False) -> \
|
||||
Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Lists all the processes in the primary layer based on the allproc method
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
darwin_symbols: The name of the table containing the kernel symbols
|
||||
filter_func: A function which takes a process object and returns True if the process should be ignored/filtered
|
||||
|
||||
Returns:
|
||||
The list of process objects from the processes linked list after filtering
|
||||
"""
|
||||
|
||||
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
|
||||
|
||||
kernel_layer = context.layers[layer_name]
|
||||
|
||||
proc = kernel.object_from_symbol(symbol_name = "allproc").lh_first
|
||||
|
||||
seen = {} # type: Dict[int, int]
|
||||
while proc is not None and proc.vol.offset != 0:
|
||||
if proc.vol.offset in seen:
|
||||
vollog.log(logging.INFO, "Recursive process list detected (a result of non-atomic acquisition).")
|
||||
break
|
||||
else:
|
||||
seen[proc.vol.offset] = 1
|
||||
|
||||
if kernel_layer.is_valid(proc.vol.offset, proc.vol.size) and not filter_func(proc):
|
||||
yield proc
|
||||
|
||||
try:
|
||||
proc = proc.p_list.le_next.dereference()
|
||||
except exceptions.InvalidAddressException:
|
||||
break
|
||||
|
||||
@classmethod
|
||||
def list_tasks_tasks(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
darwin_symbols: str,
|
||||
filter_func: Callable[[int], bool] = lambda _: False) -> \
|
||||
Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Lists all the tasks in the primary layer based on the tasks queue
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
darwin_symbols: The name of the table containing the kernel symbols
|
||||
filter_func: A function which takes a task object and returns True if the task should be ignored/filtered
|
||||
|
||||
Returns:
|
||||
The list of task objects from the `layer_name` layer's `tasks` list after filtering
|
||||
"""
|
||||
|
||||
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
|
||||
|
||||
kernel_layer = context.layers[layer_name]
|
||||
|
||||
queue_entry = kernel.object_from_symbol(symbol_name = "tasks")
|
||||
|
||||
seen = {} # type: Dict[int, int]
|
||||
for task in queue_entry.walk_list(queue_entry, "tasks", "task"):
|
||||
if task.vol.offset in seen:
|
||||
vollog.log(logging.INFO, "Recursive process list detected (a result of non-atomic acquisition).")
|
||||
break
|
||||
else:
|
||||
seen[task.vol.offset] = 1
|
||||
|
||||
try:
|
||||
proc = task.bsd_info.dereference().cast("proc")
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
if kernel_layer.is_valid(proc.vol.offset, proc.vol.size) and not filter_func(proc):
|
||||
yield proc
|
||||
|
||||
@classmethod
|
||||
def list_tasks_sessions(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
darwin_symbols: str,
|
||||
filter_func: Callable[[int], bool] = lambda _: False) -> \
|
||||
Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Lists all the tasks in the primary layer using sessions
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
darwin_symbols: The name of the table containing the kernel symbols
|
||||
filter_func: A function which takes a task object and returns True if the task should be ignored/filtered
|
||||
|
||||
Returns:
|
||||
The list of task objects from the `layer_name` layer's `tasks` list after filtering
|
||||
"""
|
||||
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
|
||||
|
||||
table_size = kernel.object_from_symbol(symbol_name = "sesshash")
|
||||
|
||||
sesshashtbl = kernel.object_from_symbol(symbol_name = "sesshashtbl")
|
||||
|
||||
proc_array = kernel.object(object_type = "array",
|
||||
offset = sesshashtbl,
|
||||
count = table_size + 1,
|
||||
subtype = kernel.get_type("sesshashhead"))
|
||||
|
||||
for proc_list in proc_array:
|
||||
for proc in mac.MacUtilities.walk_list_head(proc_list, "s_hash"):
|
||||
if proc.s_leader.is_readable() and not filter_func(proc.s_leader):
|
||||
yield proc.s_leader
|
||||
|
||||
@classmethod
|
||||
def list_tasks_process_group(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
darwin_symbols: str,
|
||||
filter_func: Callable[[int], bool] = lambda _: False) -> \
|
||||
Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Lists all the tasks in the primary layer using process groups
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
darwin_symbols: The name of the table containing the kernel symbols
|
||||
filter_func: A function which takes a task object and returns True if the task should be ignored/filtered
|
||||
|
||||
Returns:
|
||||
The list of task objects from the `layer_name` layer's `tasks` list after filtering
|
||||
"""
|
||||
|
||||
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
|
||||
|
||||
table_size = kernel.object_from_symbol(symbol_name = "pgrphash")
|
||||
|
||||
pgrphashtbl = kernel.object_from_symbol(symbol_name = "pgrphashtbl")
|
||||
|
||||
proc_array = kernel.object(object_type = "array",
|
||||
offset = pgrphashtbl,
|
||||
count = table_size + 1,
|
||||
subtype = kernel.get_type("pgrphashhead"))
|
||||
|
||||
for proc_list in proc_array:
|
||||
for pgrp in mac.MacUtilities.walk_list_head(proc_list, "pg_hash"):
|
||||
for proc in mac.MacUtilities.walk_list_head(pgrp.pg_members, "p_pglist"):
|
||||
if not filter_func(proc):
|
||||
yield proc
|
||||
|
||||
@classmethod
|
||||
def list_tasks_pid_hash_table(cls,
|
||||
context: interfaces.context.ContextInterface,
|
||||
layer_name: str,
|
||||
darwin_symbols: str,
|
||||
filter_func: Callable[[int], bool] = lambda _: False) -> \
|
||||
Iterable[interfaces.objects.ObjectInterface]:
|
||||
"""Lists all the tasks in the primary layer using the pid hash table
|
||||
|
||||
Args:
|
||||
context: The context to retrieve required elements (layers, symbol tables) from
|
||||
layer_name: The name of the layer on which to operate
|
||||
darwin_symbols: The name of the table containing the kernel symbols
|
||||
filter_func: A function which takes a task object and returns True if the task should be ignored/filtered
|
||||
|
||||
Returns:
|
||||
The list of task objects from the `layer_name` layer's `tasks` list after filtering
|
||||
"""
|
||||
|
||||
kernel = contexts.Module(context, darwin_symbols, layer_name, 0)
|
||||
|
||||
table_size = kernel.object_from_symbol(symbol_name = "pidhash")
|
||||
|
||||
pidhashtbl = kernel.object_from_symbol(symbol_name = "pidhashtbl")
|
||||
|
||||
proc_array = kernel.object(object_type = "array",
|
||||
offset = pidhashtbl,
|
||||
count = table_size + 1,
|
||||
subtype = kernel.get_type("pidhashhead"))
|
||||
|
||||
for proc_list in proc_array:
|
||||
for proc in mac.MacUtilities.walk_list_head(proc_list, "p_hash"):
|
||||
if not filter_func(proc):
|
||||
yield proc
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str)], self._generator())
|
||||
@@ -0,0 +1,73 @@
|
||||
# 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 volatility3.framework import renderers
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.plugins.mac import pslist
|
||||
|
||||
|
||||
class PsTree(plugins.PluginInterface):
|
||||
"""Plugin for listing processes in a tree based on their parent process
|
||||
ID."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._processes = {}
|
||||
self._levels = {}
|
||||
self._children = {}
|
||||
|
||||
@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 symbols"),
|
||||
requirements.PluginRequirement(name = 'pslist', plugin = pslist.PsList, version = (2, 0, 0))
|
||||
]
|
||||
|
||||
def _find_level(self, pid):
|
||||
"""Finds how deep the pid is in the processes list."""
|
||||
seen = set([])
|
||||
seen.add(pid)
|
||||
level = 0
|
||||
proc = self._processes.get(pid, None)
|
||||
while proc is not None and proc.vol.offset != 0 and proc.p_ppid != 0 and proc.p_ppid not in seen:
|
||||
ppid = int(proc.p_ppid)
|
||||
child_list = self._children.get(ppid, set([]))
|
||||
child_list.add(proc.p_pid)
|
||||
self._children[ppid] = child_list
|
||||
proc = self._processes.get(ppid, None)
|
||||
level += 1
|
||||
self._levels[pid] = level
|
||||
|
||||
def _generator(self):
|
||||
"""Generates the tree list of processes"""
|
||||
list_tasks = pslist.PsList.get_list_tasks(self.config.get('pslist_method', pslist.PsList.pslist_methods[0]))
|
||||
|
||||
for proc in list_tasks(self.context, self.config['primary'], self.config['darwin']):
|
||||
self._processes[proc.p_pid] = proc
|
||||
|
||||
# Build the child/level maps
|
||||
for pid in self._processes:
|
||||
self._find_level(pid)
|
||||
|
||||
def yield_processes(pid):
|
||||
proc = self._processes[pid]
|
||||
row = (proc.p_pid, proc.p_ppid, utility.array_to_string(proc.p_comm))
|
||||
|
||||
yield (self._levels[pid] - 1, row)
|
||||
for child_pid in self._children.get(pid, []):
|
||||
yield from yield_processes(child_pid)
|
||||
|
||||
for pid in self._levels:
|
||||
if self._levels[pid] == 1:
|
||||
yield from yield_processes(pid)
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("PID", int), ("PPID", int), ("COMM", str)], self._generator())
|
||||
@@ -0,0 +1,73 @@
|
||||
# This file is Copyright 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 List
|
||||
|
||||
from volatility3.framework import exceptions, interfaces
|
||||
from volatility3.framework import renderers, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import lsmod
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Socket_filters(plugins.PluginInterface):
|
||||
"""Enumerates kernel socket filters."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
|
||||
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'lsmod', plugin = lsmod.Lsmod, version = (1, 0, 0))
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
kernel = contexts.Module(self._context, self.config['darwin'], self.config['primary'], 0)
|
||||
|
||||
mods = lsmod.Lsmod.list_modules(self.context, self.config['primary'], self.config['darwin'])
|
||||
|
||||
handlers = mac.MacUtilities.generate_kernel_handler_info(self.context, self.config['primary'], kernel, mods)
|
||||
|
||||
members_to_check = [
|
||||
"sf_unregistered", "sf_attach", "sf_detach", "sf_notify", "sf_getpeername", "sf_getsockname", "sf_data_in",
|
||||
"sf_data_out", "sf_connect_in", "sf_connect_out", "sf_bind", "sf_setoption", "sf_getoption", "sf_listen",
|
||||
"sf_ioctl"
|
||||
]
|
||||
|
||||
filter_list = kernel.object_from_symbol(symbol_name = "sock_filter_head")
|
||||
|
||||
for filter_container in mac.MacUtilities.walk_tailq(filter_list, "sf_global_next"):
|
||||
current_filter = filter_container.sf_filter
|
||||
|
||||
filter_name = utility.pointer_to_string(current_filter.sf_name, count = 128)
|
||||
|
||||
try:
|
||||
filter_socket = filter_container.sf_entry_head.sfe_socket.vol.offset
|
||||
except exceptions.InvalidAddressException:
|
||||
filter_socket = 0
|
||||
|
||||
for member in members_to_check:
|
||||
check_addr = current_filter.member(attr = member)
|
||||
if check_addr == 0:
|
||||
continue
|
||||
|
||||
module_name, symbol_name = mac.MacUtilities.lookup_module_address(self.context, handlers, check_addr)
|
||||
|
||||
yield (0, (format_hints.Hex(current_filter.vol.offset), filter_name, member, \
|
||||
format_hints.Hex(filter_socket), format_hints.Hex(check_addr), module_name, symbol_name))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Filter", format_hints.Hex), ("Name", str), ("Member", str),
|
||||
("Socket", format_hints.Hex), ("Handler", format_hints.Hex), ("Module", str),
|
||||
("Symbol", str)], self._generator())
|
||||
@@ -0,0 +1,79 @@
|
||||
# This file is Copyright 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 List
|
||||
|
||||
from volatility3.framework import exceptions, interfaces
|
||||
from volatility3.framework import renderers, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import lsmod
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Timers(plugins.PluginInterface):
|
||||
"""Check for malicious kernel timers."""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
|
||||
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'lsmod', plugin = lsmod.Lsmod, version = (1, 0, 0))
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
kernel = contexts.Module(self.context, self.config['darwin'], self.config['primary'], 0)
|
||||
|
||||
mods = lsmod.Lsmod.list_modules(self.context, self.config['primary'], self.config['darwin'])
|
||||
|
||||
handlers = mac.MacUtilities.generate_kernel_handler_info(self.context, self.config['primary'], kernel, mods)
|
||||
|
||||
real_ncpus = kernel.object_from_symbol(symbol_name = "real_ncpus")
|
||||
|
||||
cpu_data_ptrs_ptr = kernel.get_symbol("cpu_data_ptr").address
|
||||
|
||||
cpu_data_ptrs_addr = kernel.object(object_type = "pointer",
|
||||
offset = cpu_data_ptrs_ptr,
|
||||
subtype = kernel.get_type('long unsigned int'))
|
||||
|
||||
cpu_data_ptrs = kernel.object(object_type = "array",
|
||||
offset = cpu_data_ptrs_addr,
|
||||
subtype = kernel.get_type('cpu_data'),
|
||||
count = real_ncpus)
|
||||
|
||||
for cpu_data_ptr in cpu_data_ptrs:
|
||||
try:
|
||||
queue = cpu_data_ptr.rtclock_timer.queue.head
|
||||
except exceptions.InvalidAddressException:
|
||||
break
|
||||
|
||||
for timer in queue.walk_list(queue, "q_link", "call_entry"):
|
||||
try:
|
||||
handler = timer.func.dereference().vol.offset
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
if timer.has_member("entry_time"):
|
||||
entry_time = timer.entry_time
|
||||
else:
|
||||
entry_time = -1
|
||||
|
||||
module_name, symbol_name = mac.MacUtilities.lookup_module_address(self.context, handlers, handler)
|
||||
|
||||
yield (0, (format_hints.Hex(handler), format_hints.Hex(timer.param0), format_hints.Hex(timer.param1), \
|
||||
timer.deadline, entry_time, module_name, symbol_name))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Function", format_hints.Hex), ("Param 0", format_hints.Hex),
|
||||
("Param 1", format_hints.Hex), ("Deadline", int), ("Entry Time", int),
|
||||
("Module", str), ("Symbol", str)], self._generator())
|
||||
@@ -0,0 +1,77 @@
|
||||
# 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
|
||||
#
|
||||
|
||||
import logging
|
||||
from typing import List, Iterator, Any
|
||||
|
||||
from volatility3.framework import exceptions, interfaces
|
||||
from volatility3.framework import renderers, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.interfaces import plugins
|
||||
from volatility3.framework.objects import utility
|
||||
from volatility3.framework.renderers import format_hints
|
||||
from volatility3.framework.symbols import mac
|
||||
from volatility3.plugins.mac import lsmod
|
||||
|
||||
vollog = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Trustedbsd(plugins.PluginInterface):
|
||||
"""Checks for malicious trustedbsd modules"""
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
@classmethod
|
||||
def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]:
|
||||
return [
|
||||
requirements.TranslationLayerRequirement(name = 'primary',
|
||||
description = 'Memory layer for the kernel',
|
||||
architectures = ["Intel32", "Intel64"]),
|
||||
requirements.SymbolTableRequirement(name = "darwin", description = "Mac kernel symbols"),
|
||||
requirements.VersionRequirement(name = 'macutils', component = mac.MacUtilities, version = (1, 0, 0)),
|
||||
requirements.PluginRequirement(name = 'lsmod', plugin = lsmod.Lsmod, version = (1, 0, 0))
|
||||
]
|
||||
|
||||
def _generator(self, mods: Iterator[Any]):
|
||||
kernel = contexts.Module(self._context, self.config['darwin'], self.config['primary'], 0)
|
||||
|
||||
handlers = mac.MacUtilities.generate_kernel_handler_info(self.context, self.config['primary'], kernel, mods)
|
||||
|
||||
policy_list = kernel.object_from_symbol(symbol_name = "mac_policy_list").cast("mac_policy_list")
|
||||
|
||||
entries = kernel.object(object_type = "array",
|
||||
offset = policy_list.entries.dereference().vol.offset,
|
||||
subtype = kernel.get_type('mac_policy_list_element'),
|
||||
count = policy_list.staticmax + 1)
|
||||
|
||||
for i, ent in enumerate(entries):
|
||||
# I don't know how this can happen, but the kernel makes this check all over the place
|
||||
# the policy isn't useful without any ops so a rootkit can't abuse this
|
||||
try:
|
||||
mpc = ent.mpc.dereference()
|
||||
ops = mpc.mpc_ops.dereference()
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
try:
|
||||
ent_name = utility.pointer_to_string(mpc.mpc_name, 255)
|
||||
except exceptions.InvalidAddressException:
|
||||
ent_name = "N/A"
|
||||
|
||||
for check in ops.vol.members:
|
||||
call_addr = getattr(ops, check)
|
||||
|
||||
if call_addr is None or call_addr == 0:
|
||||
continue
|
||||
|
||||
module_name, symbol_name = mac.MacUtilities.lookup_module_address(self.context, handlers, call_addr)
|
||||
|
||||
yield (0, (check, ent_name, format_hints.Hex(call_addr), module_name, symbol_name))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Member", str), ("Policy Name", str), ("Handler Address", format_hints.Hex),
|
||||
("Handler Module", str), ("Handler Symbol", str)],
|
||||
self._generator(
|
||||
lsmod.Lsmod.list_modules(self.context, self.config['primary'],
|
||||
self.config['darwin'])))
|
||||
@@ -0,0 +1,65 @@
|
||||
# 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 volatility3.framework import renderers, interfaces, exceptions, contexts
|
||||
from volatility3.framework.configuration import requirements
|
||||
from volatility3.framework.objects import utility
|
||||
|
||||
|
||||
class VFSevents(interfaces.plugins.PluginInterface):
|
||||
""" Lists processes that are filtering file system events """
|
||||
|
||||
_required_framework_version = (2, 0, 0)
|
||||
|
||||
event_types = [
|
||||
"CREATE_FILE", "DELETE", "STAT_CHANGED", "RENAME", "CONTENT_MODIFIED", "EXCHANGE", "FINDER_INFO_CHANGED",
|
||||
"CREATE_DIR", "CHOWN", "XATTR_MODIFIED", "XATTR_REMOVED", "DOCID_CREATED", "DOCID_CHANGED"
|
||||
]
|
||||
|
||||
@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"),
|
||||
]
|
||||
|
||||
def _generator(self):
|
||||
"""
|
||||
Lists the registered VFS event watching processes
|
||||
Also lists which event(s) a process is registered for
|
||||
"""
|
||||
|
||||
kernel = contexts.Module(self.context, self.config['darwin'], self.config['primary'], 0)
|
||||
|
||||
watcher_table = kernel.object_from_symbol("watcher_table")
|
||||
|
||||
for watcher in watcher_table:
|
||||
if watcher == 0:
|
||||
continue
|
||||
|
||||
task_name = utility.array_to_string(watcher.proc_name)
|
||||
task_pid = watcher.pid
|
||||
|
||||
events = []
|
||||
|
||||
try:
|
||||
event_array = kernel.object(object_type = "array",
|
||||
offset = watcher.event_list,
|
||||
count = 13,
|
||||
subtype = kernel.get_type("unsigned char"))
|
||||
|
||||
except exceptions.InvalidAddressException:
|
||||
continue
|
||||
|
||||
for i, event in enumerate(event_array):
|
||||
if event == 1:
|
||||
events.append(self.event_types[i])
|
||||
|
||||
if events != []:
|
||||
yield (0, (task_name, task_pid, ",".join(events)))
|
||||
|
||||
def run(self):
|
||||
return renderers.TreeGrid([("Name", str), ("PID", int), ("Events", str)], self._generator())
|
||||
Reference in New Issue
Block a user