From 02bf609fd14f64161ca5ad648860ed530b794d6b Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 10 Dec 2021 17:50:52 +1100 Subject: [PATCH 001/140] Prepare linux.lsof.Lsof plugin to work as a helper library for other plugin. --- volatility3/framework/plugins/linux/lsof.py | 51 ++++++++++++++------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/volatility3/framework/plugins/linux/lsof.py b/volatility3/framework/plugins/linux/lsof.py index a074f5744..9ce7027f3 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -4,7 +4,7 @@ """A module containing a collection of plugins that produce data typically found in Linux's /proc file system.""" import logging -from typing import List +from typing import List, Callable from volatility3.framework import renderers, interfaces, constants from volatility3.framework.configuration import requirements @@ -21,6 +21,8 @@ class Lsof(plugins.PluginInterface): _required_framework_version = (2, 0, 0) + _version = (2, 0, 0) + @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: return [ @@ -34,26 +36,43 @@ class Lsof(plugins.PluginInterface): optional = True) ] - def _generator(self, tasks): - symbol_table = None - for task in tasks: - if symbol_table is None: + @classmethod + def list_fds(cls, + context: interfaces.context.ContextInterface, + symbol_table: str, + filter_func: Callable[[int], bool] = lambda _: False): + + linuxutils_symbol_table = None # type: ignore + for task in pslist.PsList.list_tasks(context, symbol_table, filter_func): + if linuxutils_symbol_table is None: if constants.BANG not in task.vol.type_name: raise ValueError("Task is not part of a symbol table") - symbol_table = task.vol.type_name.split(constants.BANG)[0] + linuxutils_symbol_table = task.vol.type_name.split(constants.BANG)[0] - name = utility.array_to_string(task.comm) + task_comm = utility.array_to_string(task.comm) pid = int(task.pid) - for fd_num, _, full_path in linux.LinuxUtilities.files_descriptors_for_process( - self.context, symbol_table, task): - yield (0, (pid, name, fd_num, full_path)) + fd_generator = linux.LinuxUtilities.files_descriptors_for_process( + context, + linuxutils_symbol_table, + task) - def run(self): + for fd_fields in fd_generator: + yield pid, task_comm, task, fd_fields + + def _generator(self): filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None)) - return renderers.TreeGrid([("PID", int), ("Process", str), ("FD", int), ("Path", str)], - self._generator( - pslist.PsList.list_tasks(self.context, - self.config['kernel'], - filter_func = filter_func))) + fds_generator = self.list_fds(self.context, + self.config['kernel'], + filter_func=filter_func) + + for pid, task_comm, _task, fd_fields in fds_generator: + fd_num, _filp, full_path = fd_fields + + fields = (pid, task_comm, fd_num, full_path) + yield (0, fields) + + def run(self): + tree_grid_args = [("PID", int), ("Process", str), ("FD", int), ("Path", str)] + return renderers.TreeGrid(tree_grid_args, self._generator()) From 6456e55ddcd121ba3e570a90c83c0138af5b532c Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 10 Dec 2021 17:51:27 +1100 Subject: [PATCH 002/140] Added Sockstat linux plugin to enumerate all processes sockets. The output format is based on the `ss` tools. It supports: * Unix socket * Inet/Inet6 sockets * Netlink sockets * VSock sockets * Packet sockets * XDP sockets (eBPF) * Bluetooth sockets (When the respective symbols are present) Changes to the linux Lsof plugin were required to be able to reuse its filedescriptor listing capability. --- .../framework/constants/linux/__init__.py | 216 ++++++++++ .../framework/plugins/linux/sockstat.py | 374 ++++++++++++++++++ .../framework/symbols/linux/__init__.py | 23 ++ .../symbols/linux/extensions/__init__.py | 251 ++++++++++++ 4 files changed, 864 insertions(+) create mode 100644 volatility3/framework/plugins/linux/sockstat.py diff --git a/volatility3/framework/constants/linux/__init__.py b/volatility3/framework/constants/linux/__init__.py index c25ea0e2f..6b63de6c5 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -11,3 +11,219 @@ KERNEL_NAME = "__kernel__" # arch/x86/include/asm/page_types.h PAGE_SHIFT = 12 """The value hard coded from the Linux Kernel (hence not extracted from the layer itself)""" + +# Standard well-defined IP protocols. +# ref: include/uapi/linux/in.h +IP_PROTOCOLS = { + 0: "IP", + 1: "ICMP", + 2: "IGMP", + 4: "IPIP", + 6: "TCP", + 8: "EGP", + 12: "PUP", + 17: "UDP", + 22: "IDP", + 29: "TP", + 33: "DCCP", + 41: "IPV6", + 46: "RSVP", + 47: "GRE", + 50: "ESP", + 51: "AH", + 92: "MTP", + 94: "BEETPH", + 98: "ENCAP", + 103: "PIM", + 108: "COMP", + 132: "SCTP", + 136: "UDPLITE", + 137: "MPLS", + 143: "ETHERNET", + 255: "RAW", + 262: "MPTCP", +} + +# IPV6 extension headers +# ref: include/uapi/linux/in6.h +IPV6_PROTOCOLS = { + 0: "HOPBYHOP_OPTS", + 43: "ROUTING", + 44: "FRAGMENT", + 58: "ICMPv6", + 59: "NO_NEXT", + 60: "DESTINATION_OPTS", + 135: "MOBILITY", +} + +# ref: include/net/tcp_states.h +TCP_STATES = ( + "", + "ESTABLISHED", + "SYN_SENT", + "SYN_RECV", + "FIN_WAIT1", + "FIN_WAIT2", + "TIME_WAIT", + "CLOSE", + "CLOSE_WAIT", + "LAST_ACK", + "LISTEN", + "CLOSING", + "TCP_NEW_SYN_RECV", +) + +# ref: include/linux/net.h (socket_type enum) +SOCK_TYPES = { + 1: "STREAM", + 2: "DGRAM", + 3: "RAW", + 4: "RDM", + 5: "SEQPACKET", + 6: "DCCP", + 10: "PACKET", +} + +# Address families +# ref: include/linux/socket.h +SOCK_FAMILY = ( + "AF_UNSPEC", + "AF_UNIX", + "AF_INET", + "AF_AX25", + "AF_IPX", + "AF_APPLETALK", + "AF_NETROM", + "AF_BRIDGE", + "AF_ATMPVC", + "AF_X25", + "AF_INET6", + "AF_ROSE", + "AF_DECnet", + "AF_NETBEUI", + "AF_SECURITY", + "AF_KEY", + "AF_NETLINK", + "AF_PACKET", + "AF_ASH", + "AF_ECONET", + "AF_ATMSVC", + "AF_RDS", + "AF_SNA", + "AF_IRDA", + "AF_PPPOX", + "AF_WANPIPE", + "AF_LLC", + "AF_IB", + "AF_MPLS", + "AF_CAN", + "AF_TIPC", + "AF_BLUETOOTH", + "AF_IUCV", + "AF_RXRPC", + "AF_ISDN", + "AF_PHONET", + "AF_IEEE802154", + "AF_CAIF", + "AF_ALG", + "AF_NFC", + "AF_VSOCK", + "AF_KCM", + "AF_QIPCRTR", + "AF_SMC", + "AF_XDP", +) + +# Netlink protocols +# ref: include/uapi/linux/netlink.h +NETLINK_PROTOCOLS = ( + "NETLINK_ROUTE", + "NETLINK_UNUSED", + "NETLINK_USERSOCK", + "NETLINK_FIREWALL", + "NETLINK_SOCK_DIAG", + "NETLINK_NFLOG", + "NETLINK_XFRM", + "NETLINK_SELINUX", + "NETLINK_ISCSI", + "NETLINK_AUDIT", + "NETLINK_FIB_LOOKUP", + "NETLINK_CONNECTOR", + "NETLINK_NETFILTER", + "NETLINK_IP6_FW", + "NETLINK_DNRTMSG", + "NETLINK_KOBJECT_UEVENT", + "NETLINK_GENERIC", + "NETLINK_DM", + "NETLINK_SCSITRANSPORT", + "NETLINK_ECRYPTFS", + "NETLINK_RDMA", + "NETLINK_CRYPTO", + "NETLINK_SMC", +) + +# Short list of Ethernet Protocol ID's. +# ref: include/uapi/linux/if_ether.h +# Used in AF_PACKET socket family +ETH_PROTOCOLS = { + 0x0001: "ETH_P_802_3", + 0x0002: "ETH_P_AX25", + 0x0003: "ETH_P_ALL", + 0x0004: "ETH_P_802_2", + 0x0005: "ETH_P_SNAP", + 0x0006: "ETH_P_DDCMP", + 0x0007: "ETH_P_WAN_PPP", + 0x0008: "ETH_P_PPP_MP", + 0x0009: "ETH_P_LOCALTALK", + 0x000c: "ETH_P_CAN", + 0x000f: "ETH_P_CANFD", + 0x0010: "ETH_P_PPPTALK", + 0x0011: "ETH_P_TR_802_2", + 0x0016: "ETH_P_CONTROL", + 0x0017: "ETH_P_IRDA", + 0x0018: "ETH_P_ECONET", + 0x0019: "ETH_P_HDLC", + 0x001a: "ETH_P_ARCNET", + 0x001b: "ETH_P_DSA", + 0x001c: "ETH_P_TRAILER", + 0x0060: "ETH_P_LOOP", + 0x00F6: "ETH_P_IEEE802154", + 0x00F7: "ETH_P_CAIF", + 0x00F8: "ETH_P_XDSA", + 0x00F9: "ETH_P_MAP", + 0x0800: "ETH_P_IP", + 0x0805: "ETH_P_X25", + 0x0806: "ETH_P_ARP", + 0x8035: "ETH_P_RARP", + 0x809B: "ETH_P_ATALK", + 0x80F3: "ETH_P_AARP", + 0x8100: "ETH_P_8021Q", +} + +# Connection and socket states +# ref: include/net/bluetooth/bluetooth.h +BLUETOOTH_STATES = ( + "", + "CONNECTED", + "OPEN", + "BOUND", + "LISTEN", + "CONNECT", + "CONNECT2", + "CONFIG", + "DISCONN", + "CLOSED", +) + +# Bluetooth protocols +# ref: include/net/bluetooth/bluetooth.h +BLUETOOTH_PROTOCOLS = ( + "L2CAP", + "HCI", + "SCO", + "RFCOMM", + "BNEP", + "CMTP", + "HIDP", + "AVDTP", +) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py new file mode 100644 index 000000000..3be359463 --- /dev/null +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -0,0 +1,374 @@ +# This file is Copyright 2021 Volatility Foundation and licensed under the Volatility Software License 1.0 +# which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 +# +# Author: Gustavo Moreira + +import logging +from typing import Callable + +from volatility3.framework import renderers, interfaces, exceptions, constants +from volatility3.framework.configuration import requirements +from volatility3.framework.interfaces import plugins +from volatility3.framework.objects import utility +from volatility3.framework.symbols import linux +from volatility3.plugins.linux import lsof + + +vollog = logging.getLogger(__name__) + +class SockHandlers(object): + def __init__(self, vmlinux, task): + self._vmlinux = vmlinux + self._task = task + + netns_id = task.nsproxy.net_ns.get_inode() + self._netdevices = self._build_network_devices_map(netns_id) + + self._sock_family_handlers = { + "AF_UNIX": self._unix_sock, + "AF_INET": self._inet_sock, + "AF_INET6": self._inet_sock, + "AF_NETLINK": self._netlink_sock, + "AF_VSOCK": self._vsock_sock, + "AF_PACKET": self._packet_sock, + "AF_XDP": self._xdp_sock, + "AF_BLUETOOTH": self._bluetooth_sock, + } + + def _build_network_devices_map(self, netns_id): + netdevices_map = {} + nethead = self._vmlinux.object_from_symbol(symbol_name="net_namespace_list") + net_symname = self._vmlinux.symbol_table_name + constants.BANG + "net" + for net in nethead.to_list(net_symname, "list"): + net_device_symname = self._vmlinux.symbol_table_name + constants.BANG + "net_device" + for net_dev in net.dev_base_head.to_list(net_device_symname, "dev_list"): + if net.get_inode() != netns_id: + continue + dev_name = str(utility.array_to_string(net_dev.name)) + netdevices_map[net_dev.ifindex] = dev_name + return netdevices_map + + def process_sock(self, sock): + family = sock.family + extended = {} + sock_handler = self._sock_family_handlers.get(family) + if sock_handler: + try: + sock_fields = sock_handler(sock, extended) + return *sock_fields, extended + except exceptions.SymbolError as e: + # Cannot finds the *_sock type in the symbols + vollog.warning("Error processing socket family '%s': %s", family, e) + else: + vollog.warning("Unsupported family '%s'", family) + + # Even if the sock family is not supported, or the required types + # are not present in the symbols, we can still show some general + # information about the socket that may be helpful. + saddr_tag = daddr_tag = state = "?" + + sock_stat = saddr_tag, daddr_tag, state + + return sock, sock_stat, extended + + def _unix_sock(self, sock, _extended): + unix_sock = sock.cast("unix_sock") + state = unix_sock.state + saddr = unix_sock.name + sinode = unix_sock.inode + if unix_sock.peer != 0: + peer = unix_sock.peer.dereference().cast("unix_sock") + daddr = peer.name + dinode = peer.inode + else: + daddr = dinode = "" + + saddr_tag = f"{saddr} {sinode}" + daddr_tag = f"{daddr} {dinode}" + sock_stat = saddr_tag, daddr_tag, state + return unix_sock, sock_stat + + def _inet_sock(self, sock, _extended): + inet_sock = sock.cast("inet_sock") + saddr = inet_sock.src_addr + sport = inet_sock.src_port + daddr = inet_sock.dst_addr + dport = inet_sock.dst_port + state = inet_sock.state + + if inet_sock.family == "AF_INET6": + saddr = f"[{saddr}]" + + saddr_tag = f"{saddr}:{sport}" + daddr_tag = f"{daddr}:{dport}" + sock_stat = saddr_tag, daddr_tag, state + return inet_sock, sock_stat + + def _netlink_sock(self, sock, _extended): + netlink_sock = sock.cast("netlink_sock") + + saddr_list = [] + src_portid = f"portid:{netlink_sock.portid}" + saddr_list.append(src_portid) + if netlink_sock.groups != 0: + groups_bitmap = netlink_sock.groups.dereference() + groups_str = f"groups:0x{groups_bitmap:08x}" + saddr_list.append(groups_str) + + daddr_list = [] + dst_portid = f"portid:{netlink_sock.dst_portid}" + daddr_list.append(dst_portid) + dst_group = f"group:0x{netlink_sock.dst_group:08x}" + daddr_list.append(dst_group) + module = netlink_sock.module + if module and netlink_sock.module.name: + module_name_str = utility.array_to_string(netlink_sock.module.name) + module_name = f"lkm:{module_name_str}" + daddr_list.append(module_name) + + saddr_tag = ",".join(saddr_list) + daddr_tag = ",".join(daddr_list) + state = netlink_sock.state + + sock_stat = saddr_tag, daddr_tag, state + return netlink_sock, sock_stat + + def _vsock_sock(self, sock, _extended): + vsock_sock = sock.cast("vsock_sock") + saddr = vsock_sock.local_addr.svm_cid + sport = vsock_sock.local_addr.svm_port + daddr = vsock_sock.remote_addr.svm_cid + dport = vsock_sock.remote_addr.svm_port + state = "" # Protocol is always 0 + + saddr_tag = f"{saddr}:{sport}" + daddr_tag = f"{daddr}:{dport}" + sock_stat = saddr_tag, daddr_tag, state + return vsock_sock, sock_stat + + def _packet_sock(self, sock, extended): + packet_sock = sock.cast("packet_sock") + ifindex = packet_sock.ifindex + dev_name = self._netdevices.get(ifindex, "") if ifindex > 0 else "ANY" + + if sock.has_member("sk_filter"): + sock_filter = sock.sk_filter + self.__update_extra_socket_bpf(sock_filter, extended) + + if sock.has_member("sk_reuseport_cb"): + sock_reuseport_cb = sock.sk_reuseport_cb + self.__update_extra_socket_bpf(sock_reuseport_cb, extended) + + saddr_tag = f"{dev_name}" + daddr_tag = "" + state = packet_sock.state + sock_stat = saddr_tag, daddr_tag, state + return packet_sock, sock_stat + + def __update_extra_socket_bpf(self, sock_filter, extended): + if not sock_filter: + return + + extended["bpf_filter_type"] = "cBPF" + + if not sock_filter.has_member("prog"): + return + + bpfprog = sock_filter.prog + if not bpfprog: + return + + BPF_PROG_TYPE_UNSPEC = 0 + if bpfprog.type > BPF_PROG_TYPE_UNSPEC: + extended["bpf_filter_type"] = "eBPF" + bpfprog_aux = bpfprog.aux + if bpfprog_aux: + extended["bpf_filter_id"] = str(bpfprog_aux.id) + bpfprog_name = str(utility.array_to_string(bpfprog.aux.name)) + if bpfprog_name: + extended["bpf_filter_name"] = bpfprog_name + + def _xdp_sock(self, sock, _extended): + xdp_sock = sock.cast("xdp_sock") + device = xdp_sock.dev + if not device: + return + + dev_name = utility.array_to_string(device.name) + saddr_tag = f"{dev_name}" + + bpfprog = device.xdp_prog + if not bpfprog: + return + + bpfprog_aux = bpfprog.aux + if bpfprog_aux: + bpfprog_id = bpfprog_aux.id + daddr_tag = f"ebpf_prog_id:{bpfprog_id}" + bpf_name = utility.array_to_string(bpfprog_aux.name) + if bpf_name: + daddr_tag += f",ebpf_prog_name:{bpf_name}" + else: + daddr_tag = "" + + # Hallelujah, xdp_sock.state is an enum + xsk_state = xdp_sock.state.lookup() + state = xsk_state.replace("XSK_", "") + + sock_stat = saddr_tag, daddr_tag, state + return xdp_sock, sock_stat + + def _bluetooth_sock(self, sock, _extended): + bt_sock = sock.cast("bt_sock") + + def bt_addr(addr): + return ":".join(reversed(["%02x" % x for x in addr.b])) + + saddr_tag = daddr_tag = "" + if bt_sock.protocol == "HCI": + pinfo = bt_sock.cast("hci_pinfo") + elif bt_sock.protocol == "L2CAP": + pinfo = bt_sock.cast("l2cap_pinfo") + src_addr = bt_addr(pinfo.chan.src) + dst_addr = bt_addr(pinfo.chan.dst) + saddr_tag = f"{src_addr}" + daddr_tag = f"{dst_addr}" + elif bt_sock.protocol == "RFCOMM": + pinfo = bt_sock.cast("rfcomm_pinfo") + src_addr = bt_addr(pinfo.src) + dst_addr = bt_addr(pinfo.dst) + channel = pinfo.channel + saddr_tag = f"[{src_addr}]:{channel}" + daddr_tag = f"{dst_addr}" + else: + vollog.warning("Unsupported bluetooth protocol '%s'", bt_sock.protocol) + + state = bt_sock.state + sock_stat = saddr_tag, daddr_tag, state + return bt_sock, sock_stat + +class Sockstat(plugins.PluginInterface): + """Lists all network connections for all processes.""" + + _required_framework_version = (2, 0, 0) + + _version = (2, 0, 0) + + @classmethod + def get_requirements(cls): + return [ + requirements.ModuleRequirement(name="kernel", description="Linux kernel", + architectures=["Intel32", "Intel64"]), + requirements.PluginRequirement(name="lsof", plugin=lsof.Lsof, version=(2, 0, 0)), + requirements.VersionRequirement(name="linuxutils", component=linux.LinuxUtilities, version=(2, 0, 0)), + requirements.BooleanRequirement(name="unix", + description=("Show UNIX domain Sockets only"), + default=False, + optional=True), + requirements.ListRequirement(name="pids", + description="Filter results by process IDs. " + "It takes the root PID namespace identifiers.", + element_type=int, + optional=True), + requirements.IntRequirement(name="netns", + description="Filter results by network namespace. " + "Otherwise, all of them are shown.", + optional=True), + ] + + @classmethod + def list_sockets(cls, + context: interfaces.context.ContextInterface, + vmlinux_module_name: str, + filter_func: Callable[[int], bool] = lambda _: False): + """ + Returns every single socket descriptors + """ + vmlinux = context.modules[vmlinux_module_name] + + sfop_addr = vmlinux.object_from_symbol("socket_file_ops").vol.offset + dfop_addr = vmlinux.object_from_symbol("sockfs_dentry_operations").vol.offset + + fd_generator = lsof.Lsof.list_fds(context, vmlinux.name, filter_func) + for _pid, _task_comm, task, fd_fields in fd_generator: + fd_num, filp, _full_path = fd_fields + + if filp.f_op not in (sfop_addr, dfop_addr): + continue + + dentry = filp.get_dentry() + if not dentry: + continue + + d_inode = dentry.d_inode + if not d_inode: + continue + + socket_alloc = linux.LinuxUtilities.container_of(d_inode, "socket_alloc", "vfs_inode", vmlinux) + _socket = socket_alloc.socket + + vfs_inode = socket_alloc.vfs_inode + if not (_socket and vfs_inode): + continue + + sock = _socket.sk.dereference() + + sock_type = sock.type + family = sock.family + + sock_handler = SockHandlers(vmlinux, task) + sock_fields = sock_handler.process_sock(sock) + if not sock_fields: + continue + + child_sock = sock_fields[0] + protocol = child_sock.protocol if hasattr(child_sock, "protocol") else "" + + net = task.nsproxy.net_ns + netns_id = net.proc_inum if net.has_member("proc_inum") else net.ns.inum + yield task, netns_id, fd_num, family, sock_type, protocol, sock_fields + + def _generator(self): + pids = self.config.get('pids') + filter_func = lsof.pslist.PsList.create_pid_filter(pids) + + tasks_per_sock = {} + socket_generator = self.list_sockets(self.context, self.config['kernel'], filter_func=filter_func) + for task, netns, fd_num, family, sock_type, protocol, sock_fields in socket_generator: + if self.config['netns'] and self.config['netns'] != netns: + continue + + sock, sock_stat, extended = sock_fields + + task_comm = utility.array_to_string(task.comm) + task_info = f"{task_comm},pid={task.pid},fd={fd_num}" + if extended: + extended_str = ",".join(f"{k}={v}" for k, v in extended.items()) + task_info = f"{task_info},{extended_str}" + + fields = netns, family, sock_type, protocol, *sock_stat + + sock_addr = sock.vol.offset + tasks_per_sock.setdefault(sock_addr, {}) + tasks_per_sock[sock_addr].setdefault('tasks', []) + tasks_per_sock[sock_addr]['tasks'].append(task_info) + tasks_per_sock[sock_addr]['fields'] = fields + + for data in tasks_per_sock.values(): + task_list = [f"({task})" for task in data['tasks']] + tasks = ",".join(task_list) + + fields = data['fields'] + (tasks,) + yield (0, fields) + + def run(self): + tree_grid_args = [("NetNS", int), + ("Family", str), + ("Type", str), + ("Proto", str), + ("Source Addr:Port", str), + ("Destination Addr:Port", str), + ("State", str), + ("Tasks", str)] + + return renderers.TreeGrid(tree_grid_args, self._generator()) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 36e23a35d..739ecbedb 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -30,6 +30,16 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class('vfsmount', extensions.vfsmount) self.set_type_class('kobject', extensions.kobject) + # Network + self.set_type_class('net', extensions.net) + self.set_type_class('sock', extensions.sock) + self.set_type_class('inet_sock', extensions.inet_sock) + self.set_type_class('unix_sock', extensions.unix_sock) + self.set_type_class('netlink_sock', extensions.netlink_sock) + self.set_type_class('packet_sock', extensions.packet_sock) + if 'bt_sock' in self.types: + self.set_type_class('bt_sock', extensions.bt_sock) + if 'module' in self.types: self.set_type_class('module', extensions.module) @@ -183,6 +193,10 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): def files_descriptors_for_process(cls, context: interfaces.context.ContextInterface, symbol_table: str, task: interfaces.objects.ObjectInterface): + # task.files can be null + if not task.files: + return + fd_table = task.files.get_fds() if fd_table == 0: return @@ -267,3 +281,12 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): list_struct = vmlinux.object(object_type = struct_name, offset = list_start.vol.offset) yield list_struct list_start = getattr(list_struct, list_member) + + @classmethod + def container_of(cls, addr, type_name, member_name, vmlinux): + if not addr: + return + type_dec = vmlinux.get_type(type_name) + member_offset = type_dec.relative_child_offset(member_name) + container_addr = addr - member_offset + return vmlinux.object(object_type=type_name, offset=container_addr, absolute=True) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 0edd60608..2af5f56b0 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -4,9 +4,15 @@ import collections.abc import logging +import socket from typing import Generator, Iterable, Iterator, Optional, Tuple from volatility3.framework import constants +from volatility3.framework.constants.linux import SOCK_TYPES, SOCK_FAMILY +from volatility3.framework.constants.linux import IP_PROTOCOLS, IPV6_PROTOCOLS +from volatility3.framework.constants.linux import TCP_STATES, NETLINK_PROTOCOLS +from volatility3.framework.constants.linux import ETH_PROTOCOLS, BLUETOOTH_STATES +from volatility3.framework.constants.linux import BLUETOOTH_PROTOCOLS from volatility3.framework import exceptions, objects, interfaces, symbols from volatility3.framework.layers import linear from volatility3.framework.objects import utility @@ -539,3 +545,248 @@ class kobject(objects.StructType): ret = refcnt.refs.counter return ret + +class mnt_namespace(objects.StructType): + def get_inode(self): + if self.has_member("proc_inum"): + return self.proc_inum + elif self.ns.has_member("inum"): + return self.ns.inum + else: + raise AttributeError("Unable to find mnt_namespace inode") + +class net(objects.StructType): + def get_inode(self): + if self.has_member("proc_inum"): + return self.proc_inum + elif self.ns.has_member("inum"): + return self.ns.inum + else: + raise AttributeError("Unable to find net_namespace inode") + +class sock(objects.StructType): + def __get_vol_kernel_module_name(self): + symbol_table_arr = self.vol.type_name.split("!", 1) + symbol_table = symbol_table_arr[0] if len(symbol_table_arr) == 2 else None + + module_names = list(self._context.modules.get_modules_by_symbol_tables(symbol_table)) + if not module_names: + raise ValueError(f"No module using the symbol table {symbol_table}") + + return module_names[0] + + @property + def family(self): + family_idx = self.__sk_common.skc_family + if 0 <= family_idx < len(SOCK_FAMILY): + return SOCK_FAMILY[family_idx] + else: + return "UNKNOWN" + + @property + def type(self): + return SOCK_TYPES.get(self.sk_type, "") + + @property + def inode(self): + if not self.sk_socket: + return 0 + + kernel_module_name = self.__get_vol_kernel_module_name() + kernel = self._context.modules[kernel_module_name] + socket_alloc = linux.LinuxUtilities.container_of(self.sk_socket, "socket_alloc", "socket", kernel) + vfs_inode = socket_alloc.vfs_inode + + return vfs_inode.i_ino + +class unix_sock(objects.StructType): + @property + def name(self): + if self.addr: + sockaddr_un = self.addr.name.cast("sockaddr_un") + saddr = str(utility.array_to_string(sockaddr_un.sun_path)) + else: + saddr = "" + return saddr + + @property + def protocol(self): + return "" + + @property + def state(self): + """Return a string representing the sock state.""" + + # Unix socket states reuse (a subset) of the inet_sock states contants + if self.sk.type == "STREAM": + state_idx = self.sk.__sk_common.skc_state + if 0 <= state_idx < len(TCP_STATES): + state = TCP_STATES[state_idx] + else: + state = "UNKNOWN" + else: + state = "UNCONNECTED" + + return state + + @property + def inode(self): + return self.sk.inode + +class inet_sock(objects.StructType): + @property + def family(self): + family_idx = self.sk.__sk_common.skc_family + if 0 <= family_idx < len(SOCK_FAMILY): + return SOCK_FAMILY[family_idx] + else: + return "UNKNOWN" + + @property + def protocol(self): + # If INET6 family and a proto is defined, we use that specific IPv6 protocol. + # Otherwise, we use the standard IP protocol. + protocol = IP_PROTOCOLS.get(self.sk.sk_protocol, "UNKNOWN") + if self.family == "AF_INET6": + protocol = IPV6_PROTOCOLS.get(self.sk.sk_protocol, protocol) + return protocol + + @property + def state(self): + """Return a string representing the sock state.""" + + if self.sk.type == "STREAM": + state_idx = self.sk.__sk_common.skc_state + if 0 <= state_idx < len(TCP_STATES): + state = TCP_STATES[state_idx] + else: + state = "UNKNOWN" + else: + state = "UNCONNECTED" + + return state + + @property + def src_port(self): + sport_le = getattr(self, "sport", getattr(self, "inet_sport", None)) + if sport_le is not None: + return socket.htons(sport_le) + + @property + def dst_port(self): + sk_common = self.sk.__sk_common + if hasattr(sk_common, "skc_portpair"): + dport_le = sk_common.skc_portpair & 0xffff + elif hasattr(self, "dport"): + dport_le = self.dport + elif hasattr(self, "inet_dport"): + dport_le = self.inet_dport + elif hasattr(sk_common, "skc_dport"): + dport_le = sk_common.skc_dport + else: + return + + return socket.htons(dport_le) + + @property + def src_addr(self): + sk_common = self.sk.__sk_common + family = sk_common.skc_family + if family == socket.AF_INET: + addr_size = 4 + if hasattr(self, "rcv_saddr"): + saddr = self.rcv_saddr + elif hasattr(self, "inet_rcv_saddr"): + saddr = self.inet_rcv_saddr + else: + saddr = sk_common.skc_rcv_saddr + elif family == socket.AF_INET6: + addr_size = 16 + saddr = self.pinet6.saddr + else: + return + + parent_layer = self._context.layers[self.vol.layer_name] + addr_bytes = parent_layer.read(saddr.vol.offset, addr_size) + return socket.inet_ntop(family, addr_bytes) + + @property + def dst_addr(self): + sk_common = self.sk.__sk_common + family = sk_common.skc_family + if family == socket.AF_INET: + if hasattr(self, "daddr") and self.daddr: + daddr = self.daddr + elif hasattr(self, "inet_daddr") and self.inet_daddr: + daddr = self.inet_daddr + else: + daddr = sk_common.skc_daddr + addr_size = 4 + elif family == socket.AF_INET6: + if hasattr(self.pinet6, "daddr"): + daddr = self.pinet6.daddr + else: + daddr = sk_common.skc_v6_daddr + addr_size = 16 + else: + return + + parent_layer = self._context.layers[self.vol.layer_name] + addr_bytes = parent_layer.read(daddr.vol.offset, addr_size) + return socket.inet_ntop(family, addr_bytes) + +class netlink_sock(objects.StructType): + @property + def protocol(self): + protocol_idx = self.sk.sk_protocol + if 0 <= protocol_idx < len(NETLINK_PROTOCOLS): + return NETLINK_PROTOCOLS[protocol_idx] + else: + return "UNKNOWN" + + @property + def state(self): + # Netlink is a datagram-oriented service. We can only have + # SOCK_RAW or SOCK_DGRAM socket types. + # NOTE: We are overridden the netlink_sock.state member here + return "UNCONNECTED" + + +class packet_sock(objects.StructType): + @property + def protocol(self): + eth_proto = socket.htons(self.num) + if eth_proto == 0: + return "" + elif eth_proto in ETH_PROTOCOLS: + return ETH_PROTOCOLS[eth_proto] + else: + return f"0x{eth_proto:x}" + + @property + def state(self): + # Packet socket types are either SOCK_RAW or SOCK_DGRAM. + # NOTE: We are overriding netlink_sock.state here + return "UNCONNECTED" + + +class bt_sock(objects.StructType): + @property + def protocol(self): + type_idx = self.sk.sk_protocol + if 0 <= type_idx < len(BLUETOOTH_PROTOCOLS): + state = BLUETOOTH_PROTOCOLS[type_idx] + else: + state = "UNKNOWN" + + return state + + @property + def state(self): + state_idx = self.sk.__sk_common.skc_state + if 0 <= state_idx < len(BLUETOOTH_STATES): + state = BLUETOOTH_STATES[state_idx] + else: + state = "UNKNOWN" + + return state From 5c507f5ae8de542f1bf530b3762eeaafa90226da Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 12:44:50 +1100 Subject: [PATCH 003/140] Plugins versioning fixes --- volatility3/framework/plugins/linux/lsof.py | 2 +- volatility3/framework/plugins/linux/sockstat.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/linux/lsof.py b/volatility3/framework/plugins/linux/lsof.py index 9ce7027f3..5ebd8e5c9 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -21,7 +21,7 @@ class Lsof(plugins.PluginInterface): _required_framework_version = (2, 0, 0) - _version = (2, 0, 0) + _version = (1, 1, 0) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 3be359463..4611d52e0 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -252,14 +252,14 @@ class Sockstat(plugins.PluginInterface): _required_framework_version = (2, 0, 0) - _version = (2, 0, 0) + _version = (1, 0, 0) @classmethod def get_requirements(cls): return [ requirements.ModuleRequirement(name="kernel", description="Linux kernel", architectures=["Intel32", "Intel64"]), - requirements.PluginRequirement(name="lsof", plugin=lsof.Lsof, version=(2, 0, 0)), + requirements.PluginRequirement(name="lsof", plugin=lsof.Lsof, version=(1, 1, 0)), requirements.VersionRequirement(name="linuxutils", component=linux.LinuxUtilities, version=(2, 0, 0)), requirements.BooleanRequirement(name="unix", description=("Show UNIX domain Sockets only"), From 9766327433de338e377340ba5f3565b85869d3c0 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 12:55:08 +1100 Subject: [PATCH 004/140] Parameterized generator --- volatility3/framework/plugins/linux/lsof.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/volatility3/framework/plugins/linux/lsof.py b/volatility3/framework/plugins/linux/lsof.py index 5ebd8e5c9..30ceafdab 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -60,13 +60,7 @@ class Lsof(plugins.PluginInterface): for fd_fields in fd_generator: yield pid, task_comm, task, fd_fields - def _generator(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None)) - - fds_generator = self.list_fds(self.context, - self.config['kernel'], - filter_func=filter_func) - + def _generator(self, fds_generator): for pid, task_comm, _task, fd_fields in fds_generator: fd_num, _filp, full_path = fd_fields @@ -74,5 +68,10 @@ class Lsof(plugins.PluginInterface): yield (0, fields) def run(self): + filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None)) + fds_generator = self.list_fds(self.context, + self.config['kernel'], + filter_func=filter_func) + tree_grid_args = [("PID", int), ("Process", str), ("FD", int), ("Path", str)] - return renderers.TreeGrid(tree_grid_args, self._generator()) + return renderers.TreeGrid(tree_grid_args, self._generator(fds_generator)) From 2e93db9b57eb9ba8d33fa70209e77d881b686d16 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 13:04:29 +1100 Subject: [PATCH 005/140] Adding versioning to SockHandlers --- volatility3/framework/plugins/linux/sockstat.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 4611d52e0..c53091d86 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -16,7 +16,12 @@ from volatility3.plugins.linux import lsof vollog = logging.getLogger(__name__) -class SockHandlers(object): +class SockHandlers(interfaces.configuration.VersionableInterface): + + _required_framework_version = (2, 0, 0) + + _version = (1, 0, 0) + def __init__(self, vmlinux, task): self._vmlinux = vmlinux self._task = task @@ -259,6 +264,7 @@ class Sockstat(plugins.PluginInterface): return [ requirements.ModuleRequirement(name="kernel", description="Linux kernel", architectures=["Intel32", "Intel64"]), + requirements.VersionRequirement(name="SockHandlers", component=SockHandlers, version=(1, 0, 0)), requirements.PluginRequirement(name="lsof", plugin=lsof.Lsof, version=(1, 1, 0)), requirements.VersionRequirement(name="linuxutils", component=linux.LinuxUtilities, version=(2, 0, 0)), requirements.BooleanRequirement(name="unix", From 6970776c4c330e1bc96e77df6c13d823503d1295 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 13:07:50 +1100 Subject: [PATCH 006/140] Renaming function to use single underscore and name from `extra` to `extended` --- volatility3/framework/plugins/linux/sockstat.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index c53091d86..eab8993ba 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -158,11 +158,11 @@ class SockHandlers(interfaces.configuration.VersionableInterface): if sock.has_member("sk_filter"): sock_filter = sock.sk_filter - self.__update_extra_socket_bpf(sock_filter, extended) + self._update_extended_socket_bpf(sock_filter, extended) if sock.has_member("sk_reuseport_cb"): sock_reuseport_cb = sock.sk_reuseport_cb - self.__update_extra_socket_bpf(sock_reuseport_cb, extended) + self._update_extended_socket_bpf(sock_reuseport_cb, extended) saddr_tag = f"{dev_name}" daddr_tag = "" @@ -170,7 +170,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return packet_sock, sock_stat - def __update_extra_socket_bpf(self, sock_filter, extended): + def _update_extended_socket_bpf(self, sock_filter, extended): if not sock_filter: return From 86676cf4e87bbfa99cf4165af39ba71d7e8f9481 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 13:20:31 +1100 Subject: [PATCH 007/140] Changing BPF_PROG_TYPE_UNSPEC constant in favor of a literal 0 and a comment. --- volatility3/framework/plugins/linux/sockstat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index eab8993ba..943c497b1 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -183,8 +183,8 @@ class SockHandlers(interfaces.configuration.VersionableInterface): if not bpfprog: return - BPF_PROG_TYPE_UNSPEC = 0 - if bpfprog.type > BPF_PROG_TYPE_UNSPEC: + # BPF_PROG_TYPE_UNSPEC = 0 + if bpfprog.type > 0: extended["bpf_filter_type"] = "eBPF" bpfprog_aux = bpfprog.aux if bpfprog_aux: From a386a7a9482edbb4ef0cb011cf89f681a4d0042e Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 15:40:51 +1100 Subject: [PATCH 008/140] Removed underscore from unused arguments --- volatility3/framework/plugins/linux/sockstat.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 943c497b1..d147f3e5e 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -76,7 +76,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): return sock, sock_stat, extended - def _unix_sock(self, sock, _extended): + def _unix_sock(self, sock, extended): unix_sock = sock.cast("unix_sock") state = unix_sock.state saddr = unix_sock.name @@ -93,7 +93,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return unix_sock, sock_stat - def _inet_sock(self, sock, _extended): + def _inet_sock(self, sock, extended): inet_sock = sock.cast("inet_sock") saddr = inet_sock.src_addr sport = inet_sock.src_port @@ -109,7 +109,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return inet_sock, sock_stat - def _netlink_sock(self, sock, _extended): + def _netlink_sock(self, sock, extended): netlink_sock = sock.cast("netlink_sock") saddr_list = [] @@ -138,7 +138,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return netlink_sock, sock_stat - def _vsock_sock(self, sock, _extended): + def _vsock_sock(self, sock, extended): vsock_sock = sock.cast("vsock_sock") saddr = vsock_sock.local_addr.svm_cid sport = vsock_sock.local_addr.svm_port @@ -193,7 +193,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): if bpfprog_name: extended["bpf_filter_name"] = bpfprog_name - def _xdp_sock(self, sock, _extended): + def _xdp_sock(self, sock, extended): xdp_sock = sock.cast("xdp_sock") device = xdp_sock.dev if not device: @@ -223,7 +223,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return xdp_sock, sock_stat - def _bluetooth_sock(self, sock, _extended): + def _bluetooth_sock(self, sock, extended): bt_sock = sock.cast("bt_sock") def bt_addr(addr): From c54818e6309d6bd4a83de14dfdb6a3c8fddd6d22 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 16:00:11 +1100 Subject: [PATCH 009/140] Remove redundancies around array_to_string(). It returns a str() already. --- volatility3/framework/plugins/linux/sockstat.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index d147f3e5e..f00169cf6 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -49,7 +49,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): for net_dev in net.dev_base_head.to_list(net_device_symname, "dev_list"): if net.get_inode() != netns_id: continue - dev_name = str(utility.array_to_string(net_dev.name)) + dev_name = utility.array_to_string(net_dev.name) netdevices_map[net_dev.ifindex] = dev_name return netdevices_map @@ -189,7 +189,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): bpfprog_aux = bpfprog.aux if bpfprog_aux: extended["bpf_filter_id"] = str(bpfprog_aux.id) - bpfprog_name = str(utility.array_to_string(bpfprog.aux.name)) + bpfprog_name = utility.array_to_string(bpfprog.aux.name) if bpfprog_name: extended["bpf_filter_name"] = bpfprog_name @@ -199,8 +199,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): if not device: return - dev_name = utility.array_to_string(device.name) - saddr_tag = f"{dev_name}" + saddr_tag = utility.array_to_string(device.name) bpfprog = device.xdp_prog if not bpfprog: From 52181c7e8fd50757e549121cb00e81deaaabf6c6 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 16:22:30 +1100 Subject: [PATCH 010/140] Improvements to the parameterized generator changes --- volatility3/framework/plugins/linux/lsof.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/linux/lsof.py b/volatility3/framework/plugins/linux/lsof.py index 30ceafdab..983f62562 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -60,7 +60,12 @@ class Lsof(plugins.PluginInterface): for fd_fields in fd_generator: yield pid, task_comm, task, fd_fields - def _generator(self, fds_generator): + def _generator(self, pids, symbol_table): + filter_func = pslist.PsList.create_pid_filter(pids) + fds_generator = self.list_fds(self.context, + symbol_table, + filter_func=filter_func) + for pid, task_comm, _task, fd_fields in fds_generator: fd_num, _filp, full_path = fd_fields @@ -68,10 +73,8 @@ class Lsof(plugins.PluginInterface): yield (0, fields) def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get('pid', None)) - fds_generator = self.list_fds(self.context, - self.config['kernel'], - filter_func=filter_func) + pids = self.config.get('pid', None) + symbol_table = self.config['kernel'] tree_grid_args = [("PID", int), ("Process", str), ("FD", int), ("Path", str)] - return renderers.TreeGrid(tree_grid_args, self._generator(fds_generator)) + return renderers.TreeGrid(tree_grid_args, self._generator(pids, symbol_table)) From 7ed5739e24d5f5c836a1cad2d2b0ddf73000f9a7 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 16:24:57 +1100 Subject: [PATCH 011/140] socket is no longer imported in this file. Remove the underscore --- volatility3/framework/plugins/linux/sockstat.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index f00169cf6..1af232b07 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -310,13 +310,13 @@ class Sockstat(plugins.PluginInterface): continue socket_alloc = linux.LinuxUtilities.container_of(d_inode, "socket_alloc", "vfs_inode", vmlinux) - _socket = socket_alloc.socket + socket = socket_alloc.socket vfs_inode = socket_alloc.vfs_inode - if not (_socket and vfs_inode): + if not (socket and vfs_inode): continue - sock = _socket.sk.dereference() + sock = socket.sk.dereference() sock_type = sock.type family = sock.family From b4bcdd0856c8dd85a393647d02b95553c82bf855 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 16:51:02 +1100 Subject: [PATCH 012/140] Minor changes --- volatility3/framework/plugins/linux/sockstat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 1af232b07..2e71dcd61 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -189,7 +189,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): bpfprog_aux = bpfprog.aux if bpfprog_aux: extended["bpf_filter_id"] = str(bpfprog_aux.id) - bpfprog_name = utility.array_to_string(bpfprog.aux.name) + bpfprog_name = utility.array_to_string(bpfprog_aux.name) if bpfprog_name: extended["bpf_filter_name"] = bpfprog_name @@ -287,7 +287,7 @@ class Sockstat(plugins.PluginInterface): vmlinux_module_name: str, filter_func: Callable[[int], bool] = lambda _: False): """ - Returns every single socket descriptors + Returns every single socket descriptor """ vmlinux = context.modules[vmlinux_module_name] From 8af74229288cd30e7a662ad7025b4e4093c19c61 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 16:52:47 +1100 Subject: [PATCH 013/140] Parameterized generator --- volatility3/framework/plugins/linux/sockstat.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 2e71dcd61..c133a400d 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -333,14 +333,13 @@ class Sockstat(plugins.PluginInterface): netns_id = net.proc_inum if net.has_member("proc_inum") else net.ns.inum yield task, netns_id, fd_num, family, sock_type, protocol, sock_fields - def _generator(self): - pids = self.config.get('pids') + def _generator(self, pids, netns_arg, symbol_table): filter_func = lsof.pslist.PsList.create_pid_filter(pids) + socket_generator = self.list_sockets(self.context, symbol_table, filter_func=filter_func) tasks_per_sock = {} - socket_generator = self.list_sockets(self.context, self.config['kernel'], filter_func=filter_func) for task, netns, fd_num, family, sock_type, protocol, sock_fields in socket_generator: - if self.config['netns'] and self.config['netns'] != netns: + if netns_arg and netns_arg != netns: continue sock, sock_stat, extended = sock_fields @@ -367,6 +366,10 @@ class Sockstat(plugins.PluginInterface): yield (0, fields) def run(self): + pids = self.config.get('pids') + netns = self.config['netns'] + symbol_table = self.config['kernel'] + tree_grid_args = [("NetNS", int), ("Family", str), ("Type", str), @@ -376,4 +379,4 @@ class Sockstat(plugins.PluginInterface): ("State", str), ("Tasks", str)] - return renderers.TreeGrid(tree_grid_args, self._generator()) + return renderers.TreeGrid(tree_grid_args, self._generator(pids, netns, symbol_table)) From f4e1f4729f5e1f049f69dd6a1d7e144ffa93b9fb Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 16:56:27 +1100 Subject: [PATCH 014/140] Added type annotations --- .../framework/plugins/linux/sockstat.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index c133a400d..e1fbfeddb 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -6,7 +6,7 @@ import logging from typing import Callable -from volatility3.framework import renderers, interfaces, exceptions, constants +from volatility3.framework import renderers, interfaces, exceptions, constants, objects from volatility3.framework.configuration import requirements from volatility3.framework.interfaces import plugins from volatility3.framework.objects import utility @@ -40,7 +40,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): "AF_BLUETOOTH": self._bluetooth_sock, } - def _build_network_devices_map(self, netns_id): + def _build_network_devices_map(self, netns_id: int): netdevices_map = {} nethead = self._vmlinux.object_from_symbol(symbol_name="net_namespace_list") net_symname = self._vmlinux.symbol_table_name + constants.BANG + "net" @@ -53,7 +53,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): netdevices_map[net_dev.ifindex] = dev_name return netdevices_map - def process_sock(self, sock): + def process_sock(self, sock: objects.StructType): family = sock.family extended = {} sock_handler = self._sock_family_handlers.get(family) @@ -76,7 +76,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): return sock, sock_stat, extended - def _unix_sock(self, sock, extended): + def _unix_sock(self, sock: objects.StructType, extended: dict): unix_sock = sock.cast("unix_sock") state = unix_sock.state saddr = unix_sock.name @@ -93,7 +93,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return unix_sock, sock_stat - def _inet_sock(self, sock, extended): + def _inet_sock(self, sock: objects.StructType, extended: dict): inet_sock = sock.cast("inet_sock") saddr = inet_sock.src_addr sport = inet_sock.src_port @@ -109,7 +109,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return inet_sock, sock_stat - def _netlink_sock(self, sock, extended): + def _netlink_sock(self, sock: objects.StructType, extended: dict): netlink_sock = sock.cast("netlink_sock") saddr_list = [] @@ -138,7 +138,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return netlink_sock, sock_stat - def _vsock_sock(self, sock, extended): + def _vsock_sock(self, sock: objects.StructType, extended: dict): vsock_sock = sock.cast("vsock_sock") saddr = vsock_sock.local_addr.svm_cid sport = vsock_sock.local_addr.svm_port @@ -151,7 +151,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return vsock_sock, sock_stat - def _packet_sock(self, sock, extended): + def _packet_sock(self, sock: objects.StructType, extended: dict): packet_sock = sock.cast("packet_sock") ifindex = packet_sock.ifindex dev_name = self._netdevices.get(ifindex, "") if ifindex > 0 else "ANY" @@ -170,7 +170,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return packet_sock, sock_stat - def _update_extended_socket_bpf(self, sock_filter, extended): + def _update_extended_socket_bpf(self, sock_filter: objects.Pointer, extended: dict): if not sock_filter: return @@ -193,7 +193,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): if bpfprog_name: extended["bpf_filter_name"] = bpfprog_name - def _xdp_sock(self, sock, extended): + def _xdp_sock(self, sock: objects.StructType, extended: dict): xdp_sock = sock.cast("xdp_sock") device = xdp_sock.dev if not device: @@ -222,7 +222,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return xdp_sock, sock_stat - def _bluetooth_sock(self, sock, extended): + def _bluetooth_sock(self, sock: objects.StructType, extended: dict): bt_sock = sock.cast("bt_sock") def bt_addr(addr): From 34176a80665dfb5af34955b1079af2f3b6c63b1f Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 17:10:45 +1100 Subject: [PATCH 015/140] Moving log lines from warning to LOGLEVEL_V --- volatility3/framework/plugins/linux/sockstat.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index e1fbfeddb..5f9b6fb07 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -63,9 +63,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): return *sock_fields, extended except exceptions.SymbolError as e: # Cannot finds the *_sock type in the symbols - vollog.warning("Error processing socket family '%s': %s", family, e) + vollog.log(constants.LOGLEVEL_V, "Error processing socket family '%s': %s", family, e) else: - vollog.warning("Unsupported family '%s'", family) + vollog.log(constants.LOGLEVEL_V, "Unsupported family '%s'", family) # Even if the sock family is not supported, or the required types # are not present in the symbols, we can still show some general @@ -245,7 +245,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): saddr_tag = f"[{src_addr}]:{channel}" daddr_tag = f"{dst_addr}" else: - vollog.warning("Unsupported bluetooth protocol '%s'", bt_sock.protocol) + vollog.log(constants.LOGLEVEL_V, "Unsupported bluetooth protocol '%s'", bt_sock.protocol) state = bt_sock.state sock_stat = saddr_tag, daddr_tag, state From a1ff8d7809545a69e8f780913d05da3d7e886d9b Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 17:25:58 +1100 Subject: [PATCH 016/140] Fix comment. This was related to netlink_sock not packer_sock --- volatility3/framework/symbols/linux/extensions/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 2af5f56b0..d551f3b02 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -766,7 +766,6 @@ class packet_sock(objects.StructType): @property def state(self): # Packet socket types are either SOCK_RAW or SOCK_DGRAM. - # NOTE: We are overriding netlink_sock.state here return "UNCONNECTED" From 7dc33b83145e1f0b243a6ab3f5261c62dee22966 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 17:28:38 +1100 Subject: [PATCH 017/140] fix typo --- volatility3/framework/symbols/linux/extensions/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index d551f3b02..3f286e1a3 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -748,7 +748,8 @@ class netlink_sock(objects.StructType): def state(self): # Netlink is a datagram-oriented service. We can only have # SOCK_RAW or SOCK_DGRAM socket types. - # NOTE: We are overridden the netlink_sock.state member here + # NOTE: We are overriding the netlink_sock.state member here + return "UNCONNECTED" From a7520a377dfa1e4aaea3c82f683adcbea032f5e0 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 21 Dec 2021 14:53:13 +1100 Subject: [PATCH 018/140] Supporting socket and reuseport filters in all the socket families. --- .../framework/plugins/linux/sockstat.py | 84 ++++++++++--------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 5f9b6fb07..323f02213 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -59,7 +59,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_handler = self._sock_family_handlers.get(family) if sock_handler: try: - sock_fields = sock_handler(sock, extended) + sock_fields = sock_handler(sock) + self._update_extended_socket_filters_info(sock, extended) + return *sock_fields, extended except exceptions.SymbolError as e: # Cannot finds the *_sock type in the symbols @@ -76,7 +78,42 @@ class SockHandlers(interfaces.configuration.VersionableInterface): return sock, sock_stat, extended - def _unix_sock(self, sock: objects.StructType, extended: dict): + def _update_extended_socket_filters_info(self, sock: objects.Pointer, extended: dict) -> None: + """Get infomation from the socket and reuseport filters + + Args: + sock: The kernel sock (sk) struct + extended: Dictionary to store extended information + """ + if sock.has_member("sk_filter") and sock.sk_filter: + sock_filter = sock.sk_filter + extended["filter_type"] = "socket_filter" + self._extract_socket_filter_info(sock_filter, extended) + + if sock.has_member("sk_reuseport_cb") and sock.sk_reuseport_cb: + sock_reuseport_cb = sock.sk_reuseport_cb + extended["filter_type"] = "reuseport_filter" + self._extract_socket_filter_info(sock_reuseport_cb, extended) + + def _extract_socket_filter_info(self, sock_filter: objects.Pointer, extended: dict): + extended["bpf_filter_type"] = "cBPF" + + if not sock_filter.has_member("prog") or not sock_filter.prog: + return + + bpfprog = sock_filter.prog + + # BPF_PROG_TYPE_UNSPEC = 0 + if bpfprog.type > 0: + extended["bpf_filter_type"] = "eBPF" + bpfprog_aux = bpfprog.aux + if bpfprog_aux: + extended["bpf_filter_id"] = str(bpfprog_aux.id) + bpfprog_name = utility.array_to_string(bpfprog_aux.name) + if bpfprog_name: + extended["bpf_filter_name"] = bpfprog_name + + def _unix_sock(self, sock: objects.StructType): unix_sock = sock.cast("unix_sock") state = unix_sock.state saddr = unix_sock.name @@ -93,7 +130,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return unix_sock, sock_stat - def _inet_sock(self, sock: objects.StructType, extended: dict): + def _inet_sock(self, sock: objects.StructType): inet_sock = sock.cast("inet_sock") saddr = inet_sock.src_addr sport = inet_sock.src_port @@ -109,7 +146,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return inet_sock, sock_stat - def _netlink_sock(self, sock: objects.StructType, extended: dict): + def _netlink_sock(self, sock: objects.StructType): netlink_sock = sock.cast("netlink_sock") saddr_list = [] @@ -138,7 +175,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return netlink_sock, sock_stat - def _vsock_sock(self, sock: objects.StructType, extended: dict): + def _vsock_sock(self, sock: objects.StructType): vsock_sock = sock.cast("vsock_sock") saddr = vsock_sock.local_addr.svm_cid sport = vsock_sock.local_addr.svm_port @@ -151,49 +188,18 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return vsock_sock, sock_stat - def _packet_sock(self, sock: objects.StructType, extended: dict): + def _packet_sock(self, sock: objects.StructType): packet_sock = sock.cast("packet_sock") ifindex = packet_sock.ifindex dev_name = self._netdevices.get(ifindex, "") if ifindex > 0 else "ANY" - if sock.has_member("sk_filter"): - sock_filter = sock.sk_filter - self._update_extended_socket_bpf(sock_filter, extended) - - if sock.has_member("sk_reuseport_cb"): - sock_reuseport_cb = sock.sk_reuseport_cb - self._update_extended_socket_bpf(sock_reuseport_cb, extended) - saddr_tag = f"{dev_name}" daddr_tag = "" state = packet_sock.state sock_stat = saddr_tag, daddr_tag, state return packet_sock, sock_stat - def _update_extended_socket_bpf(self, sock_filter: objects.Pointer, extended: dict): - if not sock_filter: - return - - extended["bpf_filter_type"] = "cBPF" - - if not sock_filter.has_member("prog"): - return - - bpfprog = sock_filter.prog - if not bpfprog: - return - - # BPF_PROG_TYPE_UNSPEC = 0 - if bpfprog.type > 0: - extended["bpf_filter_type"] = "eBPF" - bpfprog_aux = bpfprog.aux - if bpfprog_aux: - extended["bpf_filter_id"] = str(bpfprog_aux.id) - bpfprog_name = utility.array_to_string(bpfprog_aux.name) - if bpfprog_name: - extended["bpf_filter_name"] = bpfprog_name - - def _xdp_sock(self, sock: objects.StructType, extended: dict): + def _xdp_sock(self, sock: objects.StructType): xdp_sock = sock.cast("xdp_sock") device = xdp_sock.dev if not device: @@ -222,7 +228,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return xdp_sock, sock_stat - def _bluetooth_sock(self, sock: objects.StructType, extended: dict): + def _bluetooth_sock(self, sock: objects.StructType): bt_sock = sock.cast("bt_sock") def bt_addr(addr): From 7099a7a52ec3d4bab96bcd3e3f70d72abdb54221 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 21 Dec 2021 14:54:38 +1100 Subject: [PATCH 019/140] Fix. We should call the `net` type method here. --- volatility3/framework/plugins/linux/sockstat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 323f02213..4f9ac64de 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -336,7 +336,7 @@ class Sockstat(plugins.PluginInterface): protocol = child_sock.protocol if hasattr(child_sock, "protocol") else "" net = task.nsproxy.net_ns - netns_id = net.proc_inum if net.has_member("proc_inum") else net.ns.inum + netns_id = net.get_inode() yield task, netns_id, fd_num, family, sock_type, protocol, sock_fields def _generator(self, pids, netns_arg, symbol_table): From fe105dc4a707ae5631322fc75bfb2c441d8ae034 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 21 Dec 2021 15:01:29 +1100 Subject: [PATCH 020/140] Add doc strings and typing info everywhere. Improve some variable names --- .../framework/plugins/linux/sockstat.py | 160 +++++++++++++++--- 1 file changed, 139 insertions(+), 21 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 4f9ac64de..a73d5581a 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -4,7 +4,7 @@ # Author: Gustavo Moreira import logging -from typing import Callable +from typing import Callable, Tuple, List, Dict from volatility3.framework import renderers, interfaces, exceptions, constants, objects from volatility3.framework.configuration import requirements @@ -17,6 +17,7 @@ from volatility3.plugins.linux import lsof vollog = logging.getLogger(__name__) class SockHandlers(interfaces.configuration.VersionableInterface): + """Handles several socket families extracting the sockets information.""" _required_framework_version = (2, 0, 0) @@ -40,7 +41,17 @@ class SockHandlers(interfaces.configuration.VersionableInterface): "AF_BLUETOOTH": self._bluetooth_sock, } - def _build_network_devices_map(self, netns_id: int): + def _build_network_devices_map(self, netns_id: int) -> Dict: + """Given a namespace ID it returns a dictionary mapping each network + interface index (ifindex) to its network interface name: + + Args: + netns_id: The network namespace ID + + Returns: + netdevices_map: Mapping network interface index (ifindex) to network + interface name + """ netdevices_map = {} nethead = self._vmlinux.object_from_symbol(symbol_name="net_namespace_list") net_symname = self._vmlinux.symbol_table_name + constants.BANG + "net" @@ -53,7 +64,17 @@ class SockHandlers(interfaces.configuration.VersionableInterface): netdevices_map[net_dev.ifindex] = dev_name return netdevices_map - def process_sock(self, sock: objects.StructType): + def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str], Dict]: + """Takes a kernel generic `sock` object and processes it with its respective socket family + + Args: + sock: Kernel generic `sock` object + + Returns a tuple with: + sock: The respective kernel's *_sock object for that socket family + sock_stat: A tuple with the source, destination and state strings. + extended: A dictionary with key/value extended information. + """ family = sock.family extended = {} sock_handler = self._sock_family_handlers.get(family) @@ -95,7 +116,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): extended["filter_type"] = "reuseport_filter" self._extract_socket_filter_info(sock_reuseport_cb, extended) - def _extract_socket_filter_info(self, sock_filter: objects.Pointer, extended: dict): + def _extract_socket_filter_info(self, sock_filter: objects.Pointer, extended: dict) -> None: extended["bpf_filter_type"] = "cBPF" if not sock_filter.has_member("prog") or not sock_filter.prog: @@ -113,7 +134,16 @@ class SockHandlers(interfaces.configuration.VersionableInterface): if bpfprog_name: extended["bpf_filter_name"] = bpfprog_name - def _unix_sock(self, sock: objects.StructType): + def _unix_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + """Handles the AF_UNIX socket family + + Args: + sock: Kernel generic `sock` object + + Returns: + unix_sock: The kernel's `unix_sock` object + sock_stat: A tuple with the source, destination and state strings. + """ unix_sock = sock.cast("unix_sock") state = unix_sock.state saddr = unix_sock.name @@ -130,7 +160,16 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return unix_sock, sock_stat - def _inet_sock(self, sock: objects.StructType): + def _inet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + """Handles the AF_INET/6 socket families + + Args: + sock: Kernel generic `sock` object + + Returns: + inet_sock: The kernel's `inet_sock` object + sock_stat: A tuple with the source, destination and state strings. + """ inet_sock = sock.cast("inet_sock") saddr = inet_sock.src_addr sport = inet_sock.src_port @@ -146,7 +185,16 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return inet_sock, sock_stat - def _netlink_sock(self, sock: objects.StructType): + def _netlink_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + """Handles the AF_NETLINK socket family + + Args: + sock: Kernel generic `sock` object + + Returns: + netlink_sock: The kernel's `netlink_sock` object + sock_stat: A tuple with the source, destination and state strings. + """ netlink_sock = sock.cast("netlink_sock") saddr_list = [] @@ -175,7 +223,16 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return netlink_sock, sock_stat - def _vsock_sock(self, sock: objects.StructType): + def _vsock_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + """Handles the AF_VSOCK socket family + + Args: + sock: Kernel generic `sock` object + + Returns: + vsock_sock: The kernel `vsock_sock` object + sock_stat: A tuple with the source, destination and state strings. + """ vsock_sock = sock.cast("vsock_sock") saddr = vsock_sock.local_addr.svm_cid sport = vsock_sock.local_addr.svm_port @@ -188,7 +245,16 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return vsock_sock, sock_stat - def _packet_sock(self, sock: objects.StructType): + def _packet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + """Handles the AF_PACKET socket family + + Args: + sock: Kernel generic `sock` object + + Returns: + packet_sock: The kernel's `packet_sock` object + sock_stat: A tuple with the source, destination and state strings. + """ packet_sock = sock.cast("packet_sock") ifindex = packet_sock.ifindex dev_name = self._netdevices.get(ifindex, "") if ifindex > 0 else "ANY" @@ -199,7 +265,16 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return packet_sock, sock_stat - def _xdp_sock(self, sock: objects.StructType): + def _xdp_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + """Handles the AF_XDP socket family + + Args: + sock: Kernel generic `sock` object + + Returns: + xdp_sock: The kernel's `xdp_sock` object + sock_stat: A tuple with the source, destination and state strings. + """ xdp_sock = sock.cast("xdp_sock") device = xdp_sock.dev if not device: @@ -228,7 +303,16 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = saddr_tag, daddr_tag, state return xdp_sock, sock_stat - def _bluetooth_sock(self, sock: objects.StructType): + def _bluetooth_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + """Handles the AF_BLUETOOTH socket family + + Args: + sock: Kernel generic `sock` object + + Returns: + bt_sock: The kernel's `bt_sock` object + sock_stat: A tuple with the source, destination and state strings. + """ bt_sock = sock.cast("bt_sock") def bt_addr(addr): @@ -290,12 +374,26 @@ class Sockstat(plugins.PluginInterface): @classmethod def list_sockets(cls, context: interfaces.context.ContextInterface, - vmlinux_module_name: str, + symbol_table: str, filter_func: Callable[[int], bool] = lambda _: False): + """Returns every single socket descriptor + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + symbol_table: The name of the kernel module on which to operate + filter_func: A function which takes a task object and returns True if the task should be ignored/filtered + + Yields: + task: Kernel's task object + netns_id: Network namespace ID + fd_num: File descriptor number + family: Socket family string (AF_UNIX, AF_INET, etc) + sock_type: Socket type string (STREAM, DGRAM, etc) + protocol: Protocol string (UDP, TCP, etc) + sock_fields: A tuple with the *_sock object, the sock stats and the + extended info dictionary """ - Returns every single socket descriptor - """ - vmlinux = context.modules[vmlinux_module_name] + vmlinux = context.modules[symbol_table] sfop_addr = vmlinux.object_from_symbol("socket_file_ops").vol.offset dfop_addr = vmlinux.object_from_symbol("sockfs_dentry_operations").vol.offset @@ -339,13 +437,31 @@ class Sockstat(plugins.PluginInterface): netns_id = net.get_inode() yield task, netns_id, fd_num, family, sock_type, protocol, sock_fields - def _generator(self, pids, netns_arg, symbol_table): + def _generator(self, pids: List[int], netns_id_arg: int, symbol_table: str): + """Enumerate tasks sockets. Each row represents a kernel socket. + + Args: + pids: List of PIDs to filter. If a empty list or + netns_id_arg: If a network namespace ID is set, it will only show this namespace. + symbol_table: The name of the kernel module on which to operate + + Yields: + netns_id: Network namespace ID + family: Socket family string (AF_UNIX, AF_INET, etc) + sock_type: Socket type string (STREAM, DGRAM, etc) + protocol: Protocol string (UDP, TCP, etc) + source: Source address string + destination: Destination address string + state: State strings (LISTEN, CONNECTED, etc) + tasks: String with a list of tasks and FDs using a socket. It can also have + exteded information such as socket filters, bpf info, etc. + """ filter_func = lsof.pslist.PsList.create_pid_filter(pids) socket_generator = self.list_sockets(self.context, symbol_table, filter_func=filter_func) tasks_per_sock = {} - for task, netns, fd_num, family, sock_type, protocol, sock_fields in socket_generator: - if netns_arg and netns_arg != netns: + for task, netns_id, fd_num, family, sock_type, protocol, sock_fields in socket_generator: + if netns_id_arg and netns_id_arg != netns_id: continue sock, sock_stat, extended = sock_fields @@ -356,8 +472,10 @@ class Sockstat(plugins.PluginInterface): extended_str = ",".join(f"{k}={v}" for k, v in extended.items()) task_info = f"{task_info},{extended_str}" - fields = netns, family, sock_type, protocol, *sock_stat + fields = netns_id, family, sock_type, protocol, *sock_stat + # Each row represents a kernel socket, so let's group the task FDs + # by socket using the socket address sock_addr = sock.vol.offset tasks_per_sock.setdefault(sock_addr, {}) tasks_per_sock[sock_addr].setdefault('tasks', []) @@ -373,7 +491,7 @@ class Sockstat(plugins.PluginInterface): def run(self): pids = self.config.get('pids') - netns = self.config['netns'] + netns_id = self.config['netns'] symbol_table = self.config['kernel'] tree_grid_args = [("NetNS", int), @@ -385,4 +503,4 @@ class Sockstat(plugins.PluginInterface): ("State", str), ("Tasks", str)] - return renderers.TreeGrid(tree_grid_args, self._generator(pids, netns, symbol_table)) + return renderers.TreeGrid(tree_grid_args, self._generator(pids, netns_id, symbol_table)) From 8f8e04da97816a2f3236fdbd7782ad7f0c5b6020 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 21 Dec 2021 15:52:22 +1100 Subject: [PATCH 021/140] vfs_inode is not being used nor required --- volatility3/framework/plugins/linux/sockstat.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index a73d5581a..f526d614e 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -416,8 +416,7 @@ class Sockstat(plugins.PluginInterface): socket_alloc = linux.LinuxUtilities.container_of(d_inode, "socket_alloc", "vfs_inode", vmlinux) socket = socket_alloc.socket - vfs_inode = socket_alloc.vfs_inode - if not (socket and vfs_inode): + if not (socket and socket.sk): continue sock = socket.sk.dereference() From b40391ee1f8e5130f52c3716e8e181eedb912cc7 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Thu, 17 Feb 2022 11:19:58 +1100 Subject: [PATCH 022/140] Returning a starred expression is not yet supported in python 3.6. Fixed --- volatility3/framework/plugins/linux/sockstat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index f526d614e..0346cf7e4 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -80,10 +80,10 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_handler = self._sock_family_handlers.get(family) if sock_handler: try: - sock_fields = sock_handler(sock) + unix_sock, sock_stat = sock_handler(sock) self._update_extended_socket_filters_info(sock, extended) - return *sock_fields, extended + return unix_sock, sock_stat, extended except exceptions.SymbolError as e: # Cannot finds the *_sock type in the symbols vollog.log(constants.LOGLEVEL_V, "Error processing socket family '%s': %s", family, e) From 643a8cc74cada83ec5d6341298c31481a0e40ec4 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Sat, 26 Feb 2022 10:14:23 +1100 Subject: [PATCH 023/140] Make this method private using just a single leading underscore --- volatility3/framework/symbols/linux/extensions/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 3f286e1a3..c9ad48044 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -565,7 +565,7 @@ class net(objects.StructType): raise AttributeError("Unable to find net_namespace inode") class sock(objects.StructType): - def __get_vol_kernel_module_name(self): + def _get_vol_kernel_module_name(self): symbol_table_arr = self.vol.type_name.split("!", 1) symbol_table = symbol_table_arr[0] if len(symbol_table_arr) == 2 else None @@ -592,7 +592,7 @@ class sock(objects.StructType): if not self.sk_socket: return 0 - kernel_module_name = self.__get_vol_kernel_module_name() + kernel_module_name = self._get_vol_kernel_module_name() kernel = self._context.modules[kernel_module_name] socket_alloc = linux.LinuxUtilities.container_of(self.sk_socket, "socket_alloc", "socket", kernel) vfs_inode = socket_alloc.vfs_inode From 333bb090cd9700f346ea10ddc4635cc87732e0b3 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 29 Apr 2022 10:21:28 +1000 Subject: [PATCH 024/140] Minor. Constants regrouped --- volatility3/framework/constants/linux/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/constants/linux/__init__.py b/volatility3/framework/constants/linux/__init__.py index a35b6f02e..550690b90 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -12,6 +12,9 @@ KERNEL_NAME = "__kernel__" PAGE_SHIFT = 12 """The value hard coded from the Linux Kernel (hence not extracted from the layer itself)""" +# include/linux/sched.h +PF_KTHREAD = 0x00200000 # I'm a kernel thread + # Standard well-defined IP protocols. # ref: include/uapi/linux/in.h IP_PROTOCOLS = { @@ -227,6 +230,3 @@ BLUETOOTH_PROTOCOLS = ( "HIDP", "AVDTP", ) - -# include/linux/sched.h -PF_KTHREAD = 0x00200000 # I'm a kernel thread From a17d1617f4b0185edca858f5defa43027a945c62 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 29 Apr 2022 12:08:04 +1000 Subject: [PATCH 025/140] Remove author --- volatility3/framework/plugins/linux/sockstat.py | 1 - 1 file changed, 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 0346cf7e4..fd172fc43 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -1,7 +1,6 @@ # This file is Copyright 2021 Volatility Foundation and licensed under the Volatility Software License 1.0 # which is available at https://www.volatilityfoundation.org/license/vsl-v1.0 # -# Author: Gustavo Moreira import logging from typing import Callable, Tuple, List, Dict From 37d8328a18c6d7ddec5bfa74d1ba436351b8df35 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 29 Apr 2022 12:41:11 +1000 Subject: [PATCH 026/140] Adding typing information to container_of() --- .../framework/symbols/linux/__init__.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 739ecbedb..347d0b366 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -1,7 +1,7 @@ # 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 typing import List, Tuple, Iterator +from typing import List, Tuple, Iterator, Optional from volatility3 import framework from volatility3.framework import exceptions, constants, interfaces, objects @@ -283,9 +283,25 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): list_start = getattr(list_struct, list_member) @classmethod - def container_of(cls, addr, type_name, member_name, vmlinux): + def container_of( + cls, addr: int, type_name: str, member_name: str, vmlinux: interfaces.context.ModuleInterface + ) -> Optional[interfaces.objects.ObjectInterface]: + """Cast a member of a structure out to the containing structure. + It mimicks the Linux kernel macro container_of() see include/linux.kernel.h + + Args: + addr: The pointer to the member. + type_name: The type of the container struct this is embedded in. + member_name: The name of the member within the struct. + vmlinux: The kernel symbols object + + Returns: + The constructed object or None + """ + if not addr: return + type_dec = vmlinux.get_type(type_name) member_offset = type_dec.relative_child_offset(member_name) container_addr = addr - member_offset From 1679769da74163a6821c58b7352f5c0420578773 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 29 Apr 2022 12:58:32 +1000 Subject: [PATCH 027/140] Catch exception when it is unable to get the kernel module name --- volatility3/framework/symbols/linux/extensions/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 05a88ba5a..fe495f88d 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -634,7 +634,11 @@ class sock(objects.StructType): if not self.sk_socket: return 0 - kernel_module_name = self._get_vol_kernel_module_name() + try: + kernel_module_name = self._get_vol_kernel_module_name() + except ValueError: + return 0 + kernel = self._context.modules[kernel_module_name] socket_alloc = linux.LinuxUtilities.container_of(self.sk_socket, "socket_alloc", "socket", kernel) vfs_inode = socket_alloc.vfs_inode From f919d29c50a374836a2e934f1efdf2f4b119cc0d Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Sat, 30 Apr 2022 14:14:18 +1000 Subject: [PATCH 028/140] Changing properties for getters --- .../framework/plugins/linux/sockstat.py | 43 ++++++------ .../symbols/linux/extensions/__init__.py | 68 +++++++------------ 2 files changed, 46 insertions(+), 65 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index fd172fc43..7d4b20da0 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -74,7 +74,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat: A tuple with the source, destination and state strings. extended: A dictionary with key/value extended information. """ - family = sock.family + family = sock.get_family() extended = {} sock_handler = self._sock_family_handlers.get(family) if sock_handler: @@ -144,13 +144,13 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat: A tuple with the source, destination and state strings. """ unix_sock = sock.cast("unix_sock") - state = unix_sock.state - saddr = unix_sock.name - sinode = unix_sock.inode + state = unix_sock.get_state() + saddr = unix_sock.get_name() + sinode = unix_sock.get_inode() if unix_sock.peer != 0: peer = unix_sock.peer.dereference().cast("unix_sock") - daddr = peer.name - dinode = peer.inode + daddr = peer.get_name() + dinode = peer.get_inode() else: daddr = dinode = "" @@ -170,13 +170,13 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat: A tuple with the source, destination and state strings. """ inet_sock = sock.cast("inet_sock") - saddr = inet_sock.src_addr - sport = inet_sock.src_port - daddr = inet_sock.dst_addr - dport = inet_sock.dst_port - state = inet_sock.state + saddr = inet_sock.get_src_addr() + sport = inet_sock.get_src_port() + daddr = inet_sock.get_dst_addr() + dport = inet_sock.get_dst_port() + state = inet_sock.get_state() - if inet_sock.family == "AF_INET6": + if inet_sock.get_family() == "AF_INET6": saddr = f"[{saddr}]" saddr_tag = f"{saddr}:{sport}" @@ -217,7 +217,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): saddr_tag = ",".join(saddr_list) daddr_tag = ",".join(daddr_list) - state = netlink_sock.state + state = netlink_sock.get_state() sock_stat = saddr_tag, daddr_tag, state return netlink_sock, sock_stat @@ -260,7 +260,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): saddr_tag = f"{dev_name}" daddr_tag = "" - state = packet_sock.state + state = packet_sock.get_state() sock_stat = saddr_tag, daddr_tag, state return packet_sock, sock_stat @@ -318,15 +318,16 @@ class SockHandlers(interfaces.configuration.VersionableInterface): return ":".join(reversed(["%02x" % x for x in addr.b])) saddr_tag = daddr_tag = "" - if bt_sock.protocol == "HCI": + bt_protocol = bt_sock.get_protocol() + if bt_protocol == "HCI": pinfo = bt_sock.cast("hci_pinfo") - elif bt_sock.protocol == "L2CAP": + elif bt_protocol == "L2CAP": pinfo = bt_sock.cast("l2cap_pinfo") src_addr = bt_addr(pinfo.chan.src) dst_addr = bt_addr(pinfo.chan.dst) saddr_tag = f"{src_addr}" daddr_tag = f"{dst_addr}" - elif bt_sock.protocol == "RFCOMM": + elif bt_protocol == "RFCOMM": pinfo = bt_sock.cast("rfcomm_pinfo") src_addr = bt_addr(pinfo.src) dst_addr = bt_addr(pinfo.dst) @@ -334,7 +335,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): saddr_tag = f"[{src_addr}]:{channel}" daddr_tag = f"{dst_addr}" else: - vollog.log(constants.LOGLEVEL_V, "Unsupported bluetooth protocol '%s'", bt_sock.protocol) + vollog.log(constants.LOGLEVEL_V, "Unsupported bluetooth protocol '%s'", bt_protocol) state = bt_sock.state sock_stat = saddr_tag, daddr_tag, state @@ -420,8 +421,8 @@ class Sockstat(plugins.PluginInterface): sock = socket.sk.dereference() - sock_type = sock.type - family = sock.family + sock_type = sock.get_type() + family = sock.get_family() sock_handler = SockHandlers(vmlinux, task) sock_fields = sock_handler.process_sock(sock) @@ -429,7 +430,7 @@ class Sockstat(plugins.PluginInterface): continue child_sock = sock_fields[0] - protocol = child_sock.protocol if hasattr(child_sock, "protocol") else "" + protocol = child_sock.get_protocol() net = task.nsproxy.net_ns netns_id = net.get_inode() diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index fe495f88d..80180ad1b 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -617,20 +617,17 @@ class sock(objects.StructType): return module_names[0] - @property - def family(self): + def get_family(self): family_idx = self.__sk_common.skc_family if 0 <= family_idx < len(SOCK_FAMILY): return SOCK_FAMILY[family_idx] else: return "UNKNOWN" - @property - def type(self): + def get_type(self): return SOCK_TYPES.get(self.sk_type, "") - @property - def inode(self): + def get_inode(self): if not self.sk_socket: return 0 @@ -646,8 +643,7 @@ class sock(objects.StructType): return vfs_inode.i_ino class unix_sock(objects.StructType): - @property - def name(self): + def get_name(self): if self.addr: sockaddr_un = self.addr.name.cast("sockaddr_un") saddr = str(utility.array_to_string(sockaddr_un.sun_path)) @@ -655,16 +651,14 @@ class unix_sock(objects.StructType): saddr = "" return saddr - @property - def protocol(self): + def get_protocol(self): return "" - @property - def state(self): + def get_state(self): """Return a string representing the sock state.""" # Unix socket states reuse (a subset) of the inet_sock states contants - if self.sk.type == "STREAM": + if self.sk.get_type() == "STREAM": state_idx = self.sk.__sk_common.skc_state if 0 <= state_idx < len(TCP_STATES): state = TCP_STATES[state_idx] @@ -675,33 +669,29 @@ class unix_sock(objects.StructType): return state - @property - def inode(self): - return self.sk.inode + def get_inode(self): + return self.sk.get_inode() class inet_sock(objects.StructType): - @property - def family(self): + def get_family(self): family_idx = self.sk.__sk_common.skc_family if 0 <= family_idx < len(SOCK_FAMILY): return SOCK_FAMILY[family_idx] else: return "UNKNOWN" - @property - def protocol(self): + def get_protocol(self): # If INET6 family and a proto is defined, we use that specific IPv6 protocol. # Otherwise, we use the standard IP protocol. protocol = IP_PROTOCOLS.get(self.sk.sk_protocol, "UNKNOWN") - if self.family == "AF_INET6": + if self.get_family() == "AF_INET6": protocol = IPV6_PROTOCOLS.get(self.sk.sk_protocol, protocol) return protocol - @property - def state(self): + def get_state(self): """Return a string representing the sock state.""" - if self.sk.type == "STREAM": + if self.sk.get_type() == "STREAM": state_idx = self.sk.__sk_common.skc_state if 0 <= state_idx < len(TCP_STATES): state = TCP_STATES[state_idx] @@ -712,14 +702,12 @@ class inet_sock(objects.StructType): return state - @property - def src_port(self): + def get_src_port(self): sport_le = getattr(self, "sport", getattr(self, "inet_sport", None)) if sport_le is not None: return socket.htons(sport_le) - @property - def dst_port(self): + def get_dst_port(self): sk_common = self.sk.__sk_common if hasattr(sk_common, "skc_portpair"): dport_le = sk_common.skc_portpair & 0xffff @@ -734,8 +722,7 @@ class inet_sock(objects.StructType): return socket.htons(dport_le) - @property - def src_addr(self): + def get_src_addr(self): sk_common = self.sk.__sk_common family = sk_common.skc_family if family == socket.AF_INET: @@ -756,8 +743,7 @@ class inet_sock(objects.StructType): addr_bytes = parent_layer.read(saddr.vol.offset, addr_size) return socket.inet_ntop(family, addr_bytes) - @property - def dst_addr(self): + def get_dst_addr(self): sk_common = self.sk.__sk_common family = sk_common.skc_family if family == socket.AF_INET: @@ -782,16 +768,14 @@ class inet_sock(objects.StructType): return socket.inet_ntop(family, addr_bytes) class netlink_sock(objects.StructType): - @property - def protocol(self): + def get_protocol(self): protocol_idx = self.sk.sk_protocol if 0 <= protocol_idx < len(NETLINK_PROTOCOLS): return NETLINK_PROTOCOLS[protocol_idx] else: return "UNKNOWN" - @property - def state(self): + def get_state(self): # Netlink is a datagram-oriented service. We can only have # SOCK_RAW or SOCK_DGRAM socket types. # NOTE: We are overriding the netlink_sock.state member here @@ -800,8 +784,7 @@ class netlink_sock(objects.StructType): class packet_sock(objects.StructType): - @property - def protocol(self): + def get_protocol(self): eth_proto = socket.htons(self.num) if eth_proto == 0: return "" @@ -810,15 +793,13 @@ class packet_sock(objects.StructType): else: return f"0x{eth_proto:x}" - @property - def state(self): + def get_state(self): # Packet socket types are either SOCK_RAW or SOCK_DGRAM. return "UNCONNECTED" class bt_sock(objects.StructType): - @property - def protocol(self): + def get_protocol(self): type_idx = self.sk.sk_protocol if 0 <= type_idx < len(BLUETOOTH_PROTOCOLS): state = BLUETOOTH_PROTOCOLS[type_idx] @@ -827,8 +808,7 @@ class bt_sock(objects.StructType): return state - @property - def state(self): + def get_state(self): state_idx = self.sk.__sk_common.skc_state if 0 <= state_idx < len(BLUETOOTH_STATES): state = BLUETOOTH_STATES[state_idx] From 5f70c7031c107645be93a71d392177c123c60307 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Sat, 30 Apr 2022 14:29:52 +1000 Subject: [PATCH 029/140] Added kernel 'socket' and 'vsock_sock' struct extensions. This removes hardcoding state in same socket families, taking the information directly from the generic 'socket'. --- .../framework/constants/linux/__init__.py | 10 ++ .../framework/plugins/linux/sockstat.py | 7 +- .../framework/symbols/linux/__init__.py | 2 + .../symbols/linux/extensions/__init__.py | 114 ++++++++++-------- 4 files changed, 83 insertions(+), 50 deletions(-) diff --git a/volatility3/framework/constants/linux/__init__.py b/volatility3/framework/constants/linux/__init__.py index 550690b90..0c4d3c376 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -137,6 +137,16 @@ SOCK_FAMILY = ( "AF_XDP", ) +# Socket states +# ref: include/uapi/linux/net.h +SOCKET_STATES = ( + "FREE", + "UNCONNECTED", + "CONNECTING", + "CONNECTED", + "DISCONNECTING" +) + # Netlink protocols # ref: include/uapi/linux/netlink.h NETLINK_PROTOCOLS = ( diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 7d4b20da0..0306bec02 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -92,7 +92,8 @@ class SockHandlers(interfaces.configuration.VersionableInterface): # Even if the sock family is not supported, or the required types # are not present in the symbols, we can still show some general # information about the socket that may be helpful. - saddr_tag = daddr_tag = state = "?" + saddr_tag = daddr_tag = "?" + state = sock.get_state() sock_stat = saddr_tag, daddr_tag, state @@ -237,7 +238,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sport = vsock_sock.local_addr.svm_port daddr = vsock_sock.remote_addr.svm_cid dport = vsock_sock.remote_addr.svm_port - state = "" # Protocol is always 0 + state = vsock_sock.get_state() saddr_tag = f"{saddr}:{sport}" daddr_tag = f"{daddr}:{dport}" @@ -337,7 +338,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): else: vollog.log(constants.LOGLEVEL_V, "Unsupported bluetooth protocol '%s'", bt_protocol) - state = bt_sock.state + state = bt_sock.get_state() sock_stat = saddr_tag, daddr_tag, state return bt_sock, sock_stat diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 347d0b366..977066eac 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -32,10 +32,12 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): # Network self.set_type_class('net', extensions.net) + self.set_type_class('socket', extensions.socket) self.set_type_class('sock', extensions.sock) self.set_type_class('inet_sock', extensions.inet_sock) self.set_type_class('unix_sock', extensions.unix_sock) self.set_type_class('netlink_sock', extensions.netlink_sock) + self.set_type_class('vsock_sock', extensions.vsock_sock) self.set_type_class('packet_sock', extensions.packet_sock) if 'bt_sock' in self.types: self.set_type_class('bt_sock', extensions.bt_sock) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 80180ad1b..644a88632 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -4,7 +4,7 @@ import collections.abc import logging -import socket +import socket as socket_module from typing import Generator, Iterable, Iterator, Optional, Tuple from volatility3.framework import constants @@ -12,7 +12,7 @@ from volatility3.framework.constants.linux import SOCK_TYPES, SOCK_FAMILY from volatility3.framework.constants.linux import IP_PROTOCOLS, IPV6_PROTOCOLS from volatility3.framework.constants.linux import TCP_STATES, NETLINK_PROTOCOLS from volatility3.framework.constants.linux import ETH_PROTOCOLS, BLUETOOTH_STATES -from volatility3.framework.constants.linux import BLUETOOTH_PROTOCOLS +from volatility3.framework.constants.linux import BLUETOOTH_PROTOCOLS, SOCKET_STATES from volatility3.framework import exceptions, objects, interfaces, symbols from volatility3.framework.layers import linear from volatility3.framework.objects import utility @@ -606,8 +606,8 @@ class net(objects.StructType): else: raise AttributeError("Unable to find net_namespace inode") -class sock(objects.StructType): - def _get_vol_kernel_module_name(self): +class socket(objects.StructType): + def _get_vol_kernel(self): symbol_table_arr = self.vol.type_name.split("!", 1) symbol_table = symbol_table_arr[0] if len(symbol_table_arr) == 2 else None @@ -615,8 +615,29 @@ class sock(objects.StructType): if not module_names: raise ValueError(f"No module using the symbol table {symbol_table}") - return module_names[0] + kernel_module_name = module_names[0] + kernel = self._context.modules[kernel_module_name] + return kernel + def get_inode(self): + try: + kernel = self._get_vol_kernel() + except ValueError: + return 0 + + socket_alloc = linux.LinuxUtilities.container_of(self.vol.offset, "socket_alloc", "socket", kernel) + vfs_inode = socket_alloc.vfs_inode + + return vfs_inode.i_ino + + def get_state(self): + socket_state_idx = self.state + if 0 <= socket_state_idx < len(SOCKET_STATES): + return SOCKET_STATES[socket_state_idx] + else: + return "UNKNOWN" + +class sock(objects.StructType): def get_family(self): family_idx = self.__sk_common.skc_family if 0 <= family_idx < len(SOCK_FAMILY): @@ -631,16 +652,11 @@ class sock(objects.StructType): if not self.sk_socket: return 0 - try: - kernel_module_name = self._get_vol_kernel_module_name() - except ValueError: - return 0 + return self.sk_socket.get_inode() - kernel = self._context.modules[kernel_module_name] - socket_alloc = linux.LinuxUtilities.container_of(self.sk_socket, "socket_alloc", "socket", kernel) - vfs_inode = socket_alloc.vfs_inode - - return vfs_inode.i_ino + def get_state(self): + # Return the generic socket state + return self.sk.sk_socket.get_state() class unix_sock(objects.StructType): def get_name(self): @@ -661,13 +677,12 @@ class unix_sock(objects.StructType): if self.sk.get_type() == "STREAM": state_idx = self.sk.__sk_common.skc_state if 0 <= state_idx < len(TCP_STATES): - state = TCP_STATES[state_idx] + return TCP_STATES[state_idx] else: - state = "UNKNOWN" + return "UNKNOWN" else: - state = "UNCONNECTED" - - return state + # Return the generic socket state + return self.sk.sk_socket.get_state() def get_inode(self): return self.sk.get_inode() @@ -694,18 +709,17 @@ class inet_sock(objects.StructType): if self.sk.get_type() == "STREAM": state_idx = self.sk.__sk_common.skc_state if 0 <= state_idx < len(TCP_STATES): - state = TCP_STATES[state_idx] + return TCP_STATES[state_idx] else: - state = "UNKNOWN" + return "UNKNOWN" else: - state = "UNCONNECTED" - - return state + # Return the generic socket state + return self.sk.sk_socket.get_state() def get_src_port(self): sport_le = getattr(self, "sport", getattr(self, "inet_sport", None)) if sport_le is not None: - return socket.htons(sport_le) + return socket_module.htons(sport_le) def get_dst_port(self): sk_common = self.sk.__sk_common @@ -720,12 +734,12 @@ class inet_sock(objects.StructType): else: return - return socket.htons(dport_le) + return socket_module.htons(dport_le) def get_src_addr(self): sk_common = self.sk.__sk_common family = sk_common.skc_family - if family == socket.AF_INET: + if family == socket_module.AF_INET: addr_size = 4 if hasattr(self, "rcv_saddr"): saddr = self.rcv_saddr @@ -733,7 +747,7 @@ class inet_sock(objects.StructType): saddr = self.inet_rcv_saddr else: saddr = sk_common.skc_rcv_saddr - elif family == socket.AF_INET6: + elif family == socket_module.AF_INET6: addr_size = 16 saddr = self.pinet6.saddr else: @@ -741,12 +755,12 @@ class inet_sock(objects.StructType): parent_layer = self._context.layers[self.vol.layer_name] addr_bytes = parent_layer.read(saddr.vol.offset, addr_size) - return socket.inet_ntop(family, addr_bytes) + return socket_module.inet_ntop(family, addr_bytes) def get_dst_addr(self): sk_common = self.sk.__sk_common family = sk_common.skc_family - if family == socket.AF_INET: + if family == socket_module.AF_INET: if hasattr(self, "daddr") and self.daddr: daddr = self.daddr elif hasattr(self, "inet_daddr") and self.inet_daddr: @@ -754,7 +768,7 @@ class inet_sock(objects.StructType): else: daddr = sk_common.skc_daddr addr_size = 4 - elif family == socket.AF_INET6: + elif family == socket_module.AF_INET6: if hasattr(self.pinet6, "daddr"): daddr = self.pinet6.daddr else: @@ -765,7 +779,7 @@ class inet_sock(objects.StructType): parent_layer = self._context.layers[self.vol.layer_name] addr_bytes = parent_layer.read(daddr.vol.offset, addr_size) - return socket.inet_ntop(family, addr_bytes) + return socket_module.inet_ntop(family, addr_bytes) class netlink_sock(objects.StructType): def get_protocol(self): @@ -776,16 +790,26 @@ class netlink_sock(objects.StructType): return "UNKNOWN" def get_state(self): - # Netlink is a datagram-oriented service. We can only have - # SOCK_RAW or SOCK_DGRAM socket types. - # NOTE: We are overriding the netlink_sock.state member here + # Return the generic socket state + return self.sk.sk_socket.get_state() - return "UNCONNECTED" + +class vsock_sock(objects.StructType): + def get_protocol(self): + # The protocol should always be 0 for vsocks + if self.sk.sk_protocol == 0: + return "" + else: + return "UNKNOWN" + + def get_state(self): + # Return the generic socket state + return self.sk.sk_socket.get_state() class packet_sock(objects.StructType): def get_protocol(self): - eth_proto = socket.htons(self.num) + eth_proto = socket_module.htons(self.num) if eth_proto == 0: return "" elif eth_proto in ETH_PROTOCOLS: @@ -794,25 +818,21 @@ class packet_sock(objects.StructType): return f"0x{eth_proto:x}" def get_state(self): - # Packet socket types are either SOCK_RAW or SOCK_DGRAM. - return "UNCONNECTED" + # Return the generic socket state + return self.sk.sk_socket.get_state() class bt_sock(objects.StructType): def get_protocol(self): type_idx = self.sk.sk_protocol if 0 <= type_idx < len(BLUETOOTH_PROTOCOLS): - state = BLUETOOTH_PROTOCOLS[type_idx] + return BLUETOOTH_PROTOCOLS[type_idx] else: - state = "UNKNOWN" - - return state + return "UNKNOWN" def get_state(self): state_idx = self.sk.__sk_common.skc_state if 0 <= state_idx < len(BLUETOOTH_STATES): - state = BLUETOOTH_STATES[state_idx] + return BLUETOOTH_STATES[state_idx] else: - state = "UNKNOWN" - - return state + return "UNKNOWN" From 9b0b2547d5a69b5ba78416fa3c858a41489e4dde Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Sat, 30 Apr 2022 14:37:51 +1000 Subject: [PATCH 030/140] Manage invalid address exception when reading src and dst addresses --- .../framework/symbols/linux/extensions/__init__.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 644a88632..53ad48942 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -754,7 +754,12 @@ class inet_sock(objects.StructType): return parent_layer = self._context.layers[self.vol.layer_name] - addr_bytes = parent_layer.read(saddr.vol.offset, addr_size) + try: + addr_bytes = parent_layer.read(saddr.vol.offset, addr_size) + except exceptions.InvalidAddressException: + vollog.debug(f"Unable to read socket src address from {saddr.vol.offset:#x}") + return "?" + return socket_module.inet_ntop(family, addr_bytes) def get_dst_addr(self): @@ -778,7 +783,12 @@ class inet_sock(objects.StructType): return parent_layer = self._context.layers[self.vol.layer_name] - addr_bytes = parent_layer.read(daddr.vol.offset, addr_size) + try: + addr_bytes = parent_layer.read(daddr.vol.offset, addr_size) + except exceptions.InvalidAddressException: + vollog.debug(f"Unable to read socket dst address from {daddr.vol.offset:#x}") + return "?" + return socket_module.inet_ntop(family, addr_bytes) class netlink_sock(objects.StructType): From db9f287cb146052d28ad75f1b9787b0131b46e8a Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Sat, 30 Apr 2022 15:25:55 +1000 Subject: [PATCH 031/140] Unrelated to this PR. Removed unused import. --- volatility3/framework/objects/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/objects/__init__.py b/volatility3/framework/objects/__init__.py index e0f927ec9..e91b0cd4e 100644 --- a/volatility3/framework/objects/__init__.py +++ b/volatility3/framework/objects/__init__.py @@ -9,7 +9,7 @@ import struct from typing import Any, ClassVar, Dict, Iterable, List, Optional, Tuple, Type, Union as TUnion, overload from volatility3.framework import constants, interfaces -from volatility3.framework.objects import templates, utility +from volatility3.framework.objects import templates vollog = logging.getLogger(__name__) From dc31ae1ddadf6d4dcbcf4bfd34bc0354cc16febe Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Tue, 13 Sep 2022 15:21:24 +0300 Subject: [PATCH 032/140] Added new containing address flag to vadinfo plugin --- volatility3/framework/plugins/windows/vadinfo.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/volatility3/framework/plugins/windows/vadinfo.py b/volatility3/framework/plugins/windows/vadinfo.py index e357b150a..3c51263b7 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -52,6 +52,10 @@ class VadInfo(interfaces.plugins.PluginInterface): "(all other address ranges are excluded). This must be " \ "a base address, not an address within the desired range.", optional = True), + requirements.IntRequirement(name='containing-address', + description="Process virtual memory address to include" \ + "This is a containing address in the VAD.", + optional=True), requirements.ListRequirement(name = 'pid', description = 'Filter on specific process IDs', element_type = int, @@ -179,6 +183,13 @@ class VadInfo(interfaces.plugins.PluginInterface): filter_func = filter_function + if self.config.get('containing-address', None) is not None: + + def containing_filter_function(x: interfaces.objects.ObjectInterface) -> bool: + return not (x.get_start() <= self.config['containing-address'] <= x.get_end()) + + filter_func = containing_filter_function + for proc in procs: process_name = utility.array_to_string(proc.ImageFileName) From d9c434da3ff0425551e0fca530d8d63f6db689ec Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Wed, 28 Sep 2022 11:19:46 +0300 Subject: [PATCH 033/140] Removed extra flag --- volatility3/framework/plugins/windows/vadinfo.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/volatility3/framework/plugins/windows/vadinfo.py b/volatility3/framework/plugins/windows/vadinfo.py index 3c51263b7..dc7e4dff4 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -49,13 +49,8 @@ class VadInfo(interfaces.plugins.PluginInterface): # TODO: Convert this to a ListRequirement so that people can filter on sets of ranges requirements.IntRequirement(name = 'address', description = "Process virtual memory address to include " \ - "(all other address ranges are excluded). This must be " \ - "a base address, not an address within the desired range.", + "(all other address ranges are excluded).", optional = True), - requirements.IntRequirement(name='containing-address', - description="Process virtual memory address to include" \ - "This is a containing address in the VAD.", - optional=True), requirements.ListRequirement(name = 'pid', description = 'Filter on specific process IDs', element_type = int, @@ -179,17 +174,10 @@ class VadInfo(interfaces.plugins.PluginInterface): if self.config.get('address', None) is not None: def filter_function(x: interfaces.objects.ObjectInterface) -> bool: - return x.get_start() not in [self.config['address']] + return not (x.get_start() <= self.config['address'] <= x.get_end()) filter_func = filter_function - if self.config.get('containing-address', None) is not None: - - def containing_filter_function(x: interfaces.objects.ObjectInterface) -> bool: - return not (x.get_start() <= self.config['containing-address'] <= x.get_end()) - - filter_func = containing_filter_function - for proc in procs: process_name = utility.array_to_string(proc.ImageFileName) From 1f185d0ee2772bfa77fb2bfc60719aca42933b2f Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 28 Oct 2022 16:51:03 +1100 Subject: [PATCH 034/140] Minor fix comment typo --- volatility3/framework/plugins/linux/sockstat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 0306bec02..29be88309 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -100,7 +100,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): return sock, sock_stat, extended def _update_extended_socket_filters_info(self, sock: objects.Pointer, extended: dict) -> None: - """Get infomation from the socket and reuseport filters + """Get information from the socket and reuseport filters Args: sock: The kernel sock (sk) struct @@ -454,7 +454,7 @@ class Sockstat(plugins.PluginInterface): destination: Destination address string state: State strings (LISTEN, CONNECTED, etc) tasks: String with a list of tasks and FDs using a socket. It can also have - exteded information such as socket filters, bpf info, etc. + extended information such as socket filters, bpf info, etc. """ filter_func = lsof.pslist.PsList.create_pid_filter(pids) socket_generator = self.list_sockets(self.context, symbol_table, filter_func=filter_func) From dfadf5376a0a61a0dc7701014527303cd0d1ad63 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 28 Oct 2022 20:46:08 +1100 Subject: [PATCH 035/140] Fix issue with AF_XDP socket family, issues with older kernel versions and other fixes and improvements --- .../framework/plugins/linux/sockstat.py | 31 ++++++++++++------- .../framework/symbols/linux/__init__.py | 30 +++++++++--------- .../symbols/linux/extensions/__init__.py | 26 ++++++++++++---- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 29be88309..6d50c296d 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -5,7 +5,8 @@ import logging from typing import Callable, Tuple, List, Dict -from volatility3.framework import renderers, interfaces, exceptions, constants, objects +from volatility3.framework import interfaces, exceptions, constants, objects +from volatility3.framework.renderers import TreeGrid, NotAvailableValue from volatility3.framework.configuration import requirements from volatility3.framework.interfaces import plugins from volatility3.framework.objects import utility @@ -92,7 +93,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): # Even if the sock family is not supported, or the required types # are not present in the symbols, we can still show some general # information about the socket that may be helpful. - saddr_tag = daddr_tag = "?" + saddr_tag = daddr_tag = NotAvailableValue() state = sock.get_state() sock_stat = saddr_tag, daddr_tag, state @@ -123,16 +124,22 @@ class SockHandlers(interfaces.configuration.VersionableInterface): return bpfprog = sock_filter.prog + if bpfprog.type == 0: + # BPF_PROG_TYPE_UNSPEC = 0 + return - # BPF_PROG_TYPE_UNSPEC = 0 - if bpfprog.type > 0: - extended["bpf_filter_type"] = "eBPF" - bpfprog_aux = bpfprog.aux - if bpfprog_aux: - extended["bpf_filter_id"] = str(bpfprog_aux.id) - bpfprog_name = utility.array_to_string(bpfprog_aux.name) - if bpfprog_name: - extended["bpf_filter_name"] = bpfprog_name + extended["bpf_filter_type"] = "eBPF" + if not bpfprog.has_member("aux") or not bpfprog.aux: + return + bpfprog_aux = bpfprog.aux + if bpfprog_aux.has_member("id"): + # `id` member was added to `bpf_prog_aux` in kernels 4.13 + extended["bpf_filter_id"] = str(bpfprog_aux.id) + if bpfprog_aux.has_member("name"): + # `name` was added to `bpf_prog_aux` in kernels 4.15 + bpfprog_name = utility.array_to_string(bpfprog_aux.name) + if bpfprog_name: + extended["bpf_filter_name"] = bpfprog_name def _unix_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: """Handles the AF_UNIX socket family @@ -503,4 +510,4 @@ class Sockstat(plugins.PluginInterface): ("State", str), ("Tasks", str)] - return renderers.TreeGrid(tree_grid_args, self._generator(pids, netns_id, symbol_table)) + return TreeGrid(tree_grid_args, self._generator(pids, netns_id, symbol_table)) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index bd8748ec8..1945bb3ef 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -27,8 +27,15 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class('dentry', extensions.dentry) self.set_type_class('fs_struct', extensions.fs_struct) self.set_type_class('files_struct', extensions.files_struct) - self.set_type_class('vfsmount', extensions.vfsmount) self.set_type_class('kobject', extensions.kobject) + # Might not exist in the current symbols + self.optional_set_type_class('module', extensions.module) + + # Mount + self.set_type_class('vfsmount', extensions.vfsmount) + # Might not exist in older kernels or the current symbols + self.optional_set_type_class('mount', extensions.mount) + self.optional_set_type_class('mnt_namespace', extensions.mnt_namespace) # Network self.set_type_class('net', extensions.net) @@ -36,21 +43,12 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class('sock', extensions.sock) self.set_type_class('inet_sock', extensions.inet_sock) self.set_type_class('unix_sock', extensions.unix_sock) - self.set_type_class('netlink_sock', extensions.netlink_sock) - self.set_type_class('vsock_sock', extensions.vsock_sock) - self.set_type_class('packet_sock', extensions.packet_sock) - - if 'bt_sock' in self.types: - self.set_type_class('bt_sock', extensions.bt_sock) - - if 'mnt_namespace' in self.types: - self.set_type_class('mnt_namespace', extensions.mnt_namespace) - - if 'module' in self.types: - self.set_type_class('module', extensions.module) - - if 'mount' in self.types: - self.set_type_class('mount', extensions.mount) + # Might not exist in older kernels or the current symbols + self.optional_set_type_class('netlink_sock', extensions.netlink_sock) + self.optional_set_type_class('vsock_sock', extensions.vsock_sock) + self.optional_set_type_class('packet_sock', extensions.packet_sock) + self.optional_set_type_class('bt_sock', extensions.bt_sock) + self.optional_set_type_class('xdp_sock', extensions.xdp_sock) class LinuxUtilities(interfaces.configuration.VersionableInterface): diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 27fa00a8c..d193200c4 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -16,8 +16,7 @@ from volatility3.framework.constants.linux import BLUETOOTH_PROTOCOLS, SOCKET_ST from volatility3.framework import exceptions, objects, interfaces, symbols from volatility3.framework.layers import linear from volatility3.framework.objects import utility -from volatility3.framework.symbols import generic, linux -from volatility3.framework.symbols import intermed +from volatility3.framework.symbols import generic, linux, intermed from volatility3.framework.symbols.linux.extensions import elf vollog = logging.getLogger(__name__) @@ -840,9 +839,15 @@ class sock(objects.StructType): return self.sk_socket.get_inode() + def get_protocol(self): + return "" + def get_state(self): # Return the generic socket state - return self.sk.sk_socket.get_state() + if self.has_member("sk"): + return self.sk.sk_socket.get_state() + + return self.sk_socket.get_state() class unix_sock(objects.StructType): def get_name(self): @@ -989,7 +994,6 @@ class netlink_sock(objects.StructType): # Return the generic socket state return self.sk.sk_socket.get_state() - class vsock_sock(objects.StructType): def get_protocol(self): # The protocol should always be 0 for vsocks @@ -1002,7 +1006,6 @@ class vsock_sock(objects.StructType): # Return the generic socket state return self.sk.sk_socket.get_state() - class packet_sock(objects.StructType): def get_protocol(self): eth_proto = socket_module.htons(self.num) @@ -1017,7 +1020,6 @@ class packet_sock(objects.StructType): # Return the generic socket state return self.sk.sk_socket.get_state() - class bt_sock(objects.StructType): def get_protocol(self): type_idx = self.sk.sk_protocol @@ -1032,3 +1034,15 @@ class bt_sock(objects.StructType): return BLUETOOTH_STATES[state_idx] else: return "UNKNOWN" + +class xdp_sock(objects.StructType): + def get_protocol(self): + # The protocol should always be 0 for xdp_sock + if self.sk.sk_protocol == 0: + return "" + else: + return "UNKNOWN" + + def get_state(self): + # Return the generic socket state + return self.sk.sk_socket.get_state() From 017fcc05f35314e180c172504c9a176b3a66551b Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Sat, 29 Oct 2022 12:55:16 +1100 Subject: [PATCH 036/140] Split address and port. Fix potential issues in xdp_sock(s) with older kernels. Postpone any kind of formatting to the generator making it more appropriate to be used as a library. --- .../framework/plugins/linux/sockstat.py | 164 +++++++++--------- .../symbols/linux/extensions/__init__.py | 49 ++---- 2 files changed, 100 insertions(+), 113 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 6d50c296d..f927c0c83 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -72,7 +72,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns a tuple with: sock: The respective kernel's *_sock object for that socket family - sock_stat: A tuple with the source, destination and state strings. + sock_stat: A tuple with the source and destination (address and port) along with its state string. extended: A dictionary with key/value extended information. """ family = sock.get_family() @@ -93,10 +93,10 @@ class SockHandlers(interfaces.configuration.VersionableInterface): # Even if the sock family is not supported, or the required types # are not present in the symbols, we can still show some general # information about the socket that may be helpful. - saddr_tag = daddr_tag = NotAvailableValue() + src_addr = src_port = dst_addr = dst_port = None state = sock.get_state() - sock_stat = saddr_tag, daddr_tag, state + sock_stat = src_addr, src_port, dst_addr, dst_port, state return sock, sock_stat, extended @@ -149,22 +149,21 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: unix_sock: The kernel's `unix_sock` object - sock_stat: A tuple with the source, destination and state strings. + sock_stat: A tuple with the source and destination (address and port) along with its state string. """ unix_sock = sock.cast("unix_sock") state = unix_sock.get_state() - saddr = unix_sock.get_name() - sinode = unix_sock.get_inode() - if unix_sock.peer != 0: - peer = unix_sock.peer.dereference().cast("unix_sock") - daddr = peer.get_name() - dinode = peer.get_inode() - else: - daddr = dinode = "" + src_addr = unix_sock.get_name() + src_port = unix_sock.get_inode() - saddr_tag = f"{saddr} {sinode}" - daddr_tag = f"{daddr} {dinode}" - sock_stat = saddr_tag, daddr_tag, state + if unix_sock.peer: + peer = unix_sock.peer.dereference().cast("unix_sock") + dst_addr = peer.get_name() + dst_port = peer.get_inode() + else: + dst_addr = dst_port = None + + sock_stat = src_addr, src_port, dst_addr, dst_port, state return unix_sock, sock_stat def _inet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: @@ -175,21 +174,16 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: inet_sock: The kernel's `inet_sock` object - sock_stat: A tuple with the source, destination and state strings. + sock_stat: A tuple with the source and destination (address and port) along with its state string. """ inet_sock = sock.cast("inet_sock") - saddr = inet_sock.get_src_addr() - sport = inet_sock.get_src_port() - daddr = inet_sock.get_dst_addr() - dport = inet_sock.get_dst_port() + src_addr = inet_sock.get_src_addr() + src_port = inet_sock.get_src_port() + dst_addr = inet_sock.get_dst_addr() + dst_port = inet_sock.get_dst_port() state = inet_sock.get_state() - if inet_sock.get_family() == "AF_INET6": - saddr = f"[{saddr}]" - - saddr_tag = f"{saddr}:{sport}" - daddr_tag = f"{daddr}:{dport}" - sock_stat = saddr_tag, daddr_tag, state + sock_stat = src_addr, src_port, dst_addr, dst_port, state return inet_sock, sock_stat def _netlink_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: @@ -200,34 +194,26 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: netlink_sock: The kernel's `netlink_sock` object - sock_stat: A tuple with the source, destination and state strings. + sock_stat: A tuple with the source and destination (address and port) along with its state string. """ netlink_sock = sock.cast("netlink_sock") - saddr_list = [] - src_portid = f"portid:{netlink_sock.portid}" - saddr_list.append(src_portid) - if netlink_sock.groups != 0: + src_addr = None + if netlink_sock.groups: groups_bitmap = netlink_sock.groups.dereference() - groups_str = f"groups:0x{groups_bitmap:08x}" - saddr_list.append(groups_str) + src_addr = f"groups:0x{groups_bitmap:08x}" + src_port = netlink_sock.portid - daddr_list = [] - dst_portid = f"portid:{netlink_sock.dst_portid}" - daddr_list.append(dst_portid) - dst_group = f"group:0x{netlink_sock.dst_group:08x}" - daddr_list.append(dst_group) + dst_addr = f"group:0x{netlink_sock.dst_group:08x}" module = netlink_sock.module - if module and netlink_sock.module.name: - module_name_str = utility.array_to_string(netlink_sock.module.name) - module_name = f"lkm:{module_name_str}" - daddr_list.append(module_name) + if module and module.name: + module_name_str = utility.array_to_string(module.name) + dst_addr = f"{dst_addr},lkm:{module_name_str}" + dst_port = netlink_sock.dst_portid - saddr_tag = ",".join(saddr_list) - daddr_tag = ",".join(daddr_list) state = netlink_sock.get_state() - sock_stat = saddr_tag, daddr_tag, state + sock_stat = src_addr, src_port, dst_addr, dst_port, state return netlink_sock, sock_stat def _vsock_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: @@ -238,18 +224,16 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: vsock_sock: The kernel `vsock_sock` object - sock_stat: A tuple with the source, destination and state strings. + sock_stat: A tuple with the source and destination (address and port) along with its state string. """ vsock_sock = sock.cast("vsock_sock") - saddr = vsock_sock.local_addr.svm_cid - sport = vsock_sock.local_addr.svm_port - daddr = vsock_sock.remote_addr.svm_cid - dport = vsock_sock.remote_addr.svm_port + src_addr = vsock_sock.local_addr.svm_cid + src_port = vsock_sock.local_addr.svm_port + dst_addr = vsock_sock.remote_addr.svm_cid + dst_port = vsock_sock.remote_addr.svm_port state = vsock_sock.get_state() - saddr_tag = f"{saddr}:{sport}" - daddr_tag = f"{daddr}:{dport}" - sock_stat = saddr_tag, daddr_tag, state + sock_stat = src_addr, src_port, dst_addr, dst_port, state return vsock_sock, sock_stat def _packet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: @@ -260,16 +244,17 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: packet_sock: The kernel's `packet_sock` object - sock_stat: A tuple with the source, destination and state strings. + sock_stat: A tuple with the source and destination (address and port) along with its state string. """ packet_sock = sock.cast("packet_sock") ifindex = packet_sock.ifindex - dev_name = self._netdevices.get(ifindex, "") if ifindex > 0 else "ANY" + dev_name = self._netdevices.get(ifindex) if ifindex > 0 else "ANY" - saddr_tag = f"{dev_name}" - daddr_tag = "" + src_addr = dev_name + src_port = dst_addr = dst_port = None state = packet_sock.get_state() - sock_stat = saddr_tag, daddr_tag, state + + sock_stat = src_addr, src_port, dst_addr, dst_port, state return packet_sock, sock_stat def _xdp_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: @@ -280,34 +265,39 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: xdp_sock: The kernel's `xdp_sock` object - sock_stat: A tuple with the source, destination and state strings. + sock_stat: A tuple with the source and destination (address and port) along with its state string. """ xdp_sock = sock.cast("xdp_sock") device = xdp_sock.dev if not device: return - saddr_tag = utility.array_to_string(device.name) + src_addr = utility.array_to_string(device.name) + src_port = dst_addr = dst_port = None bpfprog = device.xdp_prog if not bpfprog: return + if not bpfprog.has_member("aux") or not bpfprog.aux: + return + bpfprog_aux = bpfprog.aux - if bpfprog_aux: + if bpfprog_aux.has_member("id"): + # `id` member was added to `bpf_prog_aux` in kernels 4.13 bpfprog_id = bpfprog_aux.id - daddr_tag = f"ebpf_prog_id:{bpfprog_id}" + dst_port = f"ebpf_prog_id:{bpfprog_id}" + if bpfprog_aux.has_member("name"): + # `name` was added to `bpf_prog_aux` in kernels 4.15 bpf_name = utility.array_to_string(bpfprog_aux.name) if bpf_name: - daddr_tag += f",ebpf_prog_name:{bpf_name}" - else: - daddr_tag = "" + dst_addr = f"ebpf_prog_name:{bpf_name}" # Hallelujah, xdp_sock.state is an enum xsk_state = xdp_sock.state.lookup() state = xsk_state.replace("XSK_", "") - sock_stat = saddr_tag, daddr_tag, state + sock_stat = src_addr, src_port, dst_addr, dst_port, state return xdp_sock, sock_stat def _bluetooth_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: @@ -318,14 +308,14 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: bt_sock: The kernel's `bt_sock` object - sock_stat: A tuple with the source, destination and state strings. + sock_stat: A tuple with the source and destination (address and port) along with its state string. """ bt_sock = sock.cast("bt_sock") def bt_addr(addr): return ":".join(reversed(["%02x" % x for x in addr.b])) - saddr_tag = daddr_tag = "" + src_addr = src_port = dst_addr = dst_port = None bt_protocol = bt_sock.get_protocol() if bt_protocol == "HCI": pinfo = bt_sock.cast("hci_pinfo") @@ -333,20 +323,17 @@ class SockHandlers(interfaces.configuration.VersionableInterface): pinfo = bt_sock.cast("l2cap_pinfo") src_addr = bt_addr(pinfo.chan.src) dst_addr = bt_addr(pinfo.chan.dst) - saddr_tag = f"{src_addr}" - daddr_tag = f"{dst_addr}" elif bt_protocol == "RFCOMM": pinfo = bt_sock.cast("rfcomm_pinfo") src_addr = bt_addr(pinfo.src) dst_addr = bt_addr(pinfo.dst) - channel = pinfo.channel - saddr_tag = f"[{src_addr}]:{channel}" - daddr_tag = f"{dst_addr}" + src_port = pinfo.channel else: vollog.log(constants.LOGLEVEL_V, "Unsupported bluetooth protocol '%s'", bt_protocol) state = bt_sock.get_state() - sock_stat = saddr_tag, daddr_tag, state + + sock_stat = src_addr, src_port, dst_addr, dst_port, state return bt_sock, sock_stat class Sockstat(plugins.PluginInterface): @@ -457,8 +444,10 @@ class Sockstat(plugins.PluginInterface): family: Socket family string (AF_UNIX, AF_INET, etc) sock_type: Socket type string (STREAM, DGRAM, etc) protocol: Protocol string (UDP, TCP, etc) - source: Source address string - destination: Destination address string + source addr: Source address string + source port: Source port string (not all of them are int) + destination addr: Destination address string + destination port: Destination port (not all of them are int) state: State strings (LISTEN, CONNECTED, etc) tasks: String with a list of tasks and FDs using a socket. It can also have extended information such as socket filters, bpf info, etc. @@ -472,6 +461,7 @@ class Sockstat(plugins.PluginInterface): continue sock, sock_stat, extended = sock_fields + sock_stat, protocol = self._format_fields(sock_stat, protocol) task_comm = utility.array_to_string(task.comm) task_info = f"{task_comm},pid={task.pid},fd={fd_num}" @@ -496,6 +486,22 @@ class Sockstat(plugins.PluginInterface): fields = data['fields'] + (tasks,) yield (0, fields) + def _format_fields(self, sock_stat, protocol): + """Prepare the socket fields to be rendered + + Args: + sock_stat: A tuple with the source and destination (address and port) along with its state string. + protocol: Protocol string (UDP, TCP, etc) + + Returns: + `sock_stat` and `protocol` formatted. + """ + sock_stat = [NotAvailableValue() if field is None else str(field) for field in sock_stat] + if protocol is None: + protocol = NotAvailableValue() + + return tuple(sock_stat), protocol + def run(self): pids = self.config.get('pids') netns_id = self.config['netns'] @@ -505,8 +511,10 @@ class Sockstat(plugins.PluginInterface): ("Family", str), ("Type", str), ("Proto", str), - ("Source Addr:Port", str), - ("Destination Addr:Port", str), + ("Source Addr", str), + ("Source Port", str), + ("Destination Addr", str), + ("Destination Port", str), ("State", str), ("Tasks", str)] diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index d193200c4..ab0622cf0 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -819,16 +819,12 @@ class socket(objects.StructType): socket_state_idx = self.state if 0 <= socket_state_idx < len(SOCKET_STATES): return SOCKET_STATES[socket_state_idx] - else: - return "UNKNOWN" class sock(objects.StructType): def get_family(self): family_idx = self.__sk_common.skc_family if 0 <= family_idx < len(SOCK_FAMILY): return SOCK_FAMILY[family_idx] - else: - return "UNKNOWN" def get_type(self): return SOCK_TYPES.get(self.sk_type, "") @@ -840,7 +836,7 @@ class sock(objects.StructType): return self.sk_socket.get_inode() def get_protocol(self): - return "" + return def get_state(self): # Return the generic socket state @@ -851,15 +847,15 @@ class sock(objects.StructType): class unix_sock(objects.StructType): def get_name(self): - if self.addr: - sockaddr_un = self.addr.name.cast("sockaddr_un") - saddr = str(utility.array_to_string(sockaddr_un.sun_path)) - else: - saddr = "" + if not self.addr: + return + + sockaddr_un = self.addr.name.cast("sockaddr_un") + saddr = str(utility.array_to_string(sockaddr_un.sun_path)) return saddr def get_protocol(self): - return "" + return def get_state(self): """Return a string representing the sock state.""" @@ -869,8 +865,6 @@ class unix_sock(objects.StructType): state_idx = self.sk.__sk_common.skc_state if 0 <= state_idx < len(TCP_STATES): return TCP_STATES[state_idx] - else: - return "UNKNOWN" else: # Return the generic socket state return self.sk.sk_socket.get_state() @@ -883,15 +877,14 @@ class inet_sock(objects.StructType): family_idx = self.sk.__sk_common.skc_family if 0 <= family_idx < len(SOCK_FAMILY): return SOCK_FAMILY[family_idx] - else: - return "UNKNOWN" def get_protocol(self): # If INET6 family and a proto is defined, we use that specific IPv6 protocol. # Otherwise, we use the standard IP protocol. - protocol = IP_PROTOCOLS.get(self.sk.sk_protocol, "UNKNOWN") + protocol = IP_PROTOCOLS.get(self.sk.sk_protocol) if self.get_family() == "AF_INET6": protocol = IPV6_PROTOCOLS.get(self.sk.sk_protocol, protocol) + return protocol def get_state(self): @@ -901,8 +894,6 @@ class inet_sock(objects.StructType): state_idx = self.sk.__sk_common.skc_state if 0 <= state_idx < len(TCP_STATES): return TCP_STATES[state_idx] - else: - return "UNKNOWN" else: # Return the generic socket state return self.sk.sk_socket.get_state() @@ -949,7 +940,7 @@ class inet_sock(objects.StructType): addr_bytes = parent_layer.read(saddr.vol.offset, addr_size) except exceptions.InvalidAddressException: vollog.debug(f"Unable to read socket src address from {saddr.vol.offset:#x}") - return "?" + return return socket_module.inet_ntop(family, addr_bytes) @@ -978,7 +969,7 @@ class inet_sock(objects.StructType): addr_bytes = parent_layer.read(daddr.vol.offset, addr_size) except exceptions.InvalidAddressException: vollog.debug(f"Unable to read socket dst address from {daddr.vol.offset:#x}") - return "?" + return return socket_module.inet_ntop(family, addr_bytes) @@ -987,8 +978,6 @@ class netlink_sock(objects.StructType): protocol_idx = self.sk.sk_protocol if 0 <= protocol_idx < len(NETLINK_PROTOCOLS): return NETLINK_PROTOCOLS[protocol_idx] - else: - return "UNKNOWN" def get_state(self): # Return the generic socket state @@ -997,10 +986,7 @@ class netlink_sock(objects.StructType): class vsock_sock(objects.StructType): def get_protocol(self): # The protocol should always be 0 for vsocks - if self.sk.sk_protocol == 0: - return "" - else: - return "UNKNOWN" + return def get_state(self): # Return the generic socket state @@ -1010,7 +996,7 @@ class packet_sock(objects.StructType): def get_protocol(self): eth_proto = socket_module.htons(self.num) if eth_proto == 0: - return "" + return elif eth_proto in ETH_PROTOCOLS: return ETH_PROTOCOLS[eth_proto] else: @@ -1025,23 +1011,16 @@ class bt_sock(objects.StructType): type_idx = self.sk.sk_protocol if 0 <= type_idx < len(BLUETOOTH_PROTOCOLS): return BLUETOOTH_PROTOCOLS[type_idx] - else: - return "UNKNOWN" def get_state(self): state_idx = self.sk.__sk_common.skc_state if 0 <= state_idx < len(BLUETOOTH_STATES): return BLUETOOTH_STATES[state_idx] - else: - return "UNKNOWN" class xdp_sock(objects.StructType): def get_protocol(self): # The protocol should always be 0 for xdp_sock - if self.sk.sk_protocol == 0: - return "" - else: - return "UNKNOWN" + return def get_state(self): # Return the generic socket state From 25ceccb65b8695d60a6bfd3ecb999803ea128ab1 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Mon, 31 Oct 2022 15:42:47 +1100 Subject: [PATCH 037/140] Improve and fix issues in bluetooth family. Disaggregate pid, fds, and socket address to new columns. Removed task association by socket address feature. --- .../framework/plugins/linux/sockstat.py | 175 ++++++++++-------- .../symbols/linux/extensions/__init__.py | 4 +- 2 files changed, 96 insertions(+), 83 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index f927c0c83..ad3eee01f 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -6,7 +6,7 @@ import logging from typing import Callable, Tuple, List, Dict from volatility3.framework import interfaces, exceptions, constants, objects -from volatility3.framework.renderers import TreeGrid, NotAvailableValue +from volatility3.framework.renderers import TreeGrid, NotAvailableValue, format_hints from volatility3.framework.configuration import requirements from volatility3.framework.interfaces import plugins from volatility3.framework.objects import utility @@ -72,18 +72,18 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns a tuple with: sock: The respective kernel's *_sock object for that socket family - sock_stat: A tuple with the source and destination (address and port) along with its state string. - extended: A dictionary with key/value extended information. + sock_stat: A tuple with the source and destination (address and port) along with its state string + socket_filter: A dictionary with information about the socket filter """ family = sock.get_family() - extended = {} + socket_filter = {} sock_handler = self._sock_family_handlers.get(family) if sock_handler: try: unix_sock, sock_stat = sock_handler(sock) - self._update_extended_socket_filters_info(sock, extended) + self._update_socket_filters_info(sock, socket_filter) - return unix_sock, sock_stat, extended + return unix_sock, sock_stat, socket_filter except exceptions.SymbolError as e: # Cannot finds the *_sock type in the symbols vollog.log(constants.LOGLEVEL_V, "Error processing socket family '%s': %s", family, e) @@ -98,27 +98,32 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = src_addr, src_port, dst_addr, dst_port, state - return sock, sock_stat, extended + return sock, sock_stat, socket_filter - def _update_extended_socket_filters_info(self, sock: objects.Pointer, extended: dict) -> None: + def _update_socket_filters_info(self, sock: objects.Pointer, socket_filter: dict) -> None: """Get information from the socket and reuseport filters Args: sock: The kernel sock (sk) struct - extended: Dictionary to store extended information + socket_filter: A dictionary with information about the socket filter """ if sock.has_member("sk_filter") and sock.sk_filter: sock_filter = sock.sk_filter - extended["filter_type"] = "socket_filter" - self._extract_socket_filter_info(sock_filter, extended) + socket_filter["filter_type"] = "socket_filter" + self._extract_socket_filter_info(sock_filter, socket_filter) if sock.has_member("sk_reuseport_cb") and sock.sk_reuseport_cb: sock_reuseport_cb = sock.sk_reuseport_cb - extended["filter_type"] = "reuseport_filter" - self._extract_socket_filter_info(sock_reuseport_cb, extended) + socket_filter["filter_type"] = "reuseport_filter" + self._extract_socket_filter_info(sock_reuseport_cb, socket_filter) - def _extract_socket_filter_info(self, sock_filter: objects.Pointer, extended: dict) -> None: - extended["bpf_filter_type"] = "cBPF" + def _extract_socket_filter_info(self, sock_filter: objects.Pointer, socket_filter: dict) -> None: + """Get specific information for each type of filter + + Args: + socket_filter: A dictionary with information about the socket filter + """ + socket_filter["bpf_filter_type"] = "cBPF" if not sock_filter.has_member("prog") or not sock_filter.prog: return @@ -128,18 +133,18 @@ class SockHandlers(interfaces.configuration.VersionableInterface): # BPF_PROG_TYPE_UNSPEC = 0 return - extended["bpf_filter_type"] = "eBPF" + socket_filter["bpf_filter_type"] = "eBPF" if not bpfprog.has_member("aux") or not bpfprog.aux: return bpfprog_aux = bpfprog.aux if bpfprog_aux.has_member("id"): # `id` member was added to `bpf_prog_aux` in kernels 4.13 - extended["bpf_filter_id"] = str(bpfprog_aux.id) + socket_filter["bpf_filter_id"] = str(bpfprog_aux.id) if bpfprog_aux.has_member("name"): # `name` was added to `bpf_prog_aux` in kernels 4.15 bpfprog_name = utility.array_to_string(bpfprog_aux.name) if bpfprog_name: - extended["bpf_filter_name"] = bpfprog_name + socket_filter["bpf_filter_name"] = bpfprog_name def _unix_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: """Handles the AF_UNIX socket family @@ -149,7 +154,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: unix_sock: The kernel's `unix_sock` object - sock_stat: A tuple with the source and destination (address and port) along with its state string. + sock_stat: A tuple with the source and destination (address and port) along with its state string """ unix_sock = sock.cast("unix_sock") state = unix_sock.get_state() @@ -174,7 +179,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: inet_sock: The kernel's `inet_sock` object - sock_stat: A tuple with the source and destination (address and port) along with its state string. + sock_stat: A tuple with the source and destination (address and port) along with its state string """ inet_sock = sock.cast("inet_sock") src_addr = inet_sock.get_src_addr() @@ -194,7 +199,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: netlink_sock: The kernel's `netlink_sock` object - sock_stat: A tuple with the source and destination (address and port) along with its state string. + sock_stat: A tuple with the source and destination (address and port) along with its state string """ netlink_sock = sock.cast("netlink_sock") @@ -224,7 +229,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: vsock_sock: The kernel `vsock_sock` object - sock_stat: A tuple with the source and destination (address and port) along with its state string. + sock_stat: A tuple with the source and destination (address and port) along with its state string """ vsock_sock = sock.cast("vsock_sock") src_addr = vsock_sock.local_addr.svm_cid @@ -244,7 +249,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: packet_sock: The kernel's `packet_sock` object - sock_stat: A tuple with the source and destination (address and port) along with its state string. + sock_stat: A tuple with the source and destination (address and port) along with its state string """ packet_sock = sock.cast("packet_sock") ifindex = packet_sock.ifindex @@ -265,7 +270,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: xdp_sock: The kernel's `xdp_sock` object - sock_stat: A tuple with the source and destination (address and port) along with its state string. + sock_stat: A tuple with the source and destination (address and port) along with its state string """ xdp_sock = sock.cast("xdp_sock") device = xdp_sock.dev @@ -293,8 +298,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): if bpf_name: dst_addr = f"ebpf_prog_name:{bpf_name}" - # Hallelujah, xdp_sock.state is an enum - xsk_state = xdp_sock.state.lookup() + xsk_state = xdp_sock.get_state() state = xsk_state.replace("XSK_", "") sock_stat = src_addr, src_port, dst_addr, dst_port, state @@ -308,7 +312,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): Returns: bt_sock: The kernel's `bt_sock` object - sock_stat: A tuple with the source and destination (address and port) along with its state string. + sock_stat: A tuple with the source and destination (address and port) along with its state string """ bt_sock = sock.cast("bt_sock") @@ -318,16 +322,37 @@ class SockHandlers(interfaces.configuration.VersionableInterface): src_addr = src_port = dst_addr = dst_port = None bt_protocol = bt_sock.get_protocol() if bt_protocol == "HCI": - pinfo = bt_sock.cast("hci_pinfo") + if self._vmlinux.has_type("hci_pinfo"): + pinfo = bt_sock.cast("hci_pinfo") + if pinfo.has_member("hdev") and self._vmlinux.has_type("hci_dev") \ + and pinfo.hdev.has_member("dev_name"): + src_addr = utility.array_to_string(pinfo.hdev.dev_name) + else: + vollog.log(constants.LOGLEVEL_V, "Type definition for 'hci_pinfo' is not available in the symbols") elif bt_protocol == "L2CAP": - pinfo = bt_sock.cast("l2cap_pinfo") - src_addr = bt_addr(pinfo.chan.src) - dst_addr = bt_addr(pinfo.chan.dst) + if self._vmlinux.has_type("l2cap_pinfo"): + pinfo = bt_sock.cast("l2cap_pinfo") + src_addr = bt_addr(pinfo.chan.src) + dst_addr = bt_addr(pinfo.chan.dst) + src_port = pinfo.chan.sport + dst_port = pinfo.chan.psm + else: + vollog.log(constants.LOGLEVEL_V, "Type definition for 'l2cap_pinfo' is not available in the symbols") elif bt_protocol == "RFCOMM": - pinfo = bt_sock.cast("rfcomm_pinfo") - src_addr = bt_addr(pinfo.src) - dst_addr = bt_addr(pinfo.dst) - src_port = pinfo.channel + if self._vmlinux.has_type("rfcomm_pinfo"): + pinfo = bt_sock.cast("rfcomm_pinfo") + src_addr = bt_addr(pinfo.src) + dst_addr = bt_addr(pinfo.dst) + src_port = pinfo.channel + else: + vollog.log(constants.LOGLEVEL_V, "Type definition for 'rfcomm_pinfo' is not available in the symbols") + elif bt_protocol == "SCO": + if self._vmlinux.has_type("sco_pinfo"): + pinfo = bt_sock.cast("sco_pinfo") + src_addr = bt_addr(pinfo.src) + dst_addr = bt_addr(pinfo.dst) + else: + vollog.log(constants.LOGLEVEL_V, "Type definition for 'sco_pinfo' is not available in the symbols") else: vollog.log(constants.LOGLEVEL_V, "Unsupported bluetooth protocol '%s'", bt_protocol) @@ -431,6 +456,22 @@ class Sockstat(plugins.PluginInterface): netns_id = net.get_inode() yield task, netns_id, fd_num, family, sock_type, protocol, sock_fields + def _format_fields(self, sock_stat, protocol): + """Prepare the socket fields to be rendered + + Args: + sock_stat: A tuple with the source and destination (address and port) along with its state string + protocol: Protocol string (UDP, TCP, etc) + + Returns: + `sock_stat` and `protocol` formatted. + """ + sock_stat = [NotAvailableValue() if field is None else str(field) for field in sock_stat] + if protocol is None: + protocol = NotAvailableValue() + + return tuple(sock_stat), protocol + def _generator(self, pids: List[int], netns_id_arg: int, symbol_table: str): """Enumerate tasks sockets. Each row represents a kernel socket. @@ -455,7 +496,6 @@ class Sockstat(plugins.PluginInterface): filter_func = lsof.pslist.PsList.create_pid_filter(pids) socket_generator = self.list_sockets(self.context, symbol_table, filter_func=filter_func) - tasks_per_sock = {} for task, netns_id, fd_num, family, sock_type, protocol, sock_fields in socket_generator: if netns_id_arg and netns_id_arg != netns_id: continue @@ -463,59 +503,32 @@ class Sockstat(plugins.PluginInterface): sock, sock_stat, extended = sock_fields sock_stat, protocol = self._format_fields(sock_stat, protocol) - task_comm = utility.array_to_string(task.comm) - task_info = f"{task_comm},pid={task.pid},fd={fd_num}" - if extended: - extended_str = ",".join(f"{k}={v}" for k, v in extended.items()) - task_info = f"{task_info},{extended_str}" + socket_filter_str = ",".join(f"{k}={v}" for k, v in extended.items()) if extended else NotAvailableValue() - fields = netns_id, family, sock_type, protocol, *sock_stat + fields = (netns_id, task.pid, fd_num, format_hints.Hex(sock.vol.offset), + family, sock_type, protocol, *sock_stat, socket_filter_str) - # Each row represents a kernel socket, so let's group the task FDs - # by socket using the socket address - sock_addr = sock.vol.offset - tasks_per_sock.setdefault(sock_addr, {}) - tasks_per_sock[sock_addr].setdefault('tasks', []) - tasks_per_sock[sock_addr]['tasks'].append(task_info) - tasks_per_sock[sock_addr]['fields'] = fields - - for data in tasks_per_sock.values(): - task_list = [f"({task})" for task in data['tasks']] - tasks = ",".join(task_list) - - fields = data['fields'] + (tasks,) yield (0, fields) - def _format_fields(self, sock_stat, protocol): - """Prepare the socket fields to be rendered - - Args: - sock_stat: A tuple with the source and destination (address and port) along with its state string. - protocol: Protocol string (UDP, TCP, etc) - - Returns: - `sock_stat` and `protocol` formatted. - """ - sock_stat = [NotAvailableValue() if field is None else str(field) for field in sock_stat] - if protocol is None: - protocol = NotAvailableValue() - - return tuple(sock_stat), protocol - def run(self): pids = self.config.get('pids') netns_id = self.config['netns'] symbol_table = self.config['kernel'] - tree_grid_args = [("NetNS", int), - ("Family", str), - ("Type", str), - ("Proto", str), - ("Source Addr", str), - ("Source Port", str), - ("Destination Addr", str), - ("Destination Port", str), - ("State", str), - ("Tasks", str)] + tree_grid_args = [ + ("NetNS", int), + ("Pid", int), + ("FD", int), + ("Sock Offset", format_hints.Hex), + ("Family", str), + ("Type", str), + ("Proto", str), + ("Source Addr", str), + ("Source Port", str), + ("Destination Addr", str), + ("Destination Port", str), + ("State", str), + ("Filter", str), + ] return TreeGrid(tree_grid_args, self._generator(pids, netns_id, symbol_table)) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index ab0622cf0..c26d68b0e 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -1023,5 +1023,5 @@ class xdp_sock(objects.StructType): return def get_state(self): - # Return the generic socket state - return self.sk.sk_socket.get_state() + # xdp_sock.state is an enum + return self.state.lookup() From b5c3ab171e4d3554e39efd8a07dbd9f5dceafccc Mon Sep 17 00:00:00 2001 From: cpuu Date: Sat, 19 Nov 2022 15:01:39 +0900 Subject: [PATCH 038/140] Add macos tutorial --- doc/source/getting-started-macos-tutorial.rst | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 doc/source/getting-started-macos-tutorial.rst diff --git a/doc/source/getting-started-macos-tutorial.rst b/doc/source/getting-started-macos-tutorial.rst new file mode 100644 index 000000000..6dd144ac3 --- /dev/null +++ b/doc/source/getting-started-macos-tutorial.rst @@ -0,0 +1,152 @@ +macOS Tutorial +============== + +This guide will give you a brief overview of how volatility3 works as well as a demonstration of several of the plugins available in the suite. + +Acquiring memory +---------------- + +Volatility3 does not provide the ability to acquire memory. The example below is an open source tool. Other commercial tools are also available. + +* `osxpmem `_ + + + +Procedure to create symbol tables for macOS +-------------------------------------------- + +To create a symbol table please refer to :ref:`symbol-tables:Mac or Linux symbol tables`. + +.. tip:: It may be possible to locate pre-made ISF files from the `download link `_ , + which is built and maintained by `volatilityfoundation `_. + After creating the file or downloading it from the link, place the file under the directory ``volatility3/symbols/mac``. + If necessary create a mac directory under the symbols directory (this will become unnecessary in future versions). + + +Listing plugins +--------------- + +The following is a sample of the macOS plugins available for volatility3, it is not complete and more more plugins may +be added. For a complete reference, please see the volatility 3 :doc:`list of plugins `. +For plugin requests, please create an issue with a description of the requested plugin. + +.. code-block:: shell-session + + $ python3 vol.py --help | grep -i mac. | head -n 5 + mac.bash.Bash Recovers bash command history from memory. + mac.check_syscall.Check_syscall + mac.check_sysctl.Check_sysctl + mac.check_trap_table.Check_trap_table + +.. note:: Here the the command is piped to grep and head in-order to provide the start of the list of macOS plugins. + + +Using plugins +------------- + +The following is the syntax to run the volatility CLI. + +.. code-block:: shell-session + + $ python3 vol.py -f + + +Example +------- + +banners +~~~~~~~ + +In this example we will be using a memory dump from the Securinets CTF Quals 2019 Challenge called Contact_me. We will limit the discussion to memory forensics with volatility 3 and not extend it to other parts of the challenge. +Thanks go to `stuxnet `_ for providing this memory dump and `writeup `_. + + +.. code-block:: shell-session + + $ python3 vol.py -f contact_me banners.Banners + + Volatility 3 Framework 2.1.0 + + Progress: 100.00 PDB scanning finished + Offset Banner + + 0x4d2c7d0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + 0xb42b180 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + 0xcda9100 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + 0x1275e7d0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + 0x1284fba4 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + 0x34ad0180 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + + +The above command helps us to find the memory dump's Darwin kernel version. Now using the above banner we can search for the needed ISF file. +If ISF file cannot be found then, follow the instructions on :ref:`getting-started-macos-tutorial:Procedure to create symbol tables for macOS`. After that, place the ISF file under the ``volatility3/symbols/mac`` directory. + +linux.pslist +~~~~~~~~~~~~ + +.. code-block:: shell-session + + $ python3 vol.py -f contact_me mac.pslist + + Volatility 3 Framework 2.1.0 Stacking attempts finished + + PID PPID COMM + + 0 0 kernel_task + 1 0 launchd + 35 1 UserEventAgent + 38 1 kextd + 39 1 fseventsd + 37 1 uninstalld + 45 1 configd + 46 1 powerd + 52 1 logd + 58 1 warmd + ..... + +``mac.pslist`` helps us to list the processes which are running, their PIDs and PPIDs. + +mac.pstree +~~~~~~~~~~~~ + +.. code-block:: shell-session + + $ python3 vol.py -f contact_me mac.pstree + Volatility 3 Framework 2.1.0 + Progress: 100.00 Stacking attempts finished + PID PPID COMM + + 35 1 UserEventAgent + 38 1 kextd + 39 1 fseventsd + 37 1 uninstalld + 204 1 softwareupdated + * 449 204 SoftwareUpdateCo + 337 1 system_installd + * 455 337 update_dyld_shar + +``mac.pstree`` helps us to display the parent child relationships between processes. + +mac.ifconfig +~~~~~~~~~~ + +we can use the ``mac.ifconfig`` plugin to get information about the configuration of the network interfaces of the host under investigation. +.. code-block:: shell-session + + $ python3 vol.py -f contact_me mac.ifconfig + + Volatility 3 Framework 2.1.0 + Progress: 100.00 Stacking attempts finished + Interface IP Address Mac Address Promiscuous + + lo0 False + lo0 127.0.0.1 False + lo0 ::1 False + lo0 fe80:1::1 False + gif0 False + stf0 False + en0 00:0C:29:89:8B:F0 00:0C:29:89:8B:F0 False + en0 fe80:4::10fb:c89d:217f:52ae 00:0C:29:89:8B:F0 False + en0 192.168.140.128 00:0C:29:89:8B:F0 False + utun0 False + utun0 fe80:5::2a95:bb15:87e3:977c False From 17bcc8d47e372e5066d07b98b0059d1d2b7ee548 Mon Sep 17 00:00:00 2001 From: cpuu Date: Sat, 19 Nov 2022 23:11:45 +0900 Subject: [PATCH 039/140] typo --- doc/source/getting-started-macos-tutorial.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/source/getting-started-macos-tutorial.rst b/doc/source/getting-started-macos-tutorial.rst index 6dd144ac3..0d95a1700 100644 --- a/doc/source/getting-started-macos-tutorial.rst +++ b/doc/source/getting-started-macos-tutorial.rst @@ -131,6 +131,8 @@ mac.ifconfig ~~~~~~~~~~ we can use the ``mac.ifconfig`` plugin to get information about the configuration of the network interfaces of the host under investigation. + + .. code-block:: shell-session $ python3 vol.py -f contact_me mac.ifconfig From 53870c64d1553e43e283bff3fbc08cc0249d4d0e Mon Sep 17 00:00:00 2001 From: cpuu Date: Sat, 19 Nov 2022 23:15:29 +0900 Subject: [PATCH 040/140] Add macos tutorial --- doc/source/getting-started-macos-tutorial.rst | 2 +- doc/source/index.rst | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/source/getting-started-macos-tutorial.rst b/doc/source/getting-started-macos-tutorial.rst index 0d95a1700..76f77d0f3 100644 --- a/doc/source/getting-started-macos-tutorial.rst +++ b/doc/source/getting-started-macos-tutorial.rst @@ -17,7 +17,7 @@ Procedure to create symbol tables for macOS To create a symbol table please refer to :ref:`symbol-tables:Mac or Linux symbol tables`. -.. tip:: It may be possible to locate pre-made ISF files from the `download link `_ , +.. tip:: It may be possible to locate pre-made ISF files from the `download link `_ , which is built and maintained by `volatilityfoundation `_. After creating the file or downloading it from the link, place the file under the directory ``volatility3/symbols/mac``. If necessary create a mac directory under the symbols directory (this will become unnecessary in future versions). diff --git a/doc/source/index.rst b/doc/source/index.rst index 9b1d05858..e096731c7 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -26,6 +26,7 @@ There is also some information to get you started quickly: getting-started-linux-tutorial getting-started-windows-tutorial + getting-started-macos-tutorial .. toctree:: From fc0fa30f9e3fda0479f0fd9761b33550d705ecc2 Mon Sep 17 00:00:00 2001 From: cpuu Date: Sun, 20 Nov 2022 10:18:47 +0900 Subject: [PATCH 041/140] Update doc/source/getting-started-macos-tutorial.rst Co-authored-by: Donghyun Kim --- doc/source/getting-started-macos-tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/getting-started-macos-tutorial.rst b/doc/source/getting-started-macos-tutorial.rst index 76f77d0f3..bc0cb1b92 100644 --- a/doc/source/getting-started-macos-tutorial.rst +++ b/doc/source/getting-started-macos-tutorial.rst @@ -81,7 +81,7 @@ Thanks go to `stuxnet `_ for providing this memo The above command helps us to find the memory dump's Darwin kernel version. Now using the above banner we can search for the needed ISF file. If ISF file cannot be found then, follow the instructions on :ref:`getting-started-macos-tutorial:Procedure to create symbol tables for macOS`. After that, place the ISF file under the ``volatility3/symbols/mac`` directory. -linux.pslist +mac.pslist ~~~~~~~~~~~~ .. code-block:: shell-session From 92c7b3e5500b03fa68d8893204b31f50cef7b4dc Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 9 Dec 2022 15:37:20 +0000 Subject: [PATCH 042/140] add linux envars --- volatility3/framework/plugins/linux/envars.py | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 volatility3/framework/plugins/linux/envars.py diff --git a/volatility3/framework/plugins/linux/envars.py b/volatility3/framework/plugins/linux/envars.py new file mode 100644 index 000000000..b62400c45 --- /dev/null +++ b/volatility3/framework/plugins/linux/envars.py @@ -0,0 +1,110 @@ +# This file is Copyright 2022 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 exceptions, renderers +from volatility3.framework.configuration import requirements +from volatility3.framework.interfaces import plugins +from volatility3.framework.objects import utility +from volatility3.plugins.linux import pslist + +vollog = logging.getLogger(__name__) + +class Envars(plugins.PluginInterface): + """Lists processes with their environment variables""" + + _required_framework_version = (2, 0, 0) + + @classmethod + def get_requirements(cls): + # Since we're calling the plugin, make sure we have the plugin's requirements + return [ + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=["Intel32", "Intel64"], + ), + 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): + """Generates a listing of processes along with environment variables""" + + # walk the process list and return the envars + for task in tasks: + pid = task.pid + + # get process name as string + name = utility.array_to_string(task.comm) + + # try and get task parent + try: + ppid = task.parent.pid + except exceptions.InvalidAddressException: + vollog.debug(f"Unable to read parent pid for task {pid} {name}, setting ppid to 0.") + ppid = 0 + + # kernel threads never have an mm as they do not have userland mappings + try: + mm = task.mm + except exceptions.InvalidAddressException: + # no mm so cannot get envars + vollog.debug(f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars.") + mm = None + continue + + # if mm exists attempt to get envars + if mm: + + # get process layer to read envars from + proc_layer_name = task.add_process_layer() + if proc_layer_name is None: + vollog.debug(f"Unable to construct process layer for task {pid} {name}, will not extract any envars.") + continue + proc_layer = self.context.layers[proc_layer_name] + + + # get the size of the envars with sanity checking + envars_size = task.mm.env_end - task.mm.env_start + if not (0 < envars_size <= 8192): + vollog.debug(f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars.") + continue + + # attempt to read all envars data + try: + envar_data = proc_layer.read(task.mm.env_start, envars_size) + except exceptions.InvalidAddressException: + vollog.debug(f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars.") + continue + + # parse envar data, envars are null terminated, keys and values are separated by '=' + envar_data = envar_data.rstrip(b'\x00') + for envar_pair in envar_data.split(b'\x00'): + try: + key, value = envar_pair.decode().split('=', 1) + except ValueError: + vollog.debug(f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated") + continue + yield (0, (pid, ppid, name, key, value)) + + def run(self): + filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) + + return renderers.TreeGrid( + [("PID", int), ("PPID", int), ("COMM", str), ("KEY", str), ("VALUE", str)], + self._generator( + pslist.PsList.list_tasks( + self.context, self.config["kernel"], filter_func=filter_func + ) + ), + ) From a553a69efde143183c0580910dcc089cf0e060ed Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 21 Dec 2022 06:42:02 +0000 Subject: [PATCH 043/140] fix linting issues --- volatility3/framework/plugins/linux/envars.py | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/volatility3/framework/plugins/linux/envars.py b/volatility3/framework/plugins/linux/envars.py index b62400c45..028eb2a57 100644 --- a/volatility3/framework/plugins/linux/envars.py +++ b/volatility3/framework/plugins/linux/envars.py @@ -12,6 +12,7 @@ from volatility3.plugins.linux import pslist vollog = logging.getLogger(__name__) + class Envars(plugins.PluginInterface): """Lists processes with their environment variables""" @@ -51,7 +52,9 @@ class Envars(plugins.PluginInterface): try: ppid = task.parent.pid except exceptions.InvalidAddressException: - vollog.debug(f"Unable to read parent pid for task {pid} {name}, setting ppid to 0.") + vollog.debug( + f"Unable to read parent pid for task {pid} {name}, setting ppid to 0." + ) ppid = 0 # kernel threads never have an mm as they do not have userland mappings @@ -59,7 +62,9 @@ class Envars(plugins.PluginInterface): mm = task.mm except exceptions.InvalidAddressException: # no mm so cannot get envars - vollog.debug(f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars.") + vollog.debug( + f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars." + ) mm = None continue @@ -69,31 +74,38 @@ class Envars(plugins.PluginInterface): # get process layer to read envars from proc_layer_name = task.add_process_layer() if proc_layer_name is None: - vollog.debug(f"Unable to construct process layer for task {pid} {name}, will not extract any envars.") + vollog.debug( + f"Unable to construct process layer for task {pid} {name}, will not extract any envars." + ) continue proc_layer = self.context.layers[proc_layer_name] - # get the size of the envars with sanity checking envars_size = task.mm.env_end - task.mm.env_start if not (0 < envars_size <= 8192): - vollog.debug(f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars.") + vollog.debug( + f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars." + ) continue # attempt to read all envars data try: envar_data = proc_layer.read(task.mm.env_start, envars_size) except exceptions.InvalidAddressException: - vollog.debug(f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars.") + vollog.debug( + f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars." + ) continue # parse envar data, envars are null terminated, keys and values are separated by '=' - envar_data = envar_data.rstrip(b'\x00') - for envar_pair in envar_data.split(b'\x00'): + envar_data = envar_data.rstrip(b"\x00") + for envar_pair in envar_data.split(b"\x00"): try: - key, value = envar_pair.decode().split('=', 1) + key, value = envar_pair.decode().split("=", 1) except ValueError: - vollog.debug(f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated") + vollog.debug( + f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated" + ) continue yield (0, (pid, ppid, name, key, value)) From c1e425217bf8ea0e4b62a56ddaff5db29d87b4f0 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 5 Jan 2023 10:20:19 +0000 Subject: [PATCH 044/140] Initial canonical helper addition The intel 64-bit 4-page paging mechanism allows for 48-bit virtual addresses. They introduced a convention that the higher bits must be set a particular way to avoid operating system developers abusing those bits and creating problems that would be difficult to resolve in the future. Volatility requires that addresses for mapping or translation fit within the available bounds of the virtual address space. This unfortunately means that addresses that have the protections against abuse in place can may live outside this range. This provides two function (canonicalize and decanonicalize) which will either set the appropriate sign extension or remove it. The decanonicalize function will return an adress outside of the address range if the original value was not canonical. --- volatility3/framework/layers/intel.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/volatility3/framework/layers/intel.py b/volatility3/framework/layers/intel.py index dce207fd5..ecfb6bf11 100644 --- a/volatility3/framework/layers/intel.py +++ b/volatility3/framework/layers/intel.py @@ -56,6 +56,11 @@ class Intel(linear.LinearlyMappedLayer): ) self._entry_size = struct.calcsize(self._entry_format) self._entry_number = self.page_size // self._entry_size + self._canonical_prefix = self._mask( + (1 << self._bits_per_register) - 1, + self._bits_per_register, + self._maxvirtaddr, + ) # These can vary depending on the type of space self._index_shift = int( @@ -106,6 +111,23 @@ class Intel(linear.LinearlyMappedLayer): """Returns whether a particular page is valid based on its entry.""" return bool(entry & 1) + def canonicalize(self, addr: int) -> int: + """Canonicalizes an address by performing an appropiate sign extension on the higher addresses""" + if self._bits_per_register <= self._maxvirtaddr: + return addr & self.address_mask + elif addr < (1 << self._maxvirtaddr - 1): + return addr + return self._mask(addr, self._maxvirtaddr, 0) + self._canonical_prefix + + def decanonicalize(self, addr: int) -> int: + """Removes canonicalization to ensure an adress fits within the correct range if it has been canonicalized + + This will produce an address outside the range if the canonicalization is incorrect + """ + if addr < (1 << self._maxvirtaddr - 1): + return addr + return addr ^ self._canonical_prefix + def _translate(self, offset: int) -> Tuple[int, int, str]: """Translates a specific offset based on paging tables. From 0163f0b9e67258d2a433766d0027ffc25d0b6d07 Mon Sep 17 00:00:00 2001 From: Eve Date: Thu, 5 Jan 2023 12:17:36 +0000 Subject: [PATCH 045/140] add linux.iomem plugin based on vol2 plugin by atcuno --- volatility3/framework/plugins/linux/iomem.py | 139 +++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 volatility3/framework/plugins/linux/iomem.py diff --git a/volatility3/framework/plugins/linux/iomem.py b/volatility3/framework/plugins/linux/iomem.py new file mode 100644 index 000000000..6b0469d60 --- /dev/null +++ b/volatility3/framework/plugins/linux/iomem.py @@ -0,0 +1,139 @@ +# This file is Copyright 2023 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 renderers, interfaces, exceptions +from volatility3.framework.configuration import requirements +from volatility3.framework.objects import utility +from volatility3.framework.renderers import format_hints + +vollog = logging.getLogger(__name__) + + +class IOMem(interfaces.plugins.PluginInterface): + """Generates an output similar to /proc/iomem on a running system.""" + + _required_framework_version = (2, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [ + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=["Intel32", "Intel64"], + ) + ] + + @classmethod + def parse_resource( + cls, + context: interfaces.context.ContextInterface, + vmlinux_module_name: str, + resource_offset: int, + seen: set = set(), + depth: int = 0, + ): + """Recursively parse from a root resource to find details about all related resources. + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + vmlinux_module_name: The name of the kernel module on which to operate + resource_offset: The offset to the resouce to be parsed + seen: The set of resource offsets that have already been parsed + depth: How deep into the resource structure we are + + Yields: + Each row of output + """ + # create the resource object + vmlinux = context.modules[vmlinux_module_name] + resource = vmlinux.object("resource", resource_offset) + + # extract the information required for this resource + name = utility.pointer_to_string(resource.name, 128) + start = format_hints.Hex(resource.start) + end = format_hints.Hex(resource.end) + + # mark this resource as seen in the seen set. Normally this should not be needed but will protect + # against possible infinite loops. Warn the user if an infinite loop would have happened. + if resource_offset in seen: + vollog.warning( + f"The resource object at {resource_offset:#x} '{name}' has already been processed, " + "this should not normally occur. No further results from related resources will be " + "displayed to protect against infinite loops." + ) + return None + else: + seen.add(resource_offset) + + # yield information on this resource + yield depth, (name, start, end) + + # process child resource if this exists + if resource.child != 0: + yield from cls.parse_resource( + context, + vmlinux_module_name, + resource.child, + seen, + depth + 1, + ) + + # process sibling resource if this exists + if resource.sibling != 0: + yield from cls.parse_resource( + context, + vmlinux_module_name, + resource.sibling, + seen, + depth, + ) + + def _generator(self): + """Generates an output similar to /proc/iomem on a running system + + Args: + None + + Yields: + Each row of output using the parse_resource function + """ + + # get the kernel module from the current context + vmlinux_module_name = self.config["kernel"] + vmlinux = self.context.modules[vmlinux_module_name] + + # check that the iomem_resource symbol exists + # normally exported in /kernel/resource.c + try: + iomem_root_offset = vmlinux.get_absolute_symbol_address("iomem_resource") + except exceptions.SymbolError: + iomem_root_offset = None + + # error if 'iomem_resource' is not found + if not iomem_root_offset: + raise TypeError( + "This plugin requires the iomem_resource structure. This structure is not present in the supplied symbol table. This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." + ) + + # error if type 'resource' is not found + if not vmlinux.has_type("resource"): + raise TypeError( + "This plugin requires the resource type. This type is not present in the supplied symbol table. This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." + ) + + # recursively parse the resources starting from the root resource at 'iomem_resource' + yield from self.parse_resource( + self.context, vmlinux_module_name, iomem_root_offset + ) + + def run(self): + columns = [ + ("NAME", str), + ("START", format_hints.Hex), + ("END", format_hints.Hex), + ] + return renderers.TreeGrid(columns, self._generator()) From af3b70320ecbbfc38c8ce51cc2aacf7ccd584580 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Fri, 6 Jan 2023 10:18:17 +0000 Subject: [PATCH 046/140] linux: Apply black linting to outstanding files --- .../framework/constants/linux/__init__.py | 20 +- volatility3/framework/plugins/linux/lsof.py | 27 +-- .../framework/plugins/linux/sockstat.py | 192 +++++++++++++----- .../framework/symbols/linux/__init__.py | 60 +++--- .../symbols/linux/extensions/__init__.py | 29 ++- 5 files changed, 219 insertions(+), 109 deletions(-) diff --git a/volatility3/framework/constants/linux/__init__.py b/volatility3/framework/constants/linux/__init__.py index 0c4d3c376..1b133eb42 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -13,7 +13,7 @@ PAGE_SHIFT = 12 """The value hard coded from the Linux Kernel (hence not extracted from the layer itself)""" # include/linux/sched.h -PF_KTHREAD = 0x00200000 # I'm a kernel thread +PF_KTHREAD = 0x00200000 # I'm a kernel thread # Standard well-defined IP protocols. # ref: include/uapi/linux/in.h @@ -139,13 +139,7 @@ SOCK_FAMILY = ( # Socket states # ref: include/uapi/linux/net.h -SOCKET_STATES = ( - "FREE", - "UNCONNECTED", - "CONNECTING", - "CONNECTED", - "DISCONNECTING" -) +SOCKET_STATES = ("FREE", "UNCONNECTED", "CONNECTING", "CONNECTED", "DISCONNECTING") # Netlink protocols # ref: include/uapi/linux/netlink.h @@ -188,17 +182,17 @@ ETH_PROTOCOLS = { 0x0007: "ETH_P_WAN_PPP", 0x0008: "ETH_P_PPP_MP", 0x0009: "ETH_P_LOCALTALK", - 0x000c: "ETH_P_CAN", - 0x000f: "ETH_P_CANFD", + 0x000C: "ETH_P_CAN", + 0x000F: "ETH_P_CANFD", 0x0010: "ETH_P_PPPTALK", 0x0011: "ETH_P_TR_802_2", 0x0016: "ETH_P_CONTROL", 0x0017: "ETH_P_IRDA", 0x0018: "ETH_P_ECONET", 0x0019: "ETH_P_HDLC", - 0x001a: "ETH_P_ARCNET", - 0x001b: "ETH_P_DSA", - 0x001c: "ETH_P_TRAILER", + 0x001A: "ETH_P_ARCNET", + 0x001B: "ETH_P_DSA", + 0x001C: "ETH_P_TRAILER", 0x0060: "ETH_P_LOOP", 0x00F6: "ETH_P_IEEE802154", 0x00F7: "ETH_P_CAIF", diff --git a/volatility3/framework/plugins/linux/lsof.py b/volatility3/framework/plugins/linux/lsof.py index 920aaf7f1..62bade1f1 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -46,10 +46,12 @@ class Lsof(plugins.PluginInterface): ] @classmethod - def list_fds(cls, - context: interfaces.context.ContextInterface, - symbol_table: str, - filter_func: Callable[[int], bool] = lambda _: False): + def list_fds( + cls, + context: interfaces.context.ContextInterface, + symbol_table: str, + filter_func: Callable[[int], bool] = lambda _: False, + ): linuxutils_symbol_table = None # type: ignore for task in pslist.PsList.list_tasks(context, symbol_table, filter_func): @@ -62,18 +64,17 @@ class Lsof(plugins.PluginInterface): pid = int(task.pid) fd_generator = linux.LinuxUtilities.files_descriptors_for_process( - context, - linuxutils_symbol_table, - task) + context, linuxutils_symbol_table, task + ) for fd_fields in fd_generator: yield pid, task_comm, task, fd_fields def _generator(self, pids, symbol_table): filter_func = pslist.PsList.create_pid_filter(pids) - fds_generator = self.list_fds(self.context, - symbol_table, - filter_func=filter_func) + fds_generator = self.list_fds( + self.context, symbol_table, filter_func=filter_func + ) for pid, task_comm, _task, fd_fields in fds_generator: fd_num, _filp, full_path = fd_fields @@ -82,8 +83,8 @@ class Lsof(plugins.PluginInterface): yield (0, fields) def run(self): - pids = self.config.get('pid', None) - symbol_table = self.config['kernel'] + pids = self.config.get("pid", None) + symbol_table = self.config["kernel"] tree_grid_args = [("PID", int), ("Process", str), ("FD", int), ("Path", str)] - return renderers.TreeGrid(tree_grid_args, self._generator(pids, symbol_table)) \ No newline at end of file + return renderers.TreeGrid(tree_grid_args, self._generator(pids, symbol_table)) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index ad3eee01f..f03a2ad8e 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -16,6 +16,7 @@ from volatility3.plugins.linux import lsof vollog = logging.getLogger(__name__) + class SockHandlers(interfaces.configuration.VersionableInterface): """Handles several socket families extracting the sockets information.""" @@ -56,7 +57,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): nethead = self._vmlinux.object_from_symbol(symbol_name="net_namespace_list") net_symname = self._vmlinux.symbol_table_name + constants.BANG + "net" for net in nethead.to_list(net_symname, "list"): - net_device_symname = self._vmlinux.symbol_table_name + constants.BANG + "net_device" + net_device_symname = ( + self._vmlinux.symbol_table_name + constants.BANG + "net_device" + ) for net_dev in net.dev_base_head.to_list(net_device_symname, "dev_list"): if net.get_inode() != netns_id: continue @@ -64,7 +67,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): netdevices_map[net_dev.ifindex] = dev_name return netdevices_map - def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str], Dict]: + def process_sock( + self, sock: objects.StructType + ) -> Tuple[objects.StructType, Tuple[str, str, str], Dict]: """Takes a kernel generic `sock` object and processes it with its respective socket family Args: @@ -86,7 +91,12 @@ class SockHandlers(interfaces.configuration.VersionableInterface): return unix_sock, sock_stat, socket_filter except exceptions.SymbolError as e: # Cannot finds the *_sock type in the symbols - vollog.log(constants.LOGLEVEL_V, "Error processing socket family '%s': %s", family, e) + vollog.log( + constants.LOGLEVEL_V, + "Error processing socket family '%s': %s", + family, + e, + ) else: vollog.log(constants.LOGLEVEL_V, "Unsupported family '%s'", family) @@ -100,7 +110,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): return sock, sock_stat, socket_filter - def _update_socket_filters_info(self, sock: objects.Pointer, socket_filter: dict) -> None: + def _update_socket_filters_info( + self, sock: objects.Pointer, socket_filter: dict + ) -> None: """Get information from the socket and reuseport filters Args: @@ -117,7 +129,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): socket_filter["filter_type"] = "reuseport_filter" self._extract_socket_filter_info(sock_reuseport_cb, socket_filter) - def _extract_socket_filter_info(self, sock_filter: objects.Pointer, socket_filter: dict) -> None: + def _extract_socket_filter_info( + self, sock_filter: objects.Pointer, socket_filter: dict + ) -> None: """Get specific information for each type of filter Args: @@ -146,7 +160,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): if bpfprog_name: socket_filter["bpf_filter_name"] = bpfprog_name - def _unix_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + def _unix_sock( + self, sock: objects.StructType + ) -> Tuple[objects.StructType, Tuple[str, str, str]]: """Handles the AF_UNIX socket family Args: @@ -171,7 +187,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = src_addr, src_port, dst_addr, dst_port, state return unix_sock, sock_stat - def _inet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + def _inet_sock( + self, sock: objects.StructType + ) -> Tuple[objects.StructType, Tuple[str, str, str]]: """Handles the AF_INET/6 socket families Args: @@ -191,7 +209,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = src_addr, src_port, dst_addr, dst_port, state return inet_sock, sock_stat - def _netlink_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + def _netlink_sock( + self, sock: objects.StructType + ) -> Tuple[objects.StructType, Tuple[str, str, str]]: """Handles the AF_NETLINK socket family Args: @@ -221,7 +241,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = src_addr, src_port, dst_addr, dst_port, state return netlink_sock, sock_stat - def _vsock_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + def _vsock_sock( + self, sock: objects.StructType + ) -> Tuple[objects.StructType, Tuple[str, str, str]]: """Handles the AF_VSOCK socket family Args: @@ -241,7 +263,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = src_addr, src_port, dst_addr, dst_port, state return vsock_sock, sock_stat - def _packet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + def _packet_sock( + self, sock: objects.StructType + ) -> Tuple[objects.StructType, Tuple[str, str, str]]: """Handles the AF_PACKET socket family Args: @@ -262,7 +286,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = src_addr, src_port, dst_addr, dst_port, state return packet_sock, sock_stat - def _xdp_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + def _xdp_sock( + self, sock: objects.StructType + ) -> Tuple[objects.StructType, Tuple[str, str, str]]: """Handles the AF_XDP socket family Args: @@ -304,7 +330,9 @@ class SockHandlers(interfaces.configuration.VersionableInterface): sock_stat = src_addr, src_port, dst_addr, dst_port, state return xdp_sock, sock_stat - def _bluetooth_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple[str, str, str]]: + def _bluetooth_sock( + self, sock: objects.StructType + ) -> Tuple[objects.StructType, Tuple[str, str, str]]: """Handles the AF_BLUETOOTH socket family Args: @@ -324,11 +352,17 @@ class SockHandlers(interfaces.configuration.VersionableInterface): if bt_protocol == "HCI": if self._vmlinux.has_type("hci_pinfo"): pinfo = bt_sock.cast("hci_pinfo") - if pinfo.has_member("hdev") and self._vmlinux.has_type("hci_dev") \ - and pinfo.hdev.has_member("dev_name"): + if ( + pinfo.has_member("hdev") + and self._vmlinux.has_type("hci_dev") + and pinfo.hdev.has_member("dev_name") + ): src_addr = utility.array_to_string(pinfo.hdev.dev_name) else: - vollog.log(constants.LOGLEVEL_V, "Type definition for 'hci_pinfo' is not available in the symbols") + vollog.log( + constants.LOGLEVEL_V, + "Type definition for 'hci_pinfo' is not available in the symbols", + ) elif bt_protocol == "L2CAP": if self._vmlinux.has_type("l2cap_pinfo"): pinfo = bt_sock.cast("l2cap_pinfo") @@ -337,7 +371,10 @@ class SockHandlers(interfaces.configuration.VersionableInterface): src_port = pinfo.chan.sport dst_port = pinfo.chan.psm else: - vollog.log(constants.LOGLEVEL_V, "Type definition for 'l2cap_pinfo' is not available in the symbols") + vollog.log( + constants.LOGLEVEL_V, + "Type definition for 'l2cap_pinfo' is not available in the symbols", + ) elif bt_protocol == "RFCOMM": if self._vmlinux.has_type("rfcomm_pinfo"): pinfo = bt_sock.cast("rfcomm_pinfo") @@ -345,22 +382,31 @@ class SockHandlers(interfaces.configuration.VersionableInterface): dst_addr = bt_addr(pinfo.dst) src_port = pinfo.channel else: - vollog.log(constants.LOGLEVEL_V, "Type definition for 'rfcomm_pinfo' is not available in the symbols") + vollog.log( + constants.LOGLEVEL_V, + "Type definition for 'rfcomm_pinfo' is not available in the symbols", + ) elif bt_protocol == "SCO": if self._vmlinux.has_type("sco_pinfo"): pinfo = bt_sock.cast("sco_pinfo") src_addr = bt_addr(pinfo.src) dst_addr = bt_addr(pinfo.dst) else: - vollog.log(constants.LOGLEVEL_V, "Type definition for 'sco_pinfo' is not available in the symbols") + vollog.log( + constants.LOGLEVEL_V, + "Type definition for 'sco_pinfo' is not available in the symbols", + ) else: - vollog.log(constants.LOGLEVEL_V, "Unsupported bluetooth protocol '%s'", bt_protocol) + vollog.log( + constants.LOGLEVEL_V, "Unsupported bluetooth protocol '%s'", bt_protocol + ) state = bt_sock.get_state() sock_stat = src_addr, src_port, dst_addr, dst_port, state return bt_sock, sock_stat + class Sockstat(plugins.PluginInterface): """Lists all network connections for all processes.""" @@ -371,31 +417,48 @@ class Sockstat(plugins.PluginInterface): @classmethod def get_requirements(cls): return [ - requirements.ModuleRequirement(name="kernel", description="Linux kernel", - architectures=["Intel32", "Intel64"]), - requirements.VersionRequirement(name="SockHandlers", component=SockHandlers, version=(1, 0, 0)), - requirements.PluginRequirement(name="lsof", plugin=lsof.Lsof, version=(1, 1, 0)), - requirements.VersionRequirement(name="linuxutils", component=linux.LinuxUtilities, version=(2, 0, 0)), - requirements.BooleanRequirement(name="unix", - description=("Show UNIX domain Sockets only"), - default=False, - optional=True), - requirements.ListRequirement(name="pids", - description="Filter results by process IDs. " - "It takes the root PID namespace identifiers.", - element_type=int, - optional=True), - requirements.IntRequirement(name="netns", - description="Filter results by network namespace. " - "Otherwise, all of them are shown.", - optional=True), + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=["Intel32", "Intel64"], + ), + requirements.VersionRequirement( + name="SockHandlers", component=SockHandlers, version=(1, 0, 0) + ), + requirements.PluginRequirement( + name="lsof", plugin=lsof.Lsof, version=(1, 1, 0) + ), + requirements.VersionRequirement( + name="linuxutils", component=linux.LinuxUtilities, version=(2, 0, 0) + ), + requirements.BooleanRequirement( + name="unix", + description=("Show UNIX domain Sockets only"), + default=False, + optional=True, + ), + requirements.ListRequirement( + name="pids", + description="Filter results by process IDs. " + "It takes the root PID namespace identifiers.", + element_type=int, + optional=True, + ), + requirements.IntRequirement( + name="netns", + description="Filter results by network namespace. " + "Otherwise, all of them are shown.", + optional=True, + ), ] @classmethod - def list_sockets(cls, - context: interfaces.context.ContextInterface, - symbol_table: str, - filter_func: Callable[[int], bool] = lambda _: False): + def list_sockets( + cls, + context: interfaces.context.ContextInterface, + symbol_table: str, + filter_func: Callable[[int], bool] = lambda _: False, + ): """Returns every single socket descriptor Args: @@ -433,7 +496,9 @@ class Sockstat(plugins.PluginInterface): if not d_inode: continue - socket_alloc = linux.LinuxUtilities.container_of(d_inode, "socket_alloc", "vfs_inode", vmlinux) + socket_alloc = linux.LinuxUtilities.container_of( + d_inode, "socket_alloc", "vfs_inode", vmlinux + ) socket = socket_alloc.socket if not (socket and socket.sk): @@ -466,7 +531,9 @@ class Sockstat(plugins.PluginInterface): Returns: `sock_stat` and `protocol` formatted. """ - sock_stat = [NotAvailableValue() if field is None else str(field) for field in sock_stat] + sock_stat = [ + NotAvailableValue() if field is None else str(field) for field in sock_stat + ] if protocol is None: protocol = NotAvailableValue() @@ -494,26 +561,49 @@ class Sockstat(plugins.PluginInterface): extended information such as socket filters, bpf info, etc. """ filter_func = lsof.pslist.PsList.create_pid_filter(pids) - socket_generator = self.list_sockets(self.context, symbol_table, filter_func=filter_func) + socket_generator = self.list_sockets( + self.context, symbol_table, filter_func=filter_func + ) - for task, netns_id, fd_num, family, sock_type, protocol, sock_fields in socket_generator: + for ( + task, + netns_id, + fd_num, + family, + sock_type, + protocol, + sock_fields, + ) in socket_generator: if netns_id_arg and netns_id_arg != netns_id: continue sock, sock_stat, extended = sock_fields sock_stat, protocol = self._format_fields(sock_stat, protocol) - socket_filter_str = ",".join(f"{k}={v}" for k, v in extended.items()) if extended else NotAvailableValue() + socket_filter_str = ( + ",".join(f"{k}={v}" for k, v in extended.items()) + if extended + else NotAvailableValue() + ) - fields = (netns_id, task.pid, fd_num, format_hints.Hex(sock.vol.offset), - family, sock_type, protocol, *sock_stat, socket_filter_str) + fields = ( + netns_id, + task.pid, + fd_num, + format_hints.Hex(sock.vol.offset), + family, + sock_type, + protocol, + *sock_stat, + socket_filter_str, + ) yield (0, fields) def run(self): - pids = self.config.get('pids') - netns_id = self.config['netns'] - symbol_table = self.config['kernel'] + pids = self.config.get("pids") + netns_id = self.config["netns"] + symbol_table = self.config["kernel"] tree_grid_args = [ ("NetNS", int), diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 1d1419ae9..0d7cbb7e4 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -17,38 +17,38 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): super().__init__(*args, **kwargs) # Set-up Linux specific types - self.set_type_class('file', extensions.struct_file) - self.set_type_class('list_head', extensions.list_head) - self.set_type_class('mm_struct', extensions.mm_struct) - self.set_type_class('super_block', extensions.super_block) - self.set_type_class('task_struct', extensions.task_struct) - self.set_type_class('vm_area_struct', extensions.vm_area_struct) - self.set_type_class('qstr', extensions.qstr) - self.set_type_class('dentry', extensions.dentry) - self.set_type_class('fs_struct', extensions.fs_struct) - self.set_type_class('files_struct', extensions.files_struct) - self.set_type_class('kobject', extensions.kobject) + self.set_type_class("file", extensions.struct_file) + self.set_type_class("list_head", extensions.list_head) + self.set_type_class("mm_struct", extensions.mm_struct) + self.set_type_class("super_block", extensions.super_block) + self.set_type_class("task_struct", extensions.task_struct) + self.set_type_class("vm_area_struct", extensions.vm_area_struct) + self.set_type_class("qstr", extensions.qstr) + self.set_type_class("dentry", extensions.dentry) + self.set_type_class("fs_struct", extensions.fs_struct) + self.set_type_class("files_struct", extensions.files_struct) + self.set_type_class("kobject", extensions.kobject) # Might not exist in the current symbols - self.optional_set_type_class('module', extensions.module) + self.optional_set_type_class("module", extensions.module) # Mount - self.set_type_class('vfsmount', extensions.vfsmount) + self.set_type_class("vfsmount", extensions.vfsmount) # Might not exist in older kernels or the current symbols - self.optional_set_type_class('mount', extensions.mount) - self.optional_set_type_class('mnt_namespace', extensions.mnt_namespace) + self.optional_set_type_class("mount", extensions.mount) + self.optional_set_type_class("mnt_namespace", extensions.mnt_namespace) # Network - self.set_type_class('net', extensions.net) - self.set_type_class('socket', extensions.socket) - self.set_type_class('sock', extensions.sock) - self.set_type_class('inet_sock', extensions.inet_sock) - self.set_type_class('unix_sock', extensions.unix_sock) + self.set_type_class("net", extensions.net) + self.set_type_class("socket", extensions.socket) + self.set_type_class("sock", extensions.sock) + self.set_type_class("inet_sock", extensions.inet_sock) + self.set_type_class("unix_sock", extensions.unix_sock) # Might not exist in older kernels or the current symbols - self.optional_set_type_class('netlink_sock', extensions.netlink_sock) - self.optional_set_type_class('vsock_sock', extensions.vsock_sock) - self.optional_set_type_class('packet_sock', extensions.packet_sock) - self.optional_set_type_class('bt_sock', extensions.bt_sock) - self.optional_set_type_class('xdp_sock', extensions.xdp_sock) + self.optional_set_type_class("netlink_sock", extensions.netlink_sock) + self.optional_set_type_class("vsock_sock", extensions.vsock_sock) + self.optional_set_type_class("packet_sock", extensions.packet_sock) + self.optional_set_type_class("bt_sock", extensions.bt_sock) + self.optional_set_type_class("xdp_sock", extensions.xdp_sock) class LinuxUtilities(interfaces.configuration.VersionableInterface): @@ -322,7 +322,11 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): @classmethod def container_of( - cls, addr: int, type_name: str, member_name: str, vmlinux: interfaces.context.ModuleInterface + cls, + addr: int, + type_name: str, + member_name: str, + vmlinux: interfaces.context.ModuleInterface, ) -> Optional[interfaces.objects.ObjectInterface]: """Cast a member of a structure out to the containing structure. It mimicks the Linux kernel macro container_of() see include/linux.kernel.h @@ -343,4 +347,6 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): type_dec = vmlinux.get_type(type_name) member_offset = type_dec.relative_child_offset(member_name) container_addr = addr - member_offset - return vmlinux.object(object_type=type_name, offset=container_addr, absolute=True) + return vmlinux.object( + object_type=type_name, offset=container_addr, absolute=True + ) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 0ea61a3bb..55f139730 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -812,6 +812,7 @@ class vfsmount(objects.StructType): def get_mnt_root(self): return self.mnt_root + class kobject(objects.StructType): def reference_count(self): refcnt = self.kref.refcount @@ -842,6 +843,7 @@ class mnt_namespace(objects.StructType): for mount in self.list.to_list(mnt_type, "mnt_list"): yield mount + class net(objects.StructType): def get_inode(self): if self.has_member("proc_inum"): @@ -851,12 +853,15 @@ class net(objects.StructType): else: raise AttributeError("Unable to find net_namespace inode") + class socket(objects.StructType): def _get_vol_kernel(self): symbol_table_arr = self.vol.type_name.split("!", 1) symbol_table = symbol_table_arr[0] if len(symbol_table_arr) == 2 else None - module_names = list(self._context.modules.get_modules_by_symbol_tables(symbol_table)) + module_names = list( + self._context.modules.get_modules_by_symbol_tables(symbol_table) + ) if not module_names: raise ValueError(f"No module using the symbol table {symbol_table}") @@ -870,7 +875,9 @@ class socket(objects.StructType): except ValueError: return 0 - socket_alloc = linux.LinuxUtilities.container_of(self.vol.offset, "socket_alloc", "socket", kernel) + socket_alloc = linux.LinuxUtilities.container_of( + self.vol.offset, "socket_alloc", "socket", kernel + ) vfs_inode = socket_alloc.vfs_inode return vfs_inode.i_ino @@ -880,6 +887,7 @@ class socket(objects.StructType): if 0 <= socket_state_idx < len(SOCKET_STATES): return SOCKET_STATES[socket_state_idx] + class sock(objects.StructType): def get_family(self): family_idx = self.__sk_common.skc_family @@ -905,6 +913,7 @@ class sock(objects.StructType): return self.sk_socket.get_state() + class unix_sock(objects.StructType): def get_name(self): if not self.addr: @@ -932,6 +941,7 @@ class unix_sock(objects.StructType): def get_inode(self): return self.sk.get_inode() + class inet_sock(objects.StructType): def get_family(self): family_idx = self.sk.__sk_common.skc_family @@ -966,7 +976,7 @@ class inet_sock(objects.StructType): def get_dst_port(self): sk_common = self.sk.__sk_common if hasattr(sk_common, "skc_portpair"): - dport_le = sk_common.skc_portpair & 0xffff + dport_le = sk_common.skc_portpair & 0xFFFF elif hasattr(self, "dport"): dport_le = self.dport elif hasattr(self, "inet_dport"): @@ -999,7 +1009,9 @@ class inet_sock(objects.StructType): try: addr_bytes = parent_layer.read(saddr.vol.offset, addr_size) except exceptions.InvalidAddressException: - vollog.debug(f"Unable to read socket src address from {saddr.vol.offset:#x}") + vollog.debug( + f"Unable to read socket src address from {saddr.vol.offset:#x}" + ) return return socket_module.inet_ntop(family, addr_bytes) @@ -1028,11 +1040,14 @@ class inet_sock(objects.StructType): try: addr_bytes = parent_layer.read(daddr.vol.offset, addr_size) except exceptions.InvalidAddressException: - vollog.debug(f"Unable to read socket dst address from {daddr.vol.offset:#x}") + vollog.debug( + f"Unable to read socket dst address from {daddr.vol.offset:#x}" + ) return return socket_module.inet_ntop(family, addr_bytes) + class netlink_sock(objects.StructType): def get_protocol(self): protocol_idx = self.sk.sk_protocol @@ -1043,6 +1058,7 @@ class netlink_sock(objects.StructType): # Return the generic socket state return self.sk.sk_socket.get_state() + class vsock_sock(objects.StructType): def get_protocol(self): # The protocol should always be 0 for vsocks @@ -1052,6 +1068,7 @@ class vsock_sock(objects.StructType): # Return the generic socket state return self.sk.sk_socket.get_state() + class packet_sock(objects.StructType): def get_protocol(self): eth_proto = socket_module.htons(self.num) @@ -1066,6 +1083,7 @@ class packet_sock(objects.StructType): # Return the generic socket state return self.sk.sk_socket.get_state() + class bt_sock(objects.StructType): def get_protocol(self): type_idx = self.sk.sk_protocol @@ -1077,6 +1095,7 @@ class bt_sock(objects.StructType): if 0 <= state_idx < len(BLUETOOTH_STATES): return BLUETOOTH_STATES[state_idx] + class xdp_sock(objects.StructType): def get_protocol(self): # The protocol should always be 0 for xdp_sock From cd89e39ee053f3ca9b3a8f93628da2526accc99f Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Fri, 6 Jan 2023 22:10:50 +0000 Subject: [PATCH 047/140] Layers: Fix QEMU layer cutting off the last byte of the config --- volatility3/framework/layers/qemu.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/layers/qemu.py b/volatility3/framework/layers/qemu.py index 829354987..501b8655e 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -117,13 +117,15 @@ class QemuSuspendLayer(segmented.NonLinearlySegmentedLayer): chunk_size = 4096 data = b"" for i in range( - base_layer.maximum_address, base_layer.minimum_address, -chunk_size + base_layer.maximum_address + 1, base_layer.minimum_address, -chunk_size ): - if i != base_layer.maximum_address: + # Since we're going backwards, we need to include one extra byte so the tail doesn't get chopped off + if i != base_layer.maximum_address + 1: data = (base_layer.read(i, chunk_size) + data).rstrip(b"\x00") if b"\x00" in data: last_null_byte = data.rfind(b"\x00") start_of_json = data.find(b"{", last_null_byte) + if start_of_json >= 0: data = data[start_of_json:] return json.loads(data) From a1eeecf6de088888c0509fb2165b7947e87dd868 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 9 Jan 2023 21:32:19 +0000 Subject: [PATCH 048/140] Layers: Fix uncaught exception in Elf layer --- volatility3/framework/layers/elf.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/layers/elf.py b/volatility3/framework/layers/elf.py index bcafa9aed..a10d36592 100644 --- a/volatility3/framework/layers/elf.py +++ b/volatility3/framework/layers/elf.py @@ -119,4 +119,8 @@ class Elf64Stacker(interfaces.automagic.StackerLayerInterface): interfaces.configuration.path_join(new_name, "base_layer") ] = layer_name - return Elf64Layer(context, new_name, new_name) + try: + return Elf64Layer(context, new_name, new_name) + except ElfFormatException as excp: + vollog.log(constants.LOGLEVEL_VVVV, f"Exception: {excp}") + return None From a60b91b6e18cb5979f2872ec61dc60af4a10faa1 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 9 Jan 2023 21:33:07 +0000 Subject: [PATCH 049/140] Layers: Add in Xen CoreDump format support --- volatility3/framework/layers/xen.py | 171 +++++++++++++++++++ volatility3/framework/symbols/linux/xen.json | 115 +++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 volatility3/framework/layers/xen.py create mode 100644 volatility3/framework/symbols/linux/xen.json diff --git a/volatility3/framework/layers/xen.py b/volatility3/framework/layers/xen.py new file mode 100644 index 000000000..33050ea4b --- /dev/null +++ b/volatility3/framework/layers/xen.py @@ -0,0 +1,171 @@ +import logging +import struct +from typing import Optional + +from volatility3.framework import constants, interfaces, exceptions +from volatility3.framework.layers import elf +from volatility3.framework.symbols import intermed + +vollog = logging.getLogger(__name__) + + +class XenCoreDumpLayer(elf.Elf64Layer): + """A layer that supports the Xen Dump-Core format as documented at: https://xenbits.xen.org/docs/4.6-testing/misc/dump-core-format.txt""" + + _header_struct = struct.Struct(" None: + # Create a custom SymbolSpace + self._elf_table_name = intermed.IntermediateSymbolTable.create( + context, config_path, "linux", "elf" + ) + self._xen_table_name = intermed.IntermediateSymbolTable.create( + context, config_path, "linux", "xen" + ) + + super().__init__(context, config_path, name) + + def _load_segments(self) -> None: + """Load the segments from based on the PT_LOAD segments of the Elf64 format""" + ehdr = self.context.object( + self._elf_table_name + constants.BANG + "Elf64_Ehdr", + layer_name=self._base_layer, + offset=0, + ) + + segments = [] + segment_headers = [] + + for sindex in range(ehdr.e_shnum): + shdr = self.context.object( + self._elf_table_name + constants.BANG + "Elf64_Shdr", + layer_name=self._base_layer, + offset=ehdr.e_shoff + (sindex * ehdr.e_shentsize), + ) + + segment_headers.append(shdr) + + if sindex == ehdr.e_shstrndx: + segment_names = self.context.layers[self._base_layer].read( + shdr.sh_offset, shdr.sh_size + ) + segment_names = segment_names.split(b"\x00") + + if not segment_names: + raise elf.ElfFormatException("No segment names, not a Xen Core Dump") + + p2m_data = None + pfn_data = None + + for varname, pattern, outvar in [ + ("xen_p2m", b".xen_p2m", p2m_data), + ("xen_pfn", b".xen_pfn", pfn_data), + ]: + if pattern in segment_names: + hdr = segment_headers[segment_names.index(pattern)] + result = self.context.object( + self._xen_table_name + constants.BANG + varname, + layer_name=self._base_layer, + offset=hdr.sh_offset, + size=hdr.sh_size, + ) + result.entries.count = hdr.sh_size // result.entries.vol.subtype.size + outvar = result + + pages_hdr = segment_headers[segment_names.index(b".xen_pages")] + page_size = 0x1000 + + if pfn_data and not p2m_data: + for entry_index in range(len(pfn_data.entries)): + entry = pfn_data.entries[entry_index] + # TODO: Don't hardcode the maximum value here + if entry and entry != 0xFFFFFFFF: + segments.append( + ( + entry * page_size, + pages_hdr.sh_offset + (entry_index * page_size), + page_size, + page_size, + ) + ) + elif p2m_data and not pfn_data: + for entry_index in range(len(p2m_data.entries)): + entry = p2m_data.entries[entry_index] + # TODO: Don't hardcode the maximum value here + if entry.pfn != 0xFFFFFFFF: + segments.append( + ( + entry.pfn * page_size, + pages_hdr.sh_offset + (entry_index * page_size), + page_size, + page_size, + ) + ) + elif p2m_data and pfn_data: + raise elf.ElfFormatException( + self.name, f"Both P2M and PFN in Xen Core Dump" + ) + else: + raise elf.ElfFormatException( + self.name, f"Neither P2M nor PFN in Xen Core Dump" + ) + + if len(segments) == 0: + raise elf.ElfFormatException( + self.name, f"No ELF segments defined in {self._base_layer}" + ) + + self._segments = segments + + @classmethod + def _check_header( + cls, base_layer: interfaces.layers.DataLayerInterface, offset: int = 0 + ) -> bool: + try: + header_data = base_layer.read(offset, cls._header_struct.size) + except exceptions.InvalidAddressException: + raise elf.ElfFormatException( + base_layer.name, + f"Offset 0x{offset:0x} does not exist within the base layer", + ) + (magic, elf_class, elf_data_encoding, elf_version) = cls._header_struct.unpack( + header_data + ) + if magic != cls.MAGIC: + raise elf.ElfFormatException( + base_layer.name, f"Bad magic 0x{magic:x} at file offset 0x{offset:x}" + ) + if elf_class != cls.ELF_CLASS: + raise elf.ElfFormatException( + base_layer.name, f"ELF class is not 64-bit (2): {elf_class:d}" + ) + # Virtualbox uses an ELF version of 0, which isn't to specification, but is ok to deal with + return True + + +class XenCoreDumpStacker(elf.Elf64Stacker): + stack_order = 10 + + @classmethod + def stack( + cls, + context: interfaces.context.ContextInterface, + layer_name: str, + progress_callback: constants.ProgressCallback = None, + ) -> Optional[interfaces.layers.DataLayerInterface]: + try: + if not XenCoreDumpLayer._check_header(context.layers[layer_name]): + return None + except elf.ElfFormatException as excp: + vollog.log(constants.LOGLEVEL_VVVV, f"Exception: {excp}") + return None + new_name = context.layers.free_layer_name("XenCoreDumpLayer") + context.config[ + interfaces.configuration.path_join(new_name, "base_layer") + ] = layer_name + + return XenCoreDumpLayer(context, new_name, new_name) diff --git a/volatility3/framework/symbols/linux/xen.json b/volatility3/framework/symbols/linux/xen.json new file mode 100644 index 000000000..8e843e728 --- /dev/null +++ b/volatility3/framework/symbols/linux/xen.json @@ -0,0 +1,115 @@ +{ + "symbols": { + }, + "user_types": { + "xen_p2m": { + "fields":{ + "entries": { + "offset": 0, + "type": { + "count": 1, + "kind": "array", + "subtype": { + "kind": "base", + "name": "unsigned long long" + } + } + } + }, + "kind": "struct", + "size": 8 + }, + "xen_pfn":{ + "fields":{ + "entries": { + "offset": 0, + "type": { + "count": 1, + "kind": "array", + "subtype": { + "kind": "base", + "name": "unsigned long long" + } + } + } + }, + "kind": "struct", + "size": 16 + }, + "xen_pfn_entry":{ + "fields":{ + "pfn":{ + "offset": 0, + "type": { + "kind": "base", + "name": "unsigned long long" + } + }, + "gmfn":{ + "offset": 8, + "type": { + "kind": "base", + "name": "unsigned long long" + } + } + }, + "kind": "struct", + "size": 16 + + } + }, + "enums": { + }, + "base_types": { + "unsigned char": { + "endian": "little", + "kind": "char", + "signed": false, + "size": 1 + }, + "unsigned short": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 2 + }, + "long": { + "endian": "little", + "kind": "int", + "signed": true, + "size": 4 + }, + "char": { + "endian": "little", + "kind": "char", + "signed": true, + "size": 1 + }, + "unsigned long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 4 + }, + "long long": { + "endian": "little", + "kind": "int", + "signed": true, + "size": 8 + }, + "unsigned long long": { + "endian": "little", + "kind": "int", + "signed": false, + "size": 8 + } + }, + "metadata": { + "producer": { + "version": "0.0.1", + "name": "ikelos-by-hand", + "datetime": "2023-01-09T00:51:00" + }, + "format": "6.1.0" + } +} From d963aa3afb702980552653e40d3b090c57bf06bf Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Tue, 10 Jan 2023 16:39:43 +0000 Subject: [PATCH 050/140] Layers: Fix trying to be too clever with Xen --- volatility3/framework/layers/xen.py | 49 +++++++++++++++++------------ 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/volatility3/framework/layers/xen.py b/volatility3/framework/layers/xen.py index 33050ea4b..f7881a091 100644 --- a/volatility3/framework/layers/xen.py +++ b/volatility3/framework/layers/xen.py @@ -26,9 +26,23 @@ class XenCoreDumpLayer(elf.Elf64Layer): self._xen_table_name = intermed.IntermediateSymbolTable.create( context, config_path, "linux", "xen" ) + self._segment_headers = {} super().__init__(context, config_path, name) + def _extract_result_array( + self, varname: str, segment_index: int + ) -> interfaces.objects.ObjectInterface: + hdr = self._segment_headers[segment_index] + result = self.context.object( + self._xen_table_name + constants.BANG + varname, + layer_name=self._base_layer, + offset=hdr.sh_offset, + size=hdr.sh_size, + ) + result.entries.count = hdr.sh_size // result.entries.vol.subtype.size + return result + def _load_segments(self) -> None: """Load the segments from based on the PT_LOAD segments of the Elf64 format""" ehdr = self.context.object( @@ -38,7 +52,7 @@ class XenCoreDumpLayer(elf.Elf64Layer): ) segments = [] - segment_headers = [] + self._segment_headers = [] for sindex in range(ehdr.e_shnum): shdr = self.context.object( @@ -47,7 +61,7 @@ class XenCoreDumpLayer(elf.Elf64Layer): offset=ehdr.e_shoff + (sindex * ehdr.e_shentsize), ) - segment_headers.append(shdr) + self._segment_headers.append(shdr) if sindex == ehdr.e_shstrndx: segment_names = self.context.layers[self._base_layer].read( @@ -58,25 +72,20 @@ class XenCoreDumpLayer(elf.Elf64Layer): if not segment_names: raise elf.ElfFormatException("No segment names, not a Xen Core Dump") - p2m_data = None - pfn_data = None + try: + p2m_data = self._extract_result_array( + "xen_p2m", segment_names.index(b".xen_p2m") + ) + except ValueError: + p2m_data = None + try: + pfn_data = self._extract_result_array( + "xen_pfn", segment_names.index(b".xen_pfn") + ) + except ValueError: + pfn_data = None - for varname, pattern, outvar in [ - ("xen_p2m", b".xen_p2m", p2m_data), - ("xen_pfn", b".xen_pfn", pfn_data), - ]: - if pattern in segment_names: - hdr = segment_headers[segment_names.index(pattern)] - result = self.context.object( - self._xen_table_name + constants.BANG + varname, - layer_name=self._base_layer, - offset=hdr.sh_offset, - size=hdr.sh_size, - ) - result.entries.count = hdr.sh_size // result.entries.vol.subtype.size - outvar = result - - pages_hdr = segment_headers[segment_names.index(b".xen_pages")] + pages_hdr = self._segment_headers[segment_names.index(b".xen_pages")] page_size = 0x1000 if pfn_data and not p2m_data: From 1641a6e4c43aaf8ff8baf993319c61fb6163206c Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 12 Jan 2023 19:58:51 +0000 Subject: [PATCH 051/140] Windows: Fix up black issue with vadinfo --- volatility3/framework/plugins/windows/vadinfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/vadinfo.py b/volatility3/framework/plugins/windows/vadinfo.py index 6cd453550..3214c7134 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -206,7 +206,7 @@ class VadInfo(interfaces.plugins.PluginInterface): if self.config.get("address", None) is not None: def filter_function(x: interfaces.objects.ObjectInterface) -> bool: - return not (x.get_start() <= self.config['address'] <= x.get_end()) + return not (x.get_start() <= self.config["address"] <= x.get_end()) filter_func = filter_function From bd291c43d5bc31409cb6ac920f6135c29b63c58f Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 16 Jan 2023 10:15:36 +0200 Subject: [PATCH 052/140] added debug message --- volatility3/framework/symbols/windows/pdbutil.py | 1 + 1 file changed, 1 insertion(+) diff --git a/volatility3/framework/symbols/windows/pdbutil.py b/volatility3/framework/symbols/windows/pdbutil.py index 1c3260fed..74fd0e4e8 100644 --- a/volatility3/framework/symbols/windows/pdbutil.py +++ b/volatility3/framework/symbols/windows/pdbutil.py @@ -54,6 +54,7 @@ class PDBUtility(interfaces.configuration.VersionableInterface): """ result = cls.get_guid_from_mz(context, layer_name, offset) if result is None: + vollog.debug(f"Could not get GUID for {hex(offset)}") return None guid, age, pdb_name = result if config_path is None: From 5aca49388eb83d20316d24add538dc79d8e34dee Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 16 Jan 2023 12:01:45 +0200 Subject: [PATCH 053/140] fix smearing --- volatility3/framework/plugins/windows/callbacks.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 3bde95cf3..98e09d925 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -203,7 +203,10 @@ class Callbacks(interfaces.plugins.PluginInterface): callback_list = ntkrnlmp.object(object_type="_LIST_ENTRY", offset=symbol_offset) for callback in callback_list.to_list(full_type_name, "Link"): - yield "CmRegisterCallbackEx", callback.Function, f"Altitude: {callback.Altitude.String}" + altitude = "-" + with contextlib.suppress(exceptions.InvalidAddressException): + altitude = callback.Altitude.String + yield "CmRegisterCallbackEx", callback.Function, f"Altitude: {altitude}" @classmethod def list_registry_callbacks( From 5e33a98481e019083f1f4a41d8a145e2e31742e0 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 16 Jan 2023 12:39:08 +0200 Subject: [PATCH 054/140] change default value to None --- volatility3/framework/plugins/windows/callbacks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 98e09d925..a1283d394 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -203,7 +203,7 @@ class Callbacks(interfaces.plugins.PluginInterface): callback_list = ntkrnlmp.object(object_type="_LIST_ENTRY", offset=symbol_offset) for callback in callback_list.to_list(full_type_name, "Link"): - altitude = "-" + altitude = None with contextlib.suppress(exceptions.InvalidAddressException): altitude = callback.Altitude.String yield "CmRegisterCallbackEx", callback.Function, f"Altitude: {altitude}" From 4cafc982f4972a8dace64589a0adcd7a02518d83 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 16 Jan 2023 10:47:09 +0000 Subject: [PATCH 055/140] Windows: Fix up callbacks typos and typing info --- volatility3/framework/plugins/windows/callbacks.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index a1283d394..6898935fc 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -82,7 +82,7 @@ class Callbacks(interfaces.plugins.PluginInterface): context: The context to retrieve required elements (layers, symbol tables) from layer_name: The name of the layer on which to operate symbol_table: The name of the table containing the kernel symbols - callback_table_name: The nae of the table containing the callback symbols + callback_table_name: The name of the table containing the callback symbols Yields: A name, location and optional detail string @@ -182,7 +182,7 @@ class Callbacks(interfaces.plugins.PluginInterface): layer_name: str, symbol_table: str, callback_table_name: str, - ) -> Iterable[Tuple[str, int, None]]: + ) -> Iterable[Tuple[str, int, Optional[str]]]: """ Lists all registry callbacks via the CallbackListHead. """ @@ -215,14 +215,14 @@ class Callbacks(interfaces.plugins.PluginInterface): layer_name: str, symbol_table: str, callback_table_name: str, - ) -> Iterable[Tuple[str, int, None]]: + ) -> Iterable[Tuple[str, int, Optional[str]]]: """Lists all registry callbacks. Args: context: The context to retrieve required elements (layers, symbol tables) from layer_name: The name of the layer on which to operate symbol_table: The name of the table containing the kernel symbols - callback_table_name: The nae of the table containing the callback symbols + callback_table_name: The name of the table containing the callback symbols Yields: A name, location and optional detail string @@ -272,7 +272,7 @@ class Callbacks(interfaces.plugins.PluginInterface): context: The context to retrieve required elements (layers, symbol tables) from layer_name: The name of the layer on which to operate symbol_table: The name of the table containing the kernel symbols - callback_table_name: The nae of the table containing the callback symbols + callback_table_name: The name of the table containing the callback symbols Yields: A name, location and optional detail string @@ -330,7 +330,7 @@ class Callbacks(interfaces.plugins.PluginInterface): context: The context to retrieve required elements (layers, symbol tables) from layer_name: The name of the layer on which to operate symbol_table: The name of the table containing the kernel symbols - callback_table_name: The nae of the table containing the callback symbols + callback_table_name: The name of the table containing the callback symbols Yields: A name, location and optional detail string From f036acdeb8181a0cec6f4a73611080c8e14b5349 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 16 Jan 2023 13:02:20 +0200 Subject: [PATCH 056/140] missing import --- volatility3/framework/plugins/windows/callbacks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 6898935fc..56609a73a 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -3,6 +3,7 @@ # import logging +import contextlib from typing import List, Iterable, Tuple, Optional, Union from volatility3.framework import constants, exceptions, renderers, interfaces, symbols From e16887414e97cb9b2ef414a9ce15071fa9ac18f4 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Tue, 17 Jan 2023 10:15:03 +0200 Subject: [PATCH 057/140] add escapechar --- volatility3/cli/text_renderer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index 5df378d08..bb0f41ca3 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -241,7 +241,7 @@ class CSVRenderer(CLIRenderer): # Ignore the type because namedtuples don't realize they have accessible attributes header_list.append(f"{column.name}") - writer = csv.DictWriter(outfd, header_list, lineterminator="\n") + writer = csv.DictWriter(outfd, header_list, lineterminator="\n", escapechar='\\') writer.writeheader() def visitor(node: interfaces.renderers.TreeNode, accumulator): From 728e8b608ab59e4fa43353f4f2dea9f378b0e06a Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Tue, 17 Jan 2023 16:11:08 +0200 Subject: [PATCH 058/140] black reformat --- volatility3/cli/text_renderer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index bb0f41ca3..7e2167b6b 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -241,7 +241,9 @@ class CSVRenderer(CLIRenderer): # Ignore the type because namedtuples don't realize they have accessible attributes header_list.append(f"{column.name}") - writer = csv.DictWriter(outfd, header_list, lineterminator="\n", escapechar='\\') + writer = csv.DictWriter( + outfd, header_list, lineterminator="\n", escapechar="\\" + ) writer.writeheader() def visitor(node: interfaces.renderers.TreeNode, accumulator): From 1d4aa72c85c07f4bdad05de8abe1ecc8c053e760 Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 27 Jan 2023 11:07:05 +0000 Subject: [PATCH 059/140] update linux.iomem with basic smear protection --- volatility3/framework/plugins/linux/iomem.py | 23 +++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/linux/iomem.py b/volatility3/framework/plugins/linux/iomem.py index 6b0469d60..08a61bb46 100644 --- a/volatility3/framework/plugins/linux/iomem.py +++ b/volatility3/framework/plugins/linux/iomem.py @@ -48,15 +48,32 @@ class IOMem(interfaces.plugins.PluginInterface): Yields: Each row of output """ - # create the resource object vmlinux = context.modules[vmlinux_module_name] - resource = vmlinux.object("resource", resource_offset) + + # create the resource object with protection against memory smear + try: + resource = vmlinux.object("resource", resource_offset) + except exceptions.InvalidAddressException: + vollog.warning( + f"Unable to create resource object at {resource_offset:#x}. This resource, " + "its sibling, and any of it's childern and will be missing from the output." + ) + return None # extract the information required for this resource - name = utility.pointer_to_string(resource.name, 128) start = format_hints.Hex(resource.start) end = format_hints.Hex(resource.end) + # get name with protection against smear as following a pointer + try: + name = utility.pointer_to_string(resource.name, 128) + except exceptions.InvalidAddressException: + vollog.warning( + "Unable to follow pointer to name for resource object at {resource_offset:#x}, " + "replaced with UnreadableValue" + ) + name = renderers.UnreadableValue() + # mark this resource as seen in the seen set. Normally this should not be needed but will protect # against possible infinite loops. Warn the user if an infinite loop would have happened. if resource_offset in seen: From c2a1afb0ba8305b34bed11defdd21b9d83b76deb Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 29 Jan 2023 12:42:17 +0000 Subject: [PATCH 060/140] Core: Update codeql action to only run once a week --- .github/workflows/codeql.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 078af2abf..b9300251a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,11 +12,6 @@ name: "CodeQL" on: - push: - branches: [ "develop" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "develop" ] schedule: - cron: '16 8 * * 0' From 3297ba02e7cd2d24dfabac35c24996f36ca86891 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 29 Jan 2023 12:46:06 +0000 Subject: [PATCH 061/140] Core: Rather than scheduling it daily, only do it on commits --- .github/workflows/codeql.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b9300251a..fa9bd7ef6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,8 +12,13 @@ name: "CodeQL" on: - schedule: - - cron: '16 8 * * 0' + push: + branches: [ "develop" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "develop" ] +# schedule: +# - cron: '16 8 * * 0' jobs: analyze: From b375c6f71d0a90d8b3d632edbde9d25f9c72c266 Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 1 Feb 2023 09:43:02 +0000 Subject: [PATCH 062/140] Update linux.iomem --- volatility3/framework/plugins/linux/iomem.py | 44 +++++++++++--------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/volatility3/framework/plugins/linux/iomem.py b/volatility3/framework/plugins/linux/iomem.py index 08a61bb46..fddea4668 100644 --- a/volatility3/framework/plugins/linux/iomem.py +++ b/volatility3/framework/plugins/linux/iomem.py @@ -16,6 +16,7 @@ class IOMem(interfaces.plugins.PluginInterface): """Generates an output similar to /proc/iomem on a running system.""" _required_framework_version = (2, 0, 0) + _version = (1, 0, 0) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -60,10 +61,6 @@ class IOMem(interfaces.plugins.PluginInterface): ) return None - # extract the information required for this resource - start = format_hints.Hex(resource.start) - end = format_hints.Hex(resource.end) - # get name with protection against smear as following a pointer try: name = utility.pointer_to_string(resource.name, 128) @@ -87,7 +84,7 @@ class IOMem(interfaces.plugins.PluginInterface): seen.add(resource_offset) # yield information on this resource - yield depth, (name, start, end) + yield depth, (name, resource.start, resource.end) # process child resource if this exists if resource.child != 0: @@ -123,17 +120,32 @@ class IOMem(interfaces.plugins.PluginInterface): vmlinux_module_name = self.config["kernel"] vmlinux = self.context.modules[vmlinux_module_name] - # check that the iomem_resource symbol exists - # normally exported in /kernel/resource.c + # get the address for the iomem_resource try: iomem_root_offset = vmlinux.get_absolute_symbol_address("iomem_resource") except exceptions.SymbolError: iomem_root_offset = None - # error if 'iomem_resource' is not found - if not iomem_root_offset: + # only continue if iomem_root address was located + if iomem_root_offset is not None: + + # recursively parse the resources starting from the root resource at 'iomem_resource' + for depth, (name, start, end) in self.parse_resource( + self.context, vmlinux_module_name, iomem_root_offset + ): + # use format_hints to format start and end addresses for the renderers + yield depth, (name, format_hints.Hex(start), format_hints.Hex(end)) + + def run(self): + # get the kernel module from the current context + vmlinux_module_name = self.config["kernel"] + vmlinux = self.context.modules[vmlinux_module_name] + + # check that the iomem_resource symbol exists + # normally exported in /kernel/resource.c + if not vmlinux.has_symbol("iomem_resource"): raise TypeError( - "This plugin requires the iomem_resource structure. This structure is not present in the supplied symbol table. This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." + "This plugin requires the iomem_resource symbol. This symbol is not present in the supplied symbol table. This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." ) # error if type 'resource' is not found @@ -142,15 +154,9 @@ class IOMem(interfaces.plugins.PluginInterface): "This plugin requires the resource type. This type is not present in the supplied symbol table. This means you are either analyzing an unsupported kernel version or that your symbol table is corrupt." ) - # recursively parse the resources starting from the root resource at 'iomem_resource' - yield from self.parse_resource( - self.context, vmlinux_module_name, iomem_root_offset - ) - - def run(self): columns = [ - ("NAME", str), - ("START", format_hints.Hex), - ("END", format_hints.Hex), + ("Name", str), + ("Start", format_hints.Hex), + ("End", format_hints.Hex), ] return renderers.TreeGrid(columns, self._generator()) From 43a17384c32da91d711f9f186a3036bab43a3954 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Fri, 3 Feb 2023 00:35:49 +0000 Subject: [PATCH 063/140] Core: Update to black 23.1.0 which removes many blank lines and parentheses --- volatility3/cli/__init__.py | 2 +- volatility3/cli/text_renderer.py | 2 +- volatility3/cli/volargparse.py | 1 - volatility3/framework/automagic/construct_layers.py | 1 - volatility3/framework/automagic/mac.py | 1 - volatility3/framework/automagic/symbol_cache.py | 3 ++- volatility3/framework/automagic/symbol_finder.py | 4 ++-- volatility3/framework/interfaces/layers.py | 5 ++--- volatility3/framework/layers/avml.py | 2 +- volatility3/framework/layers/crash.py | 1 - volatility3/framework/layers/intel.py | 2 +- volatility3/framework/layers/leechcore.py | 1 - volatility3/framework/layers/linear.py | 4 ++-- volatility3/framework/layers/registry.py | 1 - volatility3/framework/plugins/linux/check_afinfo.py | 4 +--- volatility3/framework/plugins/linux/check_creds.py | 3 +-- volatility3/framework/plugins/linux/check_modules.py | 2 -- volatility3/framework/plugins/linux/check_syscall.py | 7 +++---- volatility3/framework/plugins/linux/lsmod.py | 1 - volatility3/framework/plugins/linux/lsof.py | 1 - volatility3/framework/plugins/linux/mountinfo.py | 1 - volatility3/framework/plugins/linux/tty_check.py | 2 -- volatility3/framework/plugins/mac/check_syscall.py | 2 +- volatility3/framework/plugins/mac/kauth_listeners.py | 1 - volatility3/framework/plugins/mac/kauth_scopes.py | 1 - volatility3/framework/plugins/mac/kevents.py | 1 - volatility3/framework/plugins/mac/list_files.py | 3 --- volatility3/framework/plugins/mac/lsmod.py | 2 -- volatility3/framework/plugins/mac/netstat.py | 2 -- volatility3/framework/plugins/mac/pslist.py | 1 - volatility3/framework/plugins/timeliner.py | 2 +- volatility3/framework/plugins/windows/bigpools.py | 1 - volatility3/framework/plugins/windows/cachedump.py | 1 - volatility3/framework/plugins/windows/callbacks.py | 5 ----- volatility3/framework/plugins/windows/devicetree.py | 2 +- volatility3/framework/plugins/windows/dlllist.py | 2 -- volatility3/framework/plugins/windows/driverirp.py | 2 -- volatility3/framework/plugins/windows/drivermodule.py | 1 - volatility3/framework/plugins/windows/driverscan.py | 1 - volatility3/framework/plugins/windows/dumpfiles.py | 1 - volatility3/framework/plugins/windows/envars.py | 1 - volatility3/framework/plugins/windows/filescan.py | 2 -- volatility3/framework/plugins/windows/getservicesids.py | 1 - volatility3/framework/plugins/windows/getsids.py | 3 --- volatility3/framework/plugins/windows/handles.py | 8 ++------ volatility3/framework/plugins/windows/hashdump.py | 1 - volatility3/framework/plugins/windows/info.py | 3 --- volatility3/framework/plugins/windows/joblinks.py | 2 +- volatility3/framework/plugins/windows/ldrmodules.py | 1 - volatility3/framework/plugins/windows/lsadump.py | 6 ------ volatility3/framework/plugins/windows/malfind.py | 1 - volatility3/framework/plugins/windows/mbrscan.py | 2 -- volatility3/framework/plugins/windows/modscan.py | 3 --- volatility3/framework/plugins/windows/modules.py | 1 - volatility3/framework/plugins/windows/mutantscan.py | 2 -- volatility3/framework/plugins/windows/netscan.py | 2 -- volatility3/framework/plugins/windows/netstat.py | 2 -- volatility3/framework/plugins/windows/poolscanner.py | 2 -- volatility3/framework/plugins/windows/privileges.py | 2 -- volatility3/framework/plugins/windows/pslist.py | 1 - volatility3/framework/plugins/windows/psscan.py | 2 -- .../framework/plugins/windows/registry/hivelist.py | 1 - .../framework/plugins/windows/registry/hivescan.py | 1 - .../framework/plugins/windows/registry/printkey.py | 4 +--- .../framework/plugins/windows/registry/userassist.py | 2 -- volatility3/framework/plugins/windows/sessions.py | 2 -- .../framework/plugins/windows/skeleton_key_check.py | 2 -- volatility3/framework/plugins/windows/ssdt.py | 3 --- volatility3/framework/plugins/windows/svcscan.py | 2 -- volatility3/framework/plugins/windows/symlinkscan.py | 2 -- volatility3/framework/plugins/windows/vadinfo.py | 1 - volatility3/framework/plugins/windows/verinfo.py | 1 - volatility3/framework/plugins/windows/virtmap.py | 2 +- volatility3/framework/renderers/__init__.py | 4 ++-- volatility3/framework/renderers/format_hints.py | 1 - volatility3/framework/symbols/__init__.py | 2 +- volatility3/framework/symbols/linux/__init__.py | 4 +--- .../framework/symbols/linux/extensions/__init__.py | 3 --- volatility3/framework/symbols/linux/extensions/elf.py | 1 - volatility3/framework/symbols/mac/__init__.py | 4 ---- volatility3/framework/symbols/mac/extensions/__init__.py | 4 ++-- .../framework/symbols/windows/extensions/__init__.py | 8 -------- .../framework/symbols/windows/extensions/network.py | 4 ---- volatility3/framework/symbols/windows/extensions/pe.py | 1 - volatility3/framework/symbols/windows/pdbutil.py | 4 +--- volatility3/plugins/windows/registry/certificates.py | 1 - 86 files changed, 32 insertions(+), 162 deletions(-) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index fb124a3c9..336902d50 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -443,7 +443,7 @@ class CommandLine: # Construct and run the plugin if constructed: renderers[args.renderer]().render(constructed.run()) - except (exceptions.VolatilityException) as excp: + except exceptions.VolatilityException as excp: self.process_exceptions(excp) @classmethod diff --git a/volatility3/cli/text_renderer.py b/volatility3/cli/text_renderer.py index 5df378d08..4df04e2a3 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -346,7 +346,7 @@ class PrettyTextRenderer(CLIRenderer): column_titles = [""] + [column.name for column in grid.columns] outfd.write(format_string.format(*column_titles)) - for (depth, line) in final_output: + for depth, line in final_output: nums_line = max([len(line[column]) for column in line]) for column in line: line[column] = line[column] + ([""] * (nums_line - len(line[column]))) diff --git a/volatility3/cli/volargparse.py b/volatility3/cli/volargparse.py index dd89a64fd..3048a0885 100644 --- a/volatility3/cli/volargparse.py +++ b/volatility3/cli/volargparse.py @@ -31,7 +31,6 @@ class HelpfulSubparserAction(argparse._SubParsersAction): values: Union[str, Sequence[Any], None], option_string: Optional[str] = None, ) -> None: - parser_name = "" arg_strings = [] # type: List[str] if values is not None: diff --git a/volatility3/framework/automagic/construct_layers.py b/volatility3/framework/automagic/construct_layers.py index ceed2fe50..239f0cfb6 100644 --- a/volatility3/framework/automagic/construct_layers.py +++ b/volatility3/framework/automagic/construct_layers.py @@ -36,7 +36,6 @@ class ConstructionMagic(interfaces.automagic.AutomagicInterface): progress_callback=None, optional=False, ) -> List[str]: - # Make sure we import the layers, so they can reconstructed framework.import_files(sys.modules["volatility3.framework.layers"]) diff --git a/volatility3/framework/automagic/mac.py b/volatility3/framework/automagic/mac.py index 3ca0b4ea2..aa75fbc3d 100644 --- a/volatility3/framework/automagic/mac.py +++ b/volatility3/framework/automagic/mac.py @@ -251,7 +251,6 @@ class MacIntelStacker(interfaces.automagic.StackerLayerInterface): context=context, progress_callback=progress_callback, ): - banner = context.layers[layer_name].read(offset, 128) idx = banner.find(b"\x00") diff --git a/volatility3/framework/automagic/symbol_cache.py b/volatility3/framework/automagic/symbol_cache.py index 44a76506c..63c6fc7fa 100644 --- a/volatility3/framework/automagic/symbol_cache.py +++ b/volatility3/framework/automagic/symbol_cache.py @@ -161,7 +161,8 @@ class CacheManagerInterface(interfaces.configuration.VersionableInterface): """Returns ISF statistics based on the location Returns: - A tuple of base_types, types, enums, symbols, or None is location not found""" + A tuple of base_types, types, enums, symbols, or None is location not found + """ def get_hash(self, location: str) -> Optional[str]: """Returns the hash of the JSON from within a location ISF""" diff --git a/volatility3/framework/automagic/symbol_finder.py b/volatility3/framework/automagic/symbol_finder.py index 0143e74b1..f30dff456 100644 --- a/volatility3/framework/automagic/symbol_finder.py +++ b/volatility3/framework/automagic/symbol_finder.py @@ -82,13 +82,13 @@ class SymbolFinder(interfaces.automagic.AutomagicInterface): shortcut=False, ) - for (sub_path, requirement) in self._requirements: + for sub_path, requirement in self._requirements: parent_path = interfaces.configuration.parent_path(sub_path) if isinstance( requirement, requirements.SymbolTableRequirement ) and requirement.unsatisfied(context, parent_path): - for (tl_sub_path, tl_requirement) in self._requirements: + for tl_sub_path, tl_requirement in self._requirements: tl_parent_path = interfaces.configuration.parent_path(tl_sub_path) # Find the TranslationLayer sibling to the SymbolTableRequirement if ( diff --git a/volatility3/framework/interfaces/layers.py b/volatility3/framework/interfaces/layers.py index a3c31a953..68592f8cb 100644 --- a/volatility3/framework/interfaces/layers.py +++ b/volatility3/framework/interfaces/layers.py @@ -294,7 +294,7 @@ class DataLayerInterface( sections.""" result: List[Tuple[int, int]] = [] position = 0 - for (start, length) in sorted(sections): + for start, length in sorted(sections): if result and start <= position: initial_start, _ = result.pop() result.append((initial_start, (start + length) - initial_start)) @@ -375,7 +375,6 @@ class DataLayerInterface( def _scan_metric( self, _scanner: "ScannerInterface", sections: List[Tuple[int, int]] ) -> Callable[[int], float]: - if not sections: raise ValueError("Sections have no size, nothing to scan") last_section, last_length = sections[-1] @@ -551,7 +550,7 @@ class TranslationLayerInterface(DataLayerInterface, metaclass=ABCMeta): scanner.chunk_size + scanner.overlap DataLayers by default are assumed to have no holes """ - for (section_start, section_length) in sections: + for section_start, section_length in sections: output: List[Tuple[str, int, int]] = [] # Hold the offsets of each chunk (including how much has been filled) diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index b12fdd01c..66f3f0e4f 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -73,7 +73,7 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): ) segments, consumed = self._read_snappy_frames(chunk_data, end - start) # The returned segments are accurate the chunk_data that was passed in, but needs shifting - for (thing, mapped_offset, size, mapped_size, compressed) in segments: + for thing, mapped_offset, size, mapped_size, compressed in segments: self._segments.append( ( thing + start, diff --git a/volatility3/framework/layers/crash.py b/volatility3/framework/layers/crash.py index 64166cfba..8efd4f7c7 100644 --- a/volatility3/framework/layers/crash.py +++ b/volatility3/framework/layers/crash.py @@ -39,7 +39,6 @@ class WindowsCrashDump32Layer(segmented.SegmentedLayer): def __init__( self, context: interfaces.context.ContextInterface, config_path: str, name: str ) -> None: - # Construct these so we can use self.config self._context = context self._config_path = config_path diff --git a/volatility3/framework/layers/intel.py b/volatility3/framework/layers/intel.py index ecfb6bf11..478eb168f 100644 --- a/volatility3/framework/layers/intel.py +++ b/volatility3/framework/layers/intel.py @@ -173,7 +173,7 @@ class Intel(linear.LinearlyMappedLayer): ) # Run through the offset in various chunks - for (name, size, large_page) in self._structure: + for name, size, large_page in self._structure: # Check we're valid if not self._page_is_valid(entry): raise exceptions.PagedInvalidAddressException( diff --git a/volatility3/framework/layers/leechcore.py b/volatility3/framework/layers/leechcore.py index 73700dd3c..542fd6ca2 100644 --- a/volatility3/framework/layers/leechcore.py +++ b/volatility3/framework/layers/leechcore.py @@ -91,7 +91,6 @@ if HAS_LEECHCORE: chunk_size = size output = [] for entry in self.handle.memmap: - if ( entry["base"] + entry["size"] <= chunk_start or entry["base"] >= chunk_start + chunk_size diff --git a/volatility3/framework/layers/linear.py b/volatility3/framework/layers/linear.py index 19203eb66..47170df7b 100644 --- a/volatility3/framework/layers/linear.py +++ b/volatility3/framework/layers/linear.py @@ -42,7 +42,7 @@ class LinearlyMappedLayer(interfaces.layers.TranslationLayerInterface): length size.""" current_offset = offset output: List[bytes] = [] - for (offset, _, mapped_offset, mapped_length, layer) in self.mapping( + for offset, _, mapped_offset, mapped_length, layer in self.mapping( offset, length, ignore_errors=pad ): if not pad and offset > current_offset: @@ -71,7 +71,7 @@ class LinearlyMappedLayer(interfaces.layers.TranslationLayerInterface): underlying mapping.""" current_offset = offset length = len(value) - for (offset, _, mapped_offset, length, layer) in self.mapping(offset, length): + for offset, _, mapped_offset, length, layer in self.mapping(offset, length): if offset > current_offset: raise exceptions.InvalidAddressException( self.name, diff --git a/volatility3/framework/layers/registry.py b/volatility3/framework/layers/registry.py index 660e0a299..cc8ce1f4c 100644 --- a/volatility3/framework/layers/registry.py +++ b/volatility3/framework/layers/registry.py @@ -269,7 +269,6 @@ class RegistryHive(linear.LinearlyMappedLayer): def mapping( self, offset: int, length: int, ignore_errors: bool = False ) -> Iterable[Tuple[int, int, int, int, str]]: - if length < 0: raise ValueError("Mapping length of RegistryHive must be positive or zero") diff --git a/volatility3/framework/plugins/linux/check_afinfo.py b/volatility3/framework/plugins/linux/check_afinfo.py index c177ee642..90e714eaa 100644 --- a/volatility3/framework/plugins/linux/check_afinfo.py +++ b/volatility3/framework/plugins/linux/check_afinfo.py @@ -68,7 +68,6 @@ class Check_afinfo(plugins.PluginInterface): yield var_name, "show", var.seq_show def _generator(self): - vmlinux = self.context.modules[self.config["kernel"]] op_members = vmlinux.get_type("file_operations").members @@ -86,7 +85,7 @@ class Check_afinfo(plugins.PluginInterface): ) protocols = [tcp, udp] - for (struct_type, global_vars) in protocols: + for struct_type, global_vars in protocols: for global_var_name in global_vars: # this will lookup fail for the IPv6 protocols on kernels without IPv6 support try: @@ -104,7 +103,6 @@ class Check_afinfo(plugins.PluginInterface): yield 0, (name, member, format_hints.Hex(address)) def run(self): - return renderers.TreeGrid( [ ("Symbol Name", str), diff --git a/volatility3/framework/plugins/linux/check_creds.py b/volatility3/framework/plugins/linux/check_creds.py index 6d4e2bc8a..ab6ee4935 100644 --- a/volatility3/framework/plugins/linux/check_creds.py +++ b/volatility3/framework/plugins/linux/check_creds.py @@ -46,7 +46,6 @@ class Check_creds(interfaces.plugins.PluginInterface): tasks = pslist.PsList.list_tasks(self.context, vmlinux.name) for task in tasks: - cred_addr = task.cred.dereference().vol.offset if cred_addr not in creds: @@ -54,7 +53,7 @@ class Check_creds(interfaces.plugins.PluginInterface): creds[cred_addr].append(task.pid) - for (_, pids) in creds.items(): + for _, pids in creds.items(): if len(pids) > 1: pid_str = "" for pid in pids: diff --git a/volatility3/framework/plugins/linux/check_modules.py b/volatility3/framework/plugins/linux/check_modules.py index 766858888..9b3594c5e 100644 --- a/volatility3/framework/plugins/linux/check_modules.py +++ b/volatility3/framework/plugins/linux/check_modules.py @@ -37,7 +37,6 @@ class Check_modules(plugins.PluginInterface): def get_kset_modules( cls, context: interfaces.context.ContextInterface, vmlinux_name: str ): - vmlinux = context.modules[vmlinux_name] try: @@ -57,7 +56,6 @@ class Check_modules(plugins.PluginInterface): for kobj in module_kset.list.to_list( vmlinux.symbol_table_name + constants.BANG + "kobject", "entry" ): - mod_kobj = vmlinux.object( object_type="module_kobject", offset=kobj.vol.offset - kobj_off, diff --git a/volatility3/framework/plugins/linux/check_syscall.py b/volatility3/framework/plugins/linux/check_syscall.py index 6b11038ec..b1d2919f9 100644 --- a/volatility3/framework/plugins/linux/check_syscall.py +++ b/volatility3/framework/plugins/linux/check_syscall.py @@ -110,7 +110,7 @@ class Check_syscall(plugins.PluginInterface): vmlinux = self.context.modules[self.config["kernel"]] data = self.context.layers.read(vmlinux.layer_name, func_addr, 6) - for (address, size, mnemonic, op_str) in md.disasm_lite(data, func_addr): + for address, size, mnemonic, op_str in md.disasm_lite(data, func_addr): if mnemonic == "CMP": table_size = int(op_str.split(",")[1].strip()) & 0xFFFF break @@ -161,7 +161,7 @@ class Check_syscall(plugins.PluginInterface): ia32_info = self._get_table_info(vmlinux, "ia32_sys_call_table", ptr_sz) tables.append(("32bit", ia32_info)) - for (table_name, (tableaddr, tblsz)) in tables: + for table_name, (tableaddr, tblsz) in tables: table = vmlinux.object( object_type="array", subtype=vmlinux.get_type("pointer"), @@ -169,7 +169,7 @@ class Check_syscall(plugins.PluginInterface): count=tblsz, ) - for (i, call_addr) in enumerate(table): + for i, call_addr in enumerate(table): if not call_addr: continue @@ -196,7 +196,6 @@ class Check_syscall(plugins.PluginInterface): ) def run(self): - return renderers.TreeGrid( [ ("Table Address", format_hints.Hex), diff --git a/volatility3/framework/plugins/linux/lsmod.py b/volatility3/framework/plugins/linux/lsmod.py index 1c1e094c3..a65b0d00b 100644 --- a/volatility3/framework/plugins/linux/lsmod.py +++ b/volatility3/framework/plugins/linux/lsmod.py @@ -60,7 +60,6 @@ class Lsmod(plugins.PluginInterface): def _generator(self): try: for module in self.list_modules(self.context, self.config["kernel"]): - mod_size = module.get_init_size() + module.get_core_size() mod_name = utility.array_to_string(module.name) diff --git a/volatility3/framework/plugins/linux/lsof.py b/volatility3/framework/plugins/linux/lsof.py index 62bade1f1..d970ad8a9 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -52,7 +52,6 @@ class Lsof(plugins.PluginInterface): symbol_table: str, filter_func: Callable[[int], bool] = lambda _: False, ): - linuxutils_symbol_table = None # type: ignore for task in pslist.PsList.list_tasks(context, symbol_table, filter_func): if linuxutils_symbol_table is None: diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index c849d51c6..ebd6e55a0 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -203,7 +203,6 @@ class MountInfo(plugins.PluginInterface): mount_format: bool, per_namespace: bool, ) -> Iterable[Tuple[int, Tuple]]: - for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(tasks, per_namespace): if mnt_ns_ids and mnt_ns_id not in mnt_ns_ids: continue diff --git a/volatility3/framework/plugins/linux/tty_check.py b/volatility3/framework/plugins/linux/tty_check.py index dcc9f3e06..45238ef8c 100644 --- a/volatility3/framework/plugins/linux/tty_check.py +++ b/volatility3/framework/plugins/linux/tty_check.py @@ -61,7 +61,6 @@ class tty_check(plugins.PluginInterface): for tty in tty_drivers.to_list( vmlinux.symbol_table_name + constants.BANG + "tty_driver", "tty_drivers" ): - try: ttys = utility.array_of_pointers( tty.ttys.dereference(), @@ -73,7 +72,6 @@ class tty_check(plugins.PluginInterface): continue for tty_dev in ttys: - if tty_dev == 0: continue diff --git a/volatility3/framework/plugins/mac/check_syscall.py b/volatility3/framework/plugins/mac/check_syscall.py index a7a32e9ab..5c22e6463 100644 --- a/volatility3/framework/plugins/mac/check_syscall.py +++ b/volatility3/framework/plugins/mac/check_syscall.py @@ -47,7 +47,7 @@ class Check_syscall(plugins.PluginInterface): table = kernel.object_from_symbol(symbol_name="sysent") - for (i, ent) in enumerate(table): + for i, ent in enumerate(table): try: call_addr = ent.sy_call.dereference().vol.offset except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/mac/kauth_listeners.py b/volatility3/framework/plugins/mac/kauth_listeners.py index 0b945a8fd..ed43bfb42 100644 --- a/volatility3/framework/plugins/mac/kauth_listeners.py +++ b/volatility3/framework/plugins/mac/kauth_listeners.py @@ -49,7 +49,6 @@ class Kauth_listeners(interfaces.plugins.PluginInterface): for scope in kauth_scopes.Kauth_scopes.list_kauth_scopes( self.context, self.config["kernel"] ): - scope_name = utility.pointer_to_string(scope.ks_identifier, 128) for listener in scope.get_listeners(): diff --git a/volatility3/framework/plugins/mac/kauth_scopes.py b/volatility3/framework/plugins/mac/kauth_scopes.py index bfd7216a8..afb320a07 100644 --- a/volatility3/framework/plugins/mac/kauth_scopes.py +++ b/volatility3/framework/plugins/mac/kauth_scopes.py @@ -65,7 +65,6 @@ class Kauth_scopes(interfaces.plugins.PluginInterface): ) for scope in self.list_kauth_scopes(self.context, self.config["kernel"]): - callback = scope.ks_callback if callback == 0: continue diff --git a/volatility3/framework/plugins/mac/kevents.py b/volatility3/framework/plugins/mac/kevents.py index 74c5f6037..3b996bc0a 100644 --- a/volatility3/framework/plugins/mac/kevents.py +++ b/volatility3/framework/plugins/mac/kevents.py @@ -184,7 +184,6 @@ class Kevents(interfaces.plugins.PluginInterface): for task_name, pid, kn in self.list_kernel_events( self.context, self.config["kernel"], 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] diff --git a/volatility3/framework/plugins/mac/list_files.py b/volatility3/framework/plugins/mac/list_files.py index ede0fa32b..c18b0b7a2 100644 --- a/volatility3/framework/plugins/mac/list_files.py +++ b/volatility3/framework/plugins/mac/list_files.py @@ -137,7 +137,6 @@ class List_Files(plugins.PluginInterface): def _walk_mounts( cls, context: interfaces.context.ContextInterface, kernel_module_name: str ) -> Iterable[interfaces.objects.ObjectInterface]: - loop_vnodes = {} # iterate each vnode source from each mount @@ -186,7 +185,6 @@ class List_Files(plugins.PluginInterface): def list_files( cls, context: interfaces.context.ContextInterface, kernel_module_name: str ) -> Iterable[interfaces.objects.ObjectInterface]: - vnodes = cls._walk_mounts(context, kernel_module_name) for voff, (vnode_name, parent_offset, vnode) in vnodes.items(): @@ -196,7 +194,6 @@ class List_Files(plugins.PluginInterface): def _generator(self): for vnode, full_path in self.list_files(self.context, self.config["kernel"]): - yield (0, (format_hints.Hex(vnode.vol.offset), full_path)) def run(self): diff --git a/volatility3/framework/plugins/mac/lsmod.py b/volatility3/framework/plugins/mac/lsmod.py index 2cdd5e3de..2979e374b 100644 --- a/volatility3/framework/plugins/mac/lsmod.py +++ b/volatility3/framework/plugins/mac/lsmod.py @@ -63,7 +63,6 @@ class Lsmod(plugins.PluginInterface): seen: Set = set() while kmod != 0 and kmod not in seen and len(seen) < 1024: - kmod_obj = kmod.dereference() if not kernel_layer.is_valid(kmod_obj.vol.offset, kmod_obj.vol.size): @@ -81,7 +80,6 @@ class Lsmod(plugins.PluginInterface): def _generator(self): for module in self.list_modules(self.context, self.config["kernel"]): - mod_name = utility.array_to_string(module.name) mod_size = module.size diff --git a/volatility3/framework/plugins/mac/netstat.py b/volatility3/framework/plugins/mac/netstat.py index 581a9c67f..76bba25f6 100644 --- a/volatility3/framework/plugins/mac/netstat.py +++ b/volatility3/framework/plugins/mac/netstat.py @@ -68,7 +68,6 @@ class Netstat(plugins.PluginInterface): # 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, kernel_module_name, filter_func): - task_name = utility.array_to_string(task.p_comm) pid = task.p_pid @@ -101,7 +100,6 @@ class Netstat(plugins.PluginInterface): for task_name, pid, socket in self.list_sockets( self.context, self.config["kernel"], filter_func=filter_func ): - family = socket.get_family() if family == 1: diff --git a/volatility3/framework/plugins/mac/pslist.py b/volatility3/framework/plugins/mac/pslist.py index c2ae71e7e..1d97216bf 100644 --- a/volatility3/framework/plugins/mac/pslist.py +++ b/volatility3/framework/plugins/mac/pslist.py @@ -83,7 +83,6 @@ class PsList(interfaces.plugins.PluginInterface): @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 [] diff --git a/volatility3/framework/plugins/timeliner.py b/volatility3/framework/plugins/timeliner.py index 0776b6cc8..d1c1c0f70 100644 --- a/volatility3/framework/plugins/timeliner.py +++ b/volatility3/framework/plugins/timeliner.py @@ -136,7 +136,7 @@ class Timeliner(interfaces.plugins.PluginInterface): ) try: vollog.log(logging.INFO, f"Running {plugin_name}") - for (item, timestamp_type, timestamp) in plugin.generate_timeline(): + for item, timestamp_type, timestamp in plugin.generate_timeline(): times = self.timeline.get((plugin_name, item), {}) if times.get(timestamp_type, None) is not None: vollog.debug( diff --git a/volatility3/framework/plugins/windows/bigpools.py b/volatility3/framework/plugins/windows/bigpools.py index 1a51a0b81..393c2a417 100644 --- a/volatility3/framework/plugins/windows/bigpools.py +++ b/volatility3/framework/plugins/windows/bigpools.py @@ -141,7 +141,6 @@ class BigPools(interfaces.plugins.PluginInterface): tags=tags, show_free=self.config.get("show-free"), ): - num_bytes = big_pool.get_number_of_bytes() if not isinstance(num_bytes, interfaces.renderers.BaseAbsentValue): num_bytes = format_hints.Hex(num_bytes) diff --git a/volatility3/framework/plugins/windows/cachedump.py b/volatility3/framework/plugins/windows/cachedump.py index 7d3093ed7..a9b669add 100644 --- a/volatility3/framework/plugins/windows/cachedump.py +++ b/volatility3/framework/plugins/windows/cachedump.py @@ -173,7 +173,6 @@ class Cachedump(interfaces.plugins.PluginInterface): kernel.symbol_table_name, hive_offsets=None if offset is None else [offset], ): - if hive.get_name().split("\\")[-1].upper() == "SYSTEM": syshive = hive if hive.get_name().split("\\")[-1].upper() == "SECURITY": diff --git a/volatility3/framework/plugins/windows/callbacks.py b/volatility3/framework/plugins/windows/callbacks.py index 56609a73a..48b2e7c62 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -104,7 +104,6 @@ class Callbacks(interfaces.plugins.PluginInterface): ] for symbol_name, extended_list in symbol_names: - try: symbol_offset = ntkrnlmp.get_symbol(symbol_name).address except exceptions.SymbolError: @@ -354,7 +353,6 @@ class Callbacks(interfaces.plugins.PluginInterface): ) for callback in callback_record.Entry: - if not context.layers[layer_name].is_valid(callback.CallbackRoutine, 64): continue @@ -372,7 +370,6 @@ class Callbacks(interfaces.plugins.PluginInterface): yield "KeBugCheckCallbackListHead", callback.CallbackRoutine, component def _generator(self): - kernel = self.context.modules[self.config["kernel"]] callback_table_name = self.create_callback_table( @@ -397,7 +394,6 @@ class Callbacks(interfaces.plugins.PluginInterface): kernel.symbol_table_name, callback_table_name, ): - if callback_detail is None: detail = renderers.NotApplicableValue() else: @@ -451,7 +447,6 @@ class Callbacks(interfaces.plugins.PluginInterface): ) def run(self): - return renderers.TreeGrid( [ ("Type", str), diff --git a/volatility3/framework/plugins/windows/devicetree.py b/volatility3/framework/plugins/windows/devicetree.py index 2541629d5..6f39799c1 100644 --- a/volatility3/framework/plugins/windows/devicetree.py +++ b/volatility3/framework/plugins/windows/devicetree.py @@ -180,7 +180,7 @@ class DeviceTree(interfaces.plugins.PluginInterface): ), ) - except (exceptions.InvalidAddressException): + except exceptions.InvalidAddressException: vollog.log( constants.LOGLEVEL_VVVV, f"Invalid address identified in drivers and devices: {driver.vol.offset:x}", diff --git a/volatility3/framework/plugins/windows/dlllist.py b/volatility3/framework/plugins/windows/dlllist.py index c1593b836..d73cea652 100644 --- a/volatility3/framework/plugins/windows/dlllist.py +++ b/volatility3/framework/plugins/windows/dlllist.py @@ -129,12 +129,10 @@ class DllList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): nt_major_version == 6 and nt_minor_version >= 1 ) for proc in procs: - proc_id = proc.UniqueProcessId proc_layer_name = proc.add_process_layer() for entry in proc.load_order_modules(): - BaseDllName = FullDllName = renderers.UnreadableValue() with contextlib.suppress(exceptions.InvalidAddressException): BaseDllName = entry.BaseDllName.get_string() diff --git a/volatility3/framework/plugins/windows/driverirp.py b/volatility3/framework/plugins/windows/driverirp.py index 4d2c24dea..b5cd33db7 100644 --- a/volatility3/framework/plugins/windows/driverirp.py +++ b/volatility3/framework/plugins/windows/driverirp.py @@ -71,7 +71,6 @@ class DriverIrp(interfaces.plugins.PluginInterface): for driver in driverscan.DriverScan.scan_drivers( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: driver_name = driver.get_driver_name() except (ValueError, exceptions.InvalidAddressException): @@ -113,7 +112,6 @@ class DriverIrp(interfaces.plugins.PluginInterface): ) def run(self): - return renderers.TreeGrid( [ ("Offset", format_hints.Hex), diff --git a/volatility3/framework/plugins/windows/drivermodule.py b/volatility3/framework/plugins/windows/drivermodule.py index cc735db30..de827602e 100644 --- a/volatility3/framework/plugins/windows/drivermodule.py +++ b/volatility3/framework/plugins/windows/drivermodule.py @@ -73,7 +73,6 @@ class DriverModule(interfaces.plugins.PluginInterface): ) def run(self) -> renderers.TreeGrid: - return renderers.TreeGrid( [ ("Offset", format_hints.Hex), diff --git a/volatility3/framework/plugins/windows/driverscan.py b/volatility3/framework/plugins/windows/driverscan.py index d8df80702..24d81c3d5 100644 --- a/volatility3/framework/plugins/windows/driverscan.py +++ b/volatility3/framework/plugins/windows/driverscan.py @@ -54,7 +54,6 @@ class DriverScan(interfaces.plugins.PluginInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object diff --git a/volatility3/framework/plugins/windows/dumpfiles.py b/volatility3/framework/plugins/windows/dumpfiles.py index af9568897..38d55d15d 100755 --- a/volatility3/framework/plugins/windows/dumpfiles.py +++ b/volatility3/framework/plugins/windows/dumpfiles.py @@ -229,7 +229,6 @@ class DumpFiles(interfaces.plugins.PluginInterface): ) for proc in procs: - try: object_table = proc.ObjectTable except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/windows/envars.py b/volatility3/framework/plugins/windows/envars.py index a1dbd7665..66db03c9c 100644 --- a/volatility3/framework/plugins/windows/envars.py +++ b/volatility3/framework/plugins/windows/envars.py @@ -221,7 +221,6 @@ class Envars(interfaces.plugins.PluginInterface): ) def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) kernel = self.context.modules[self.config["kernel"]] diff --git a/volatility3/framework/plugins/windows/filescan.py b/volatility3/framework/plugins/windows/filescan.py index de3331e16..0f68f39d4 100644 --- a/volatility3/framework/plugins/windows/filescan.py +++ b/volatility3/framework/plugins/windows/filescan.py @@ -53,7 +53,6 @@ class FileScan(interfaces.plugins.PluginInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object @@ -63,7 +62,6 @@ class FileScan(interfaces.plugins.PluginInterface): for fileobj in self.scan_files( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: file_name = fileobj.FileName.String except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/windows/getservicesids.py b/volatility3/framework/plugins/windows/getservicesids.py index c4088426f..9b20ed2d0 100644 --- a/volatility3/framework/plugins/windows/getservicesids.py +++ b/volatility3/framework/plugins/windows/getservicesids.py @@ -73,7 +73,6 @@ class GetServiceSIDs(interfaces.plugins.PluginInterface): ] def _generator(self): - kernel = self.context.modules[self.config["kernel"]] # Get the system hive for hive in hivelist.HiveList.list_hives( diff --git a/volatility3/framework/plugins/windows/getsids.py b/volatility3/framework/plugins/windows/getsids.py index 2334a328d..3e332f85d 100644 --- a/volatility3/framework/plugins/windows/getsids.py +++ b/volatility3/framework/plugins/windows/getsids.py @@ -112,7 +112,6 @@ class GetSIDs(interfaces.plugins.PluginInterface): filter_string="config\\software", hive_offsets=None, ): - try: for subkey in hive.get_key(key).get_subkeys(): sid = str(subkey.get_name()) @@ -165,7 +164,6 @@ class GetSIDs(interfaces.plugins.PluginInterface): return sids def _generator(self, procs): - user_sids = self.lookup_user_sids() # Go all over the process list, get the token @@ -214,7 +212,6 @@ class GetSIDs(interfaces.plugins.PluginInterface): ) def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) kernel = self.context.modules[self.config["kernel"]] diff --git a/volatility3/framework/plugins/windows/handles.py b/volatility3/framework/plugins/windows/handles.py index 2f25a0597..dd7c90860 100644 --- a/volatility3/framework/plugins/windows/handles.py +++ b/volatility3/framework/plugins/windows/handles.py @@ -136,7 +136,6 @@ class Handles(interfaces.plugins.PluginInterface): """ if self._sar_value is None: - if not has_capstone: return None kernel = self.context.modules[self.config["kernel"]] @@ -160,7 +159,7 @@ class Handles(interfaces.plugins.PluginInterface): md = capstone.Cs(capstone.CS_ARCH_X86, capstone.CS_MODE_64) - for (address, size, mnemonic, op_str) in md.disasm_lite( + for address, size, mnemonic, op_str in md.disasm_lite( data, kvo + func_addr ): # print("{} {} {} {}".format(address, size, mnemonic, op_str)) @@ -300,7 +299,6 @@ class Handles(interfaces.plugins.PluginInterface): masked_offset = offset & layer_object.maximum_address for entry in table: - if level > 0: for x in self._make_handle_array(entry, level - 1, depth): yield x @@ -329,7 +327,6 @@ class Handles(interfaces.plugins.PluginInterface): continue def handles(self, handle_table): - try: TableCode = handle_table.TableCode & ~self._level_mask table_levels = handle_table.TableCode & self._level_mask @@ -395,7 +392,7 @@ class Handles(interfaces.plugins.PluginInterface): except (ValueError, exceptions.InvalidAddressException): obj_name = "" - except (exceptions.InvalidAddressException): + except exceptions.InvalidAddressException: vollog.log( constants.LOGLEVEL_VVV, f"Cannot access _OBJECT_HEADER at {entry.vol.offset:#x}", @@ -416,7 +413,6 @@ class Handles(interfaces.plugins.PluginInterface): ) def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) kernel = self.context.modules[self.config["kernel"]] diff --git a/volatility3/framework/plugins/windows/hashdump.py b/volatility3/framework/plugins/windows/hashdump.py index 72bea2c8b..0c98ab8ca 100644 --- a/volatility3/framework/plugins/windows/hashdump.py +++ b/volatility3/framework/plugins/windows/hashdump.py @@ -602,7 +602,6 @@ class Hashdump(interfaces.plugins.PluginInterface): kernel.symbol_table_name, hive_offsets=None if offset is None else [offset], ): - if hive.get_name().split("\\")[-1].upper() == "SYSTEM": syshive = hive if hive.get_name().split("\\")[-1].upper() == "SAM": diff --git a/volatility3/framework/plugins/windows/info.py b/volatility3/framework/plugins/windows/info.py index aa7837029..100a677c2 100644 --- a/volatility3/framework/plugins/windows/info.py +++ b/volatility3/framework/plugins/windows/info.py @@ -187,7 +187,6 @@ class Info(plugins.PluginInterface): return nt_header def _generator(self): - kernel = self.context.modules[self.config["kernel"]] layer_name = kernel.layer_name @@ -215,7 +214,6 @@ class Info(plugins.PluginInterface): yield (0, (layer.name, f"{i} {layer.__class__.__name__}")) if kdbg.Header.OwnerTag == 0x4742444B: - yield (0, ("KdDebuggerDataBlock", hex(kdbg.vol.offset))) yield (0, ("NTBuildLab", kdbg.get_build_lab())) yield (0, ("CSDVersion", str(kdbg.get_csdversion()))) @@ -285,5 +283,4 @@ class Info(plugins.PluginInterface): ) def run(self): - return TreeGrid([("Variable", str), ("Value", str)], self._generator()) diff --git a/volatility3/framework/plugins/windows/joblinks.py b/volatility3/framework/plugins/windows/joblinks.py index 354ef31c9..d84c133c0 100644 --- a/volatility3/framework/plugins/windows/joblinks.py +++ b/volatility3/framework/plugins/windows/joblinks.py @@ -103,7 +103,7 @@ class JobLinks(interfaces.plugins.PluginInterface): ), ) - except (exceptions.InvalidAddressException): + except exceptions.InvalidAddressException: continue def run(self) -> renderers.TreeGrid: diff --git a/volatility3/framework/plugins/windows/ldrmodules.py b/volatility3/framework/plugins/windows/ldrmodules.py index a9b229048..9642810a5 100644 --- a/volatility3/framework/plugins/windows/ldrmodules.py +++ b/volatility3/framework/plugins/windows/ldrmodules.py @@ -33,7 +33,6 @@ class LdrModules(interfaces.plugins.PluginInterface): ] def _generator(self, procs): - pe_table_name = intermed.IntermediateSymbolTable.create( self.context, self.config_path, "windows", "pe", class_types=pe.class_types ) diff --git a/volatility3/framework/plugins/windows/lsadump.py b/volatility3/framework/plugins/windows/lsadump.py index 8cb239905..12589b07e 100644 --- a/volatility3/framework/plugins/windows/lsadump.py +++ b/volatility3/framework/plugins/windows/lsadump.py @@ -118,12 +118,10 @@ class Lsadump(interfaces.plugins.PluginInterface): if enc_secret_key: enc_secret_value = next(enc_secret_key.get_values()) if enc_secret_value: - enc_secret = sechive.read( enc_secret_value.Data + 4, enc_secret_value.DataLength ) if enc_secret: - if not is_vista_or_later: secret = cls.decrypt_secret(enc_secret[0xC:], lsakey) else: @@ -160,7 +158,6 @@ class Lsadump(interfaces.plugins.PluginInterface): def _generator( self, syshive: registry.RegistryHive, sechive: registry.RegistryHive ): - kernel = self.context.modules[self.config["kernel"]] vista_or_later = versions.is_vista_or_later( @@ -183,7 +180,6 @@ class Lsadump(interfaces.plugins.PluginInterface): return for key in secrets_key.get_subkeys(): - sec_val_key = hashdump.Hashdump.get_hive_key( sechive, "Policy\\Secrets\\" + key.get_key_path().split("\\")[3] + "\\CurrVal", @@ -208,7 +204,6 @@ class Lsadump(interfaces.plugins.PluginInterface): yield (0, (key.get_name(), secret.decode("latin1"), secret)) def run(self): - offset = self.config.get("offset", None) syshive = sechive = None kernel = self.context.modules[self.config["kernel"]] @@ -220,7 +215,6 @@ class Lsadump(interfaces.plugins.PluginInterface): kernel.symbol_table_name, hive_offsets=None if offset is None else [offset], ): - if hive.get_name().split("\\")[-1].upper() == "SYSTEM": syshive = hive if hive.get_name().split("\\")[-1].upper() == "SECURITY": diff --git a/volatility3/framework/plugins/windows/malfind.py b/volatility3/framework/plugins/windows/malfind.py index 1e7a009eb..424925955 100644 --- a/volatility3/framework/plugins/windows/malfind.py +++ b/volatility3/framework/plugins/windows/malfind.py @@ -151,7 +151,6 @@ class Malfind(interfaces.plugins.PluginInterface): for vad, data in self.list_injections( self.context, kernel.layer_name, kernel.symbol_table_name, proc ): - # if we're on a 64 bit kernel, we may still need 32 bit disasm due to wow64 if is_32bit_arch or proc.get_is_wow64(): architecture = "intel" diff --git a/volatility3/framework/plugins/windows/mbrscan.py b/volatility3/framework/plugins/windows/mbrscan.py index ccf6eccea..e58ca8c24 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -99,7 +99,6 @@ class MBRScan(interfaces.plugins.PluginInterface): all_zeros = bootcode.count(b"\x00") == len(bootcode) if not all_zeros: - partition_entries = [ partition_table.FirstEntry, partition_table.SecondEntry, @@ -155,7 +154,6 @@ class MBRScan(interfaces.plugins.PluginInterface): for partition_index, partition_entry_object in enumerate( partition_entries, start=1 ): - if not self.config.get("full", True): yield ( 1, diff --git a/volatility3/framework/plugins/windows/modscan.py b/volatility3/framework/plugins/windows/modscan.py index bbd9a7b4a..99fadac07 100644 --- a/volatility3/framework/plugins/windows/modscan.py +++ b/volatility3/framework/plugins/windows/modscan.py @@ -70,7 +70,6 @@ class ModScan(interfaces.plugins.PluginInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object @@ -175,7 +174,6 @@ class ModScan(interfaces.plugins.PluginInterface): for mod in self.scan_modules( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: BaseDllName = mod.BaseDllName.get_string() except exceptions.InvalidAddressException: @@ -188,7 +186,6 @@ class ModScan(interfaces.plugins.PluginInterface): file_output = "Disabled" if self.config["dump"]: - session_layer_name = self.find_session_layer( self.context, session_layers, mod.DllBase ) diff --git a/volatility3/framework/plugins/windows/modules.py b/volatility3/framework/plugins/windows/modules.py index eba6d1ce7..ff61c215c 100644 --- a/volatility3/framework/plugins/windows/modules.py +++ b/volatility3/framework/plugins/windows/modules.py @@ -53,7 +53,6 @@ class Modules(interfaces.plugins.PluginInterface): for mod in self.list_modules( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: BaseDllName = mod.BaseDllName.get_string() except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/windows/mutantscan.py b/volatility3/framework/plugins/windows/mutantscan.py index ad6e024d1..64d3b5470 100644 --- a/volatility3/framework/plugins/windows/mutantscan.py +++ b/volatility3/framework/plugins/windows/mutantscan.py @@ -53,7 +53,6 @@ class MutantScan(interfaces.plugins.PluginInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object @@ -63,7 +62,6 @@ class MutantScan(interfaces.plugins.PluginInterface): for mutant in self.scan_mutants( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: name = mutant.get_name() except (ValueError, exceptions.InvalidAddressException): diff --git a/volatility3/framework/plugins/windows/netscan.py b/volatility3/framework/plugins/windows/netscan.py index 5c866bfeb..d0bbd5cbd 100644 --- a/volatility3/framework/plugins/windows/netscan.py +++ b/volatility3/framework/plugins/windows/netscan.py @@ -375,7 +375,6 @@ class NetScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, nt_symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object @@ -394,7 +393,6 @@ class NetScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): kernel.symbol_table_name, netscan_symbol_table, ): - vollog.debug( f"Found netw obj @ 0x{netw_obj.vol.offset:2x} of assumed type {type(netw_obj)}" ) diff --git a/volatility3/framework/plugins/windows/netstat.py b/volatility3/framework/plugins/windows/netstat.py index 1685f2a21..d3ce3fd2e 100644 --- a/volatility3/framework/plugins/windows/netstat.py +++ b/volatility3/framework/plugins/windows/netstat.py @@ -329,7 +329,6 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): alignment, net_symbol_table, ): - endpoint = context.object( obj_name, layer_name=layer_name, @@ -591,7 +590,6 @@ class NetStat(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): tcpip_module.DllBase, tcpip_symbol_table, ): - # objects passed pool header constraints. check for additional constraints if strict flag is set. if not show_corrupt_results and not netw_obj.is_valid(): continue diff --git a/volatility3/framework/plugins/windows/poolscanner.py b/volatility3/framework/plugins/windows/poolscanner.py index 13c611bf8..e131c5f78 100644 --- a/volatility3/framework/plugins/windows/poolscanner.py +++ b/volatility3/framework/plugins/windows/poolscanner.py @@ -144,7 +144,6 @@ class PoolScanner(plugins.PluginInterface): ] def _generator(self): - kernel = self.context.modules[self.config["kernel"]] symbol_table = kernel.symbol_table_name @@ -367,7 +366,6 @@ class PoolScanner(plugins.PluginInterface): for constraint, header in cls.pool_scan( context, scan_layer, symbol_table, constraints, alignment=alignment ): - mem_objects = header.get_object( constraint=constraint, use_top_down=is_windows_8_or_later, diff --git a/volatility3/framework/plugins/windows/privileges.py b/volatility3/framework/plugins/windows/privileges.py index 7a7087c95..0370dfc92 100644 --- a/volatility3/framework/plugins/windows/privileges.py +++ b/volatility3/framework/plugins/windows/privileges.py @@ -66,7 +66,6 @@ class Privs(interfaces.plugins.PluginInterface): ] def _generator(self, procs): - for task in procs: try: process_token = task.Token.dereference().cast("_TOKEN") @@ -107,7 +106,6 @@ class Privs(interfaces.plugins.PluginInterface): ) def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) kernel = self.context.modules[self.config["kernel"]] diff --git a/volatility3/framework/plugins/windows/pslist.py b/volatility3/framework/plugins/windows/pslist.py index 7a06af36f..88697e71a 100644 --- a/volatility3/framework/plugins/windows/pslist.py +++ b/volatility3/framework/plugins/windows/pslist.py @@ -226,7 +226,6 @@ class PsList(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): kernel.symbol_table_name, filter_func=self.create_pid_filter(self.config.get("pid", None)), ): - if not self.config.get("physical", self.PHYSICAL_DEFAULT): offset = proc.vol.offset else: diff --git a/volatility3/framework/plugins/windows/psscan.py b/volatility3/framework/plugins/windows/psscan.py index 427814d22..3d9ae5c1e 100644 --- a/volatility3/framework/plugins/windows/psscan.py +++ b/volatility3/framework/plugins/windows/psscan.py @@ -87,7 +87,6 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result if not filter_func(mem_object): yield mem_object @@ -192,7 +191,6 @@ class PsScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface): kernel.symbol_table_name, filter_func=pslist.PsList.create_pid_filter(self.config.get("pid", None)), ): - file_output = "Disabled" if self.config["dump"]: # windows 10 objects (maybe others in the future) are already in virtual memory diff --git a/volatility3/framework/plugins/windows/registry/hivelist.py b/volatility3/framework/plugins/windows/registry/hivelist.py index 4abcd2f15..91798de40 100644 --- a/volatility3/framework/plugins/windows/registry/hivelist.py +++ b/volatility3/framework/plugins/windows/registry/hivelist.py @@ -88,7 +88,6 @@ class HiveList(interfaces.plugins.PluginInterface): symbol_table=kernel.symbol_table_name, filter_string=self.config.get("filter", None), ): - file_output = "Disabled" if self.config["dump"]: # Construct the hive diff --git a/volatility3/framework/plugins/windows/registry/hivescan.py b/volatility3/framework/plugins/windows/registry/hivescan.py index c3a52e303..7b3c0b622 100644 --- a/volatility3/framework/plugins/windows/registry/hivescan.py +++ b/volatility3/framework/plugins/windows/registry/hivescan.py @@ -86,7 +86,6 @@ class HiveScan(interfaces.plugins.PluginInterface): for hive in self.scan_hives( self.context, kernel.layer_name, kernel.symbol_table_name ): - yield (0, (format_hints.Hex(hive.vol.offset),)) def run(self): diff --git a/volatility3/framework/plugins/windows/registry/printkey.py b/volatility3/framework/plugins/windows/registry/printkey.py index 19527321e..537bfc943 100644 --- a/volatility3/framework/plugins/windows/registry/printkey.py +++ b/volatility3/framework/plugins/windows/registry/printkey.py @@ -241,7 +241,6 @@ class PrintKey(interfaces.plugins.PluginInterface): key: str = None, recurse: bool = False, ): - for hive in hivelist.HiveList.list_hives( self.context, self.config_path, @@ -249,14 +248,13 @@ class PrintKey(interfaces.plugins.PluginInterface): symbol_table=symbol_table, hive_offsets=hive_offsets, ): - try: # Walk it if key is not None: node_path = hive.get_key(key, return_list=True) else: node_path = [hive.get_node(hive.root_cell_offset)] - for (x, y) in self._printkey_iterator(hive, node_path, recurse=recurse): + for x, y in self._printkey_iterator(hive, node_path, recurse=recurse): yield (x - len(node_path), y) except ( exceptions.InvalidAddressException, diff --git a/volatility3/framework/plugins/windows/registry/userassist.py b/volatility3/framework/plugins/windows/registry/userassist.py index f64a130fa..f90724f66 100644 --- a/volatility3/framework/plugins/windows/registry/userassist.py +++ b/volatility3/framework/plugins/windows/registry/userassist.py @@ -248,7 +248,6 @@ class UserAssist(interfaces.plugins.PluginInterface): # output any values under Count for value in countkey.get_values(): - value_name = value.get_name() with contextlib.suppress(UnicodeDecodeError): value_name = codecs.encode(value_name, "rot_13") @@ -281,7 +280,6 @@ class UserAssist(interfaces.plugins.PluginInterface): yield result def _generator(self): - hive_offsets = None if self.config.get("offset", None) is not None: hive_offsets = [self.config.get("offset", None)] diff --git a/volatility3/framework/plugins/windows/sessions.py b/volatility3/framework/plugins/windows/sessions.py index 3e15878bd..d766b40ea 100644 --- a/volatility3/framework/plugins/windows/sessions.py +++ b/volatility3/framework/plugins/windows/sessions.py @@ -51,7 +51,6 @@ class Sessions(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) kernel.symbol_table_name, filter_func=filter_func, ): - session_id = proc.get_session_id() # Detect RDP, Console or set default value @@ -112,7 +111,6 @@ class Sessions(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterface) yield (description, timeliner.TimeLinerType.CREATED, row_data[5]) def run(self): - return renderers.TreeGrid( [ ("Session ID", int), diff --git a/volatility3/framework/plugins/windows/skeleton_key_check.py b/volatility3/framework/plugins/windows/skeleton_key_check.py index e7a1820e4..b697774cb 100644 --- a/volatility3/framework/plugins/windows/skeleton_key_check.py +++ b/volatility3/framework/plugins/windows/skeleton_key_check.py @@ -187,7 +187,6 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface): proc_layer_name: str, cryptdll_base: int, ) -> Tuple[interfaces.objects.ObjectInterface, int, int, int]: - """ Finds the CSystems array through use of PDB symbols @@ -574,7 +573,6 @@ class Skeleton_Key_Check(interfaces.plugins.PluginInterface): scanners.BytesScanner(b"\x17\x00\x00\x00\x01\x00\x00\x00"), sections=[(cryptdll_base, cryptdll_size)], ): - # this occurs across page boundaries if not proc_layer.is_valid(address, ecrypt_size): continue diff --git a/volatility3/framework/plugins/windows/ssdt.py b/volatility3/framework/plugins/windows/ssdt.py index 184d8388c..6a47c36e9 100644 --- a/volatility3/framework/plugins/windows/ssdt.py +++ b/volatility3/framework/plugins/windows/ssdt.py @@ -56,7 +56,6 @@ class SSDT(plugins.PluginInterface): context_modules = [] for mod in mods: - try: module_name_with_ext = mod.BaseDllName.get_string() except exceptions.InvalidAddressException: @@ -83,7 +82,6 @@ class SSDT(plugins.PluginInterface): return contexts.ModuleCollection(context_modules) def _generator(self) -> Iterator[Tuple[int, Tuple[int, int, Any, Any]]]: - kernel = self.context.modules[self.config["kernel"]] layer_name = kernel.layer_name @@ -132,7 +130,6 @@ class SSDT(plugins.PluginInterface): ) for idx, function_obj in enumerate(functions): - function = find_address(function_obj) module_symbols = collection.get_module_symbols_by_absolute_location( function diff --git a/volatility3/framework/plugins/windows/svcscan.py b/volatility3/framework/plugins/windows/svcscan.py index e6c1829e9..60562915e 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -180,7 +180,6 @@ class SvcScan(interfaces.plugins.PluginInterface): symbol_table=kernel.symbol_table_name, filter_func=filter_func, ): - proc_id = "Unknown" try: proc_id = task.UniqueProcessId @@ -200,7 +199,6 @@ class SvcScan(interfaces.plugins.PluginInterface): scanner=scanners.BytesScanner(needle=service_tag), sections=vadyarascan.VadYaraScan.get_vad_maps(task), ): - if not is_vista_or_later: service_record = self.context.object( service_table_name + constants.BANG + "_SERVICE_RECORD", diff --git a/volatility3/framework/plugins/windows/symlinkscan.py b/volatility3/framework/plugins/windows/symlinkscan.py index 78c2c6931..89fdf142e 100644 --- a/volatility3/framework/plugins/windows/symlinkscan.py +++ b/volatility3/framework/plugins/windows/symlinkscan.py @@ -52,7 +52,6 @@ class SymlinkScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfa for result in poolscanner.PoolScanner.generate_pool_scan( context, layer_name, symbol_table, constraints ): - _constraint, mem_object, _header = result yield mem_object @@ -62,7 +61,6 @@ class SymlinkScan(interfaces.plugins.PluginInterface, timeliner.TimeLinerInterfa for link in self.scan_symlinks( self.context, kernel.layer_name, kernel.symbol_table_name ): - try: from_name = link.get_link_name() except (ValueError, exceptions.InvalidAddressException): diff --git a/volatility3/framework/plugins/windows/vadinfo.py b/volatility3/framework/plugins/windows/vadinfo.py index 3214c7134..812affe86 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -214,7 +214,6 @@ class VadInfo(interfaces.plugins.PluginInterface): process_name = utility.array_to_string(proc.ImageFileName) for vad in self.list_vads(proc, filter_func=filter_func): - file_output = "Disabled" if self.config["dump"]: file_handle = self.vad_dump( diff --git a/volatility3/framework/plugins/windows/verinfo.py b/volatility3/framework/plugins/windows/verinfo.py index fea4a0f80..1c6615804 100644 --- a/volatility3/framework/plugins/windows/verinfo.py +++ b/volatility3/framework/plugins/windows/verinfo.py @@ -222,7 +222,6 @@ class VerInfo(interfaces.plugins.PluginInterface): continue for entry in proc.load_order_modules(): - try: BaseDllName = entry.BaseDllName.get_string() except exceptions.InvalidAddressException: diff --git a/volatility3/framework/plugins/windows/virtmap.py b/volatility3/framework/plugins/windows/virtmap.py index 6fbf13932..5190bec8d 100644 --- a/volatility3/framework/plugins/windows/virtmap.py +++ b/volatility3/framework/plugins/windows/virtmap.py @@ -31,7 +31,7 @@ class VirtMap(interfaces.plugins.PluginInterface): def _generator(self, map): for entry in sorted(map): - for (start, end) in map[entry]: + for start, end in map[entry]: yield (0, (entry, format_hints.Hex(start), format_hints.Hex(end))) @classmethod diff --git a/volatility3/framework/renderers/__init__.py b/volatility3/framework/renderers/__init__.py index ee87b3b85..534686022 100644 --- a/volatility3/framework/renderers/__init__.py +++ b/volatility3/framework/renderers/__init__.py @@ -181,7 +181,7 @@ class TreeGrid(interfaces.renderers.TreeGrid): converted_columns: List[interfaces.renderers.Column] = [] if len(columns) < 1: raise ValueError("Columns must be a list containing at least one column") - for (name, column_type) in columns: + for name, column_type in columns: is_simple_type = issubclass(column_type, self.base_types) if not is_simple_type: raise TypeError( @@ -238,7 +238,7 @@ class TreeGrid(interfaces.renderers.TreeGrid): if not self.populated: try: prev_nodes: List[interfaces.renderers.TreeNode] = [] - for (level, item) in self._generator: + for level, item in self._generator: parent_index = min(len(prev_nodes), level) parent = prev_nodes[parent_index - 1] if parent_index > 0 else None treenode = self._append(parent, item) diff --git a/volatility3/framework/renderers/format_hints.py b/volatility3/framework/renderers/format_hints.py index 239acbde3..6ec9ebab9 100644 --- a/volatility3/framework/renderers/format_hints.py +++ b/volatility3/framework/renderers/format_hints.py @@ -36,7 +36,6 @@ class MultiTypeData(bytes): split_nulls: bool = False, show_hex: bool = False, ) -> "MultiTypeData": - if isinstance(original, int): data = str(original).encode(encoding) else: diff --git a/volatility3/framework/symbols/__init__.py b/volatility3/framework/symbols/__init__.py index d1af56a26..10cf39cf1 100644 --- a/volatility3/framework/symbols/__init__.py +++ b/volatility3/framework/symbols/__init__.py @@ -192,7 +192,7 @@ class SymbolSpace(interfaces.symbols.SymbolSpaceInterface): replacements.add((traverser, child)) elif child.children: template_traverse_list.append(child) - for (parent, child) in replacements: + for parent, child in replacements: parent.replace_child(child, self._resolved[child.vol.type_name]) def get_type(self, type_name: str) -> interfaces.objects.Template: diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 0d7cbb7e4..ce07167e5 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -62,7 +62,6 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): # based on __d_path from the Linux kernel @classmethod def _do_get_path(cls, rdentry, rmnt, dentry, vfsmnt) -> str: - ret_path: List[str] = [] while dentry != rdentry or vfsmnt != rmnt: @@ -204,7 +203,6 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): symbol_table: str, task: interfaces.objects.ObjectInterface, ): - # task.files can be null if not task.files: return @@ -225,7 +223,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): fd_table, count=max_fds, subtype=file_type, context=context ) - for (fd_num, filp) in enumerate(fds): + for fd_num, filp in enumerate(fds): if filp != 0: full_path = LinuxUtilities.path_for_file(context, task, filp) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 55f139730..5ab8f1aa0 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -595,7 +595,6 @@ class list_head(objects.StructType, collections.abc.Iterable): seen = {self.vol.offset} while link.vol.offset not in seen: - obj = self._context.object( symbol_type, layer, offset=link.vol.offset - relative_offset ) @@ -630,7 +629,6 @@ class files_struct(objects.StructType): class mount(objects.StructType): - MNT_NOSUID = 0x01 MNT_NODEV = 0x02 MNT_NOEXEC = 0x04 @@ -755,7 +753,6 @@ class mount(objects.StructType): and current_mnt.has_parent() and current_mnt.vol.offset not in mnt_seen ): - current_dentry = current_mnt.mnt_mountpoint mnt_seen.add(current_mnt.vol.offset) current_mnt = current_mnt.mnt_parent diff --git a/volatility3/framework/symbols/linux/extensions/elf.py b/volatility3/framework/symbols/linux/extensions/elf.py index df6b23df8..416a7e4d2 100644 --- a/volatility3/framework/symbols/linux/extensions/elf.py +++ b/volatility3/framework/symbols/linux/extensions/elf.py @@ -22,7 +22,6 @@ class elf(objects.StructType): size: int, members: Dict[str, Tuple[int, interfaces.objects.Template]], ) -> None: - super().__init__( context=context, type_name=type_name, diff --git a/volatility3/framework/symbols/mac/__init__.py b/volatility3/framework/symbols/mac/__init__.py index 3909817ea..56ac96633 100644 --- a/volatility3/framework/symbols/mac/__init__.py +++ b/volatility3/framework/symbols/mac/__init__.py @@ -70,7 +70,6 @@ class MacUtilities(interfaces.configuration.VersionableInterface): kernel, # ikelos - how to type this?? mods_list: Iterator[Any], ): - try: start_addr = kernel.object_from_symbol("vm_kernel_stext") except exceptions.SymbolError: @@ -231,7 +230,6 @@ class MacUtilities(interfaces.configuration.VersionableInterface): next_member: str, max_elements: int = 4096, ) -> Iterable[interfaces.objects.ObjectInterface]: - for element in cls._walk_iterable( queue, "tqh_first", "tqe_next", next_member, max_elements ): @@ -244,7 +242,6 @@ class MacUtilities(interfaces.configuration.VersionableInterface): next_member: str, max_elements: int = 4096, ) -> Iterable[interfaces.objects.ObjectInterface]: - for element in cls._walk_iterable( queue, "lh_first", "le_next", next_member, max_elements ): @@ -257,7 +254,6 @@ class MacUtilities(interfaces.configuration.VersionableInterface): 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 ): diff --git a/volatility3/framework/symbols/mac/extensions/__init__.py b/volatility3/framework/symbols/mac/extensions/__init__.py index b678304b8..c89b527e6 100644 --- a/volatility3/framework/symbols/mac/extensions/__init__.py +++ b/volatility3/framework/symbols/mac/extensions/__init__.py @@ -206,7 +206,7 @@ class vm_map_entry(objects.StructType): permask = "rwx" perms = "" - for (ctr, i) in enumerate([1, 3, 5]): + for ctr, i in enumerate([1, 3, 5]): if (self.protection & i) == i: perms = perms + permask[ctr] else: @@ -593,7 +593,7 @@ class sysctl_oid(objects.StructType): checks = [0x80000000, 0x40000000, 0x00800000] perms = ["R", "W", "L"] - for (i, c) in enumerate(checks): + for i, c in enumerate(checks): if c & self.oid_kind: ret = ret + perms[i] else: diff --git a/volatility3/framework/symbols/windows/extensions/__init__.py b/volatility3/framework/symbols/windows/extensions/__init__.py index d34d6a22f..ba00a4053 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -202,7 +202,6 @@ class MMVAD_SHORT(objects.StructType): # this is for windows 8 and 10 elif self.has_member("VadNode"): - if self.VadNode.has_member("u1"): return self.VadNode.u1.Parent & ~0x3 @@ -211,7 +210,6 @@ class MMVAD_SHORT(objects.StructType): # also for windows 8 and 10 elif self.has_member("Core"): - if self.Core.VadNode.has_member("u1"): return self.Core.VadNode.u1.Parent & ~0x3 @@ -224,14 +222,12 @@ class MMVAD_SHORT(objects.StructType): """Get the VAD's starting virtual address. This is the first accessible byte in the range.""" if self.has_member("StartingVpn"): - if self.has_member("StartingVpnHigh"): return (self.StartingVpn << 12) | (self.StartingVpnHigh << 44) else: return self.StartingVpn << 12 elif self.has_member("Core"): - if self.Core.has_member("StartingVpnHigh"): return (self.Core.StartingVpn << 12) | (self.Core.StartingVpnHigh << 44) else: @@ -243,7 +239,6 @@ class MMVAD_SHORT(objects.StructType): """Get the VAD's ending virtual address. This is the last accessible byte in the range.""" if self.has_member("EndingVpn"): - if self.has_member("EndingVpnHigh"): return (((self.EndingVpn + 1) << 12) | (self.EndingVpnHigh << 44)) - 1 else: @@ -376,7 +371,6 @@ class EX_FAST_REF(objects.StructType): """ def dereference(self) -> interfaces.objects.ObjectInterface: - if constants.BANG not in self.vol.type_name: raise ValueError( f"Invalid symbol table name syntax (no {constants.BANG} found)" @@ -771,7 +765,6 @@ class EPROCESS(generic.GenericIntelProcess, pool.ExecutiveObject): return False def get_vad_root(self): - # windows 8 and 2012 (_MM_AVL_TABLE) if self.VadRoot.has_member("BalancedRoot"): return self.VadRoot.BalancedRoot @@ -1346,7 +1339,6 @@ class SHARED_CACHE_MAP(objects.StructType): limit_depth = level_depth if section_size > self.VACB_SIZE_OF_FIRST_LEVEL: - # Create an array of 128 entries for the VACB index array. vacb_array = self._context.object( object_type=symbol_table_name + constants.BANG + "array", diff --git a/volatility3/framework/symbols/windows/extensions/network.py b/volatility3/framework/symbols/windows/extensions/network.py index c0f2bd61a..9b7573c2e 100644 --- a/volatility3/framework/symbols/windows/extensions/network.py +++ b/volatility3/framework/symbols/windows/extensions/network.py @@ -64,7 +64,6 @@ class _TCP_LISTENER(objects.StructType): size: int, members: Dict[str, Tuple[int, interfaces.objects.Template]], ) -> None: - super().__init__( context=context, type_name=type_name, @@ -167,7 +166,6 @@ class _TCP_LISTENER(objects.StructType): yield "v6", inaddr6_any, inaddr6_any def is_valid(self): - try: if not self.get_address_family() in (AF_INET, AF_INET6): vollog.debug( @@ -189,7 +187,6 @@ class _TCP_ENDPOINT(_TCP_LISTENER): """Class for objects found in TcpE pools""" def _ipv4_or_ipv6(self, inaddr): - if self.get_address_family() == AF_INET: return inet_ntop(socket.AF_INET, inaddr.addr4) else: @@ -214,7 +211,6 @@ class _TCP_ENDPOINT(_TCP_LISTENER): return None def is_valid(self): - if self.State not in self.State.choices.values(): vollog.debug( f"{type(self)} 0x{self.vol.offset:x} invalid due to invalid tcp state {self.State}" diff --git a/volatility3/framework/symbols/windows/extensions/pe.py b/volatility3/framework/symbols/windows/extensions/pe.py index adee956f7..3f34fc3dd 100644 --- a/volatility3/framework/symbols/windows/extensions/pe.py +++ b/volatility3/framework/symbols/windows/extensions/pe.py @@ -151,7 +151,6 @@ class IMAGE_DOS_HEADER(objects.StructType): counter = 0 for sect in nt_header.get_sections(): - if sect.VirtualAddress > size_of_image: raise ValueError( f"Section VirtualAddress is too large: {sect.VirtualAddress}" diff --git a/volatility3/framework/symbols/windows/pdbutil.py b/volatility3/framework/symbols/windows/pdbutil.py index 74fd0e4e8..a43933ccf 100644 --- a/volatility3/framework/symbols/windows/pdbutil.py +++ b/volatility3/framework/symbols/windows/pdbutil.py @@ -249,7 +249,6 @@ class PDBUtility(interfaces.configuration.VersionableInterface): # Check for writability filter_string = os.path.join(pdb_name, guid + "-" + str(age)) for path in symbols.__path__: - # Store any temporary files created by downloading PDB files tmp_files = [] potential_output_filename = os.path.join( @@ -353,7 +352,7 @@ class PDBUtility(interfaces.configuration.VersionableInterface): if end is None: end = ctx.layers[layer_name].maximum_address - for (GUID, age, pdb_name, signature_offset) in ctx.layers[layer_name].scan( + for GUID, age, pdb_name, signature_offset in ctx.layers[layer_name].scan( ctx, PdbSignatureScanner(pdb_names), progress_callback=progress_callback, @@ -426,7 +425,6 @@ class PDBUtility(interfaces.configuration.VersionableInterface): module_size: int = None, create_module: bool = False, ) -> Tuple[Optional[str], Optional[str]]: - if module_offset is None: module_offset = context.layers[layer_name].minimum_address if module_size is None: diff --git a/volatility3/plugins/windows/registry/certificates.py b/volatility3/plugins/windows/registry/certificates.py index 3212cb465..5ef840f32 100644 --- a/volatility3/plugins/windows/registry/certificates.py +++ b/volatility3/plugins/windows/registry/certificates.py @@ -77,7 +77,6 @@ class Certificates(interfaces.plugins.PluginInterface): layer_name=kernel.layer_name, symbol_table=kernel.symbol_table_name, ): - for top_key in [ "Microsoft\\SystemCertificates", "Software\\Microsoft\\SystemCertificates", From aac4c735280537c55c8f6eb738f08aaa8304b8e2 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Fri, 3 Feb 2023 09:22:30 +0000 Subject: [PATCH 064/140] Actions: Bump black checkout to Node16/wqv3 --- .github/workflows/black.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index dba5b5b80..5f4523072 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -6,7 +6,7 @@ jobs: lint: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: psf/black@stable with: options: "--check --diff --verbose" From 4bb6d93e122693b89a5a3947c12271400e25be3e Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 3 Feb 2023 10:18:06 +0000 Subject: [PATCH 065/140] Update linux.psscan --- volatility3/framework/plugins/linux/psscan.py | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 volatility3/framework/plugins/linux/psscan.py diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py new file mode 100644 index 000000000..f87b78eb1 --- /dev/null +++ b/volatility3/framework/plugins/linux/psscan.py @@ -0,0 +1,158 @@ +# This file is Copyright 2023 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, List, Tuple +import struct +from enum import Enum + +from volatility3.framework import renderers, interfaces, symbols, constants +from volatility3.framework.configuration import requirements +from volatility3.framework.objects import utility +from volatility3.framework.layers import scanners +from volatility3.framework.renderers import format_hints + +vollog = logging.getLogger(__name__) + + +class DescExitStateEnum(Enum): + """Enum for linux task exit_state as defined in include/linux/sched.h""" + + TASK_RUNNING = 0x00000000 + EXIT_DEAD = 0x00000010 + EXIT_ZOMBIE = 0x00000020 + EXIT_TRACE = EXIT_ZOMBIE | EXIT_DEAD + + +class PsScan(interfaces.plugins.PluginInterface): + """Scans for processes present in a particular linux image.""" + + _required_framework_version = (2, 0, 0) + + @classmethod + def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: + return [ + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=["Intel32", "Intel64"], + ), + ] + + def _get_task_fields( + self, task: interfaces.objects.ObjectInterface + ) -> Tuple[int, int, int, str, str]: + """Extract the fields needed for the final output + + Args: + task: A task object from where to get the fields. + Returns: + A tuple with the fields to show in the plugin output. + """ + pid = task.tgid + tid = task.pid + ppid = task.parent.tgid if task.parent else 0 + name = utility.array_to_string(task.comm) + exit_state = DescExitStateEnum(task.exit_state).name + + task_fields = ( + format_hints.Hex(task.vol.offset), + pid, + tid, + ppid, + name, + exit_state, + ) + return task_fields + + def _generator(self): + """Generates the tasks found from scanning.""" + + for task in self.scan_tasks( + self.context, self.config["kernel"], self.config["kernel.layer_name"] + ): + row = self._get_task_fields(task) + yield (0, row) + + @classmethod + def scan_tasks( + cls, + context: interfaces.context.ContextInterface, + vmlinux_module_name: str, + kernel_layer_name: str, + ) -> Iterable[interfaces.objects.ObjectInterface]: + """Scans for tasks in the memory layer. + + Args: + context: The context to retrieve required elements (layers, symbol tables) from + vmlinux_module_name: The name of the kernel module on which to operate + kernel_layer_name: The name for the kernel layer + Yields: + Task objects + """ + vmlinux = context.modules[vmlinux_module_name] + + # check if this image is 32bit or 64bit + is_32bit = not symbols.symbol_table_is_64bit(context, vmlinux.symbol_table_name) + if is_32bit: + pack_format = "I" + else: + pack_format = "Q" + + # get task_struct to find the offset to the sched_class pointer + sched_class_offset = vmlinux.get_type("task_struct").members["sched_class"][0] + kernel_layer = context.layers[kernel_layer_name] + + needles = [] + for symbol in vmlinux.symbols: + + # find all sched_class names by searching by if they include '_sched_class', e.g. 'fair_sched_class' + if "_sched_class" in symbol: + + # use canonicalize to set the appropriate sign extension for the addr + addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address) + + # append to needles list the packed hex for searching + needles.append(struct.pack(pack_format, addr)) + + # scan the memory_layer for these needles + memory_layer = context.layers["memory_layer"] + for address, _ in memory_layer.scan( + context, scanners.MultiStringScanner(needles) + ): + # create task in the memory_layer + ptask = context.object( + vmlinux.symbol_table_name + constants.BANG + "task_struct", + offset=address - sched_class_offset, + layer_name="memory_layer", + ) + + # sanity check exit_state + try: + # attempt tp parse the exist_state using the enum + DescExitStateEnum(ptask.exit_state) + except ValueError: + vollog.debug( + f"Skipping task_struct at {hex(ptask.vol.offset)} as exit_state {ptask.exit_state} is likely not valid" + ) + continue + + # sanity check pid + if not (0 < ptask.pid < 65535): + vollog.debug( + f"Skipping task_struct at {hex(ptask.vol.offset)} as pid {ptask.pid} is likely not valid" + ) + continue + + yield ptask + + def run(self): + columns = [ + ("OFFSET (P)", format_hints.Hex), + ("PID", int), + ("TID", int), + ("PPID", int), + ("COMM", str), + ("EXIT_STATE", str), + ] + return renderers.TreeGrid(columns, self._generator()) From e81935869ab51f5b792aaa0c390418ab64e90755 Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 3 Feb 2023 10:37:29 +0000 Subject: [PATCH 066/140] Update linux.psscan to find kernel layer name correctly --- volatility3/framework/plugins/linux/psscan.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index f87b78eb1..25e7206ec 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -68,8 +68,11 @@ class PsScan(interfaces.plugins.PluginInterface): def _generator(self): """Generates the tasks found from scanning.""" + vmlinux_module_name = self.config["kernel"] + vmlinux = self.context.modules[vmlinux_module_name] + for task in self.scan_tasks( - self.context, self.config["kernel"], self.config["kernel.layer_name"] + self.context, vmlinux_module_name, vmlinux.layer_name ): row = self._get_task_fields(task) yield (0, row) From 9e5a98ca40e5614df28ba5b09ffc64a656f19fb9 Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 3 Feb 2023 10:51:18 +0000 Subject: [PATCH 067/140] Update linux.psscan with black linting and version --- volatility3/framework/plugins/linux/psscan.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index 25e7206ec..ba233e53d 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -28,6 +28,7 @@ class PsScan(interfaces.plugins.PluginInterface): """Scans for processes present in a particular linux image.""" _required_framework_version = (2, 0, 0) + _version = (1, 0, 0) @classmethod def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface]: @@ -101,23 +102,19 @@ class PsScan(interfaces.plugins.PluginInterface): pack_format = "I" else: pack_format = "Q" - # get task_struct to find the offset to the sched_class pointer sched_class_offset = vmlinux.get_type("task_struct").members["sched_class"][0] kernel_layer = context.layers[kernel_layer_name] needles = [] for symbol in vmlinux.symbols: - # find all sched_class names by searching by if they include '_sched_class', e.g. 'fair_sched_class' if "_sched_class" in symbol: - # use canonicalize to set the appropriate sign extension for the addr addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address) # append to needles list the packed hex for searching needles.append(struct.pack(pack_format, addr)) - # scan the memory_layer for these needles memory_layer = context.layers["memory_layer"] for address, _ in memory_layer.scan( @@ -139,14 +136,12 @@ class PsScan(interfaces.plugins.PluginInterface): f"Skipping task_struct at {hex(ptask.vol.offset)} as exit_state {ptask.exit_state} is likely not valid" ) continue - # sanity check pid if not (0 < ptask.pid < 65535): vollog.debug( f"Skipping task_struct at {hex(ptask.vol.offset)} as pid {ptask.pid} is likely not valid" ) continue - yield ptask def run(self): From d6ebad8235060a257e9af9a5e9831bb64a607606 Mon Sep 17 00:00:00 2001 From: Ashley Date: Thu, 9 Feb 2023 21:19:11 -0700 Subject: [PATCH 068/140] Update simple-plugin.rst Very minor typo fix. --- doc/source/simple-plugin.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/simple-plugin.rst b/doc/source/simple-plugin.rst index c4908caf3..39670a62d 100644 --- a/doc/source/simple-plugin.rst +++ b/doc/source/simple-plugin.rst @@ -259,7 +259,7 @@ The plugin then takes the process's ``BaseDllName`` value, and calls :py:meth:`~ as defined by the symbols, are directly accessible and use the case-style of the symbol library it came from (in Windows, attributes are CamelCase), such as ``entry.BaseDllName`` in this instance. Any attributes not defined by the symbol but added by Volatility extensions cannot be properties (in case they overlap with the attributes defined in the symbol libraries) -and are therefore always methods and pretended with ``get_``, in this example ``BaseDllName.get_string()``. +and are therefore always methods and prepended with ``get_``, in this example ``BaseDllName.get_string()``. Finally, ``FullDllName`` is populated. These operations read from memory, and as such, the memory image may be unable to read the data at a particular offset. This will cause an exception to be thrown. In Volatility 3, exceptions are thrown From 4734a3d1f83295af45997758f1b31c07ba4e79fe Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 18 Feb 2023 21:14:02 +0000 Subject: [PATCH 069/140] Automagic: Fix cache issue with missing files --- volatility3/framework/automagic/symbol_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/automagic/symbol_cache.py b/volatility3/framework/automagic/symbol_cache.py index 63c6fc7fa..1ca5ba210 100644 --- a/volatility3/framework/automagic/symbol_cache.py +++ b/volatility3/framework/automagic/symbol_cache.py @@ -332,7 +332,7 @@ class SqliteCache(CacheManagerInterface): if inner_url.scheme == "file": pathname = inner_url.path.split("!")[0] - if pathname: + if pathname and os.path.exists(pathname): timestamp = datetime.datetime.fromtimestamp( os.stat(pathname).st_mtime ) From 471b19b037deab511bc7e9144bc5b25b47cfc81d Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 18 Feb 2023 21:05:05 +0000 Subject: [PATCH 070/140] Layers: Use ctypes for snappy support --- requirements-dev.txt | 4 ---- requirements.txt | 4 ---- volatility3/framework/layers/avml.py | 35 ++++++++++++++++++++++------ 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 7c372da2a..9db14d441 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -20,7 +20,3 @@ jsonschema>=2.3.0 # This is required for memory acquisition via leechcore/pcileech. leechcorepyc>=2.4.0 - -# This is required for analyzing Linux samples compressed using AVMLs native -# compression format. It is not required for AVML's standard LiME compression. -python-snappy==0.6.0 diff --git a/requirements.txt b/requirements.txt index 1793012f1..99e0786cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,3 @@ pycryptodome # This is required for memory acquisition via leechcore/pcileech. leechcorepyc>=2.4.0 - -# This is required for analyzing Linux samples compressed using AVMLs native -# compression format. It is not required for AVML's standard LiME compression. -python-snappy==0.6.0 diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index 66f3f0e4f..3ce25ca6f 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -6,6 +6,7 @@ The user of the file doesn't have to worry about the compression, but random access is not allowed.""" +import ctypes import logging import struct from typing import Tuple, List, Optional @@ -16,13 +17,35 @@ from volatility3.framework.layers import segmented vollog = logging.getLogger(__name__) try: - import snappy + from ctypes import cdll + + # TODO: Find library for windows if needed + lib_snappy = cdll.LoadLibrary("libsnappy.so.1") + __snappy_uncompress = lib_snappy.snappy_uncompress + __snappy_uncompressed_length = lib_snappy.snappy_uncompressed_length HAS_SNAPPY = True -except ImportError: +except OSError: HAS_SNAPPY = False +class SnappyException(Exception): + pass + + +def uncompress(s): + """Uncompress a snappy compressed string.""" + ulen = ctypes.c_int(0) + cresult = __snappy_uncompressed_length(s, len(s), ctypes.byref(ulen)) + if cresult != 0: + raise SnappyException(f"Error in snappy_uncompressed_length: {cresult}") + ubuf = ctypes.create_string_buffer(ulen.value) + __snappy_uncompress(s, len(s), ubuf, ctypes.byref(ulen)) + if cresult != 0: + raise SnappyException(f"Error in snappy_uncompress: {cresult}") + return ubuf.raw + + class AVMLLayer(segmented.NonLinearlySegmentedLayer): """A Lime format TranslationLayer. @@ -44,9 +67,7 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): if magic not in [0x4C4D5641] or version != 2: raise exceptions.LayerException("File not completely in AVML format") if not HAS_SNAPPY: - vollog.warning( - "AVML file detected, but snappy python library not installed" - ) + vollog.warning("AVML file detected, but snappy library could not be found") raise exceptions.LayerException( "AVML format dependencies not satisfied (snappy)" ) @@ -131,7 +152,7 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): ] if frame_type == 0x00: # Compressed data - frame_data = snappy.decompress(frame_data) + frame_data = uncompress(frame_data) # TODO: Verify CRC segments.append( ( @@ -156,7 +177,7 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): ) -> bytes: start_offset, _, _, _ = self._find_segment(offset) if self._compressed[mapped_offset]: - decoded_data = snappy.decompress(data) + decoded_data = uncompress(data) else: decoded_data = data decoded_data = decoded_data[offset - start_offset :] From c7252e9707ac0fb96c5fd65036cf8a8ff4b96672 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 19 Feb 2023 10:03:23 +0000 Subject: [PATCH 071/140] Automagic: Handle snappy for windows. --- volatility3/framework/layers/avml.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index 3ce25ca6f..83c10186f 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -20,7 +20,23 @@ try: from ctypes import cdll # TODO: Find library for windows if needed - lib_snappy = cdll.LoadLibrary("libsnappy.so.1") + try: + # Linux/Mac + lib_snappy = cdll.LoadLibrary("libsnappy.so.1") + except OSError: + lib_snappy = None + + try: + if not lib_snappy: + # Windows 64 + lib_snappy = cdll.LoadLibrary("snappy64") + except OSError: + lib_snappy = None + + if lib_snappy: + # Windows 32 + lib_snappy = cdll.LoadLibrary("snappy32") + __snappy_uncompress = lib_snappy.snappy_uncompress __snappy_uncompressed_length = lib_snappy.snappy_uncompressed_length @@ -29,7 +45,7 @@ except OSError: HAS_SNAPPY = False -class SnappyException(Exception): +class SnappyException(exceptions.VolatilityException): pass @@ -65,9 +81,12 @@ class AVMLLayer(segmented.NonLinearlySegmentedLayer): layer.read(layer.minimum_address, struct.calcsize(header_structure)), ) if magic not in [0x4C4D5641] or version != 2: - raise exceptions.LayerException("File not completely in AVML format") + raise exceptions.LayerException("File not in AVML format") if not HAS_SNAPPY: - vollog.warning("AVML file detected, but snappy library could not be found") + vollog.warning( + "AVML file detected, but snappy library could not be found\n" + "Please install the snappy from your distribution or https://google.github.io/snappy/." + ) raise exceptions.LayerException( "AVML format dependencies not satisfied (snappy)" ) From ad3773d89884650cba6573280588f29e14e6ba0e Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 19 Feb 2023 10:23:04 +0000 Subject: [PATCH 072/140] Automagic: Improve identified AVML CodeQL issues --- volatility3/framework/layers/avml.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index 83c10186f..1ba564c61 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -17,25 +17,23 @@ from volatility3.framework.layers import segmented vollog = logging.getLogger(__name__) try: - from ctypes import cdll - # TODO: Find library for windows if needed try: # Linux/Mac - lib_snappy = cdll.LoadLibrary("libsnappy.so.1") + lib_snappy = ctypes.cdll.LoadLibrary("libsnappy.so.1") except OSError: lib_snappy = None try: if not lib_snappy: # Windows 64 - lib_snappy = cdll.LoadLibrary("snappy64") + lib_snappy = ctypes.cdll.LoadLibrary("snappy64") except OSError: lib_snappy = None if lib_snappy: # Windows 32 - lib_snappy = cdll.LoadLibrary("snappy32") + lib_snappy = ctypes.cdll.LoadLibrary("snappy32") __snappy_uncompress = lib_snappy.snappy_uncompress __snappy_uncompressed_length = lib_snappy.snappy_uncompressed_length @@ -56,7 +54,7 @@ def uncompress(s): if cresult != 0: raise SnappyException(f"Error in snappy_uncompressed_length: {cresult}") ubuf = ctypes.create_string_buffer(ulen.value) - __snappy_uncompress(s, len(s), ubuf, ctypes.byref(ulen)) + cresult = __snappy_uncompress(s, len(s), ubuf, ctypes.byref(ulen)) if cresult != 0: raise SnappyException(f"Error in snappy_uncompress: {cresult}") return ubuf.raw From 1770edf6fa7a87f5c714aaeaedb4e02ce91e040f Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 22 Feb 2023 17:24:12 +0000 Subject: [PATCH 073/140] Automagic: Fix typo in cache stats --- volatility3/framework/automagic/symbol_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/automagic/symbol_cache.py b/volatility3/framework/automagic/symbol_cache.py index 1ca5ba210..a58bf0091 100644 --- a/volatility3/framework/automagic/symbol_cache.py +++ b/volatility3/framework/automagic/symbol_cache.py @@ -371,7 +371,7 @@ class SqliteCache(CacheManagerInterface): # Get stats stats_base_types = len(json_obj.get("base_types", {})) - stats_types = len(json_obj.get("types", {})) + stats_types = len(json_obj.get("user_types", {})) stats_enums = len(json_obj.get("enums", {})) stats_symbols = len(json_obj.get("symbols", {})) From 588b0962541887dbfddedc779e5d93c5ceda87d2 Mon Sep 17 00:00:00 2001 From: Maxime THIEBAUT <46688461+0xThiebaut@users.noreply.github.com> Date: Sat, 25 Feb 2023 18:23:42 +0100 Subject: [PATCH 074/140] Add PID filtering to `windows.pstree` --- .../framework/plugins/windows/pstree.py | 46 +++++++++++++++---- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/volatility3/framework/plugins/windows/pstree.py b/volatility3/framework/plugins/windows/pstree.py index 88a3697da..5c78d1682 100644 --- a/volatility3/framework/plugins/windows/pstree.py +++ b/volatility3/framework/plugins/windows/pstree.py @@ -3,7 +3,7 @@ # import datetime import logging -from typing import Dict, Set, Tuple +from typing import Callable, Dict, Set, Tuple from volatility3.framework import objects, interfaces, renderers from volatility3.framework.configuration import requirements @@ -24,6 +24,7 @@ class PsTree(interfaces.plugins.PluginInterface): self._processes: Dict[int, Tuple[interfaces.objects.ObjectInterface, int]] = {} self._levels: Dict[int, int] = {} self._children: Dict[int, Set[int]] = {} + self._ancestors: Set[int] = set([]) @classmethod def get_requirements(cls): @@ -45,18 +46,26 @@ class PsTree(interfaces.plugins.PluginInterface): requirements.ListRequirement( name="pid", element_type=int, - description="Process ID to include (all other processes are excluded)", + description="Process ID to include (with ancestors and descendants, all other processes are excluded)", optional=True, ), ] - def find_level(self, pid: objects.Pointer) -> None: + def find_level( + self, + pid: objects.Pointer, + filter_func: Callable[ + [interfaces.objects.ObjectInterface], bool + ] = lambda _: False, + ) -> None: """Finds how deep the pid is in the processes list.""" - seen = set([]) - seen.add(pid) + seen = {pid} level = 0 proc, _ = self._processes.get(pid, None) + filtered = not filter_func(proc) while proc is not None and proc.InheritedFromUniqueProcessId not in seen: + if filtered: + self._ancestors.add(proc.UniqueProcessId) child_list = self._children.get(proc.InheritedFromUniqueProcessId, set([])) child_list.add(proc.UniqueProcessId) self._children[proc.InheritedFromUniqueProcessId] = child_list @@ -67,7 +76,12 @@ class PsTree(interfaces.plugins.PluginInterface): level += 1 self._levels[pid] = level - def _generator(self): + def _generator( + self, + filter_func: Callable[ + [interfaces.objects.ObjectInterface], bool + ] = lambda _: False, + ): """Generates the Tree of processes.""" kernel = self.context.modules[self.config["kernel"]] @@ -87,15 +101,21 @@ class PsTree(interfaces.plugins.PluginInterface): # Build the child/level maps for pid in self._processes: - self.find_level(pid) + self.find_level(pid, filter_func) process_pids = set([]) - def yield_processes(pid): + def yield_processes(pid, descendant: bool = False): if pid in process_pids: vollog.debug(f"Pid cycle: already processed pid {pid}") return + process_pids.add(pid) + + if pid not in self._ancestors and not descendant: + vollog.debug(f"Pid cycle: pid {pid} not in filtered tree") + return + proc, offset = self._processes[pid] row = ( proc.UniqueProcessId, @@ -114,7 +134,9 @@ class PsTree(interfaces.plugins.PluginInterface): yield (self._levels[pid] - 1, row) for child_pid in self._children.get(pid, []): - yield from yield_processes(child_pid) + yield from yield_processes( + child_pid, descendant or not filter_func(proc) + ) for pid in self._levels: if self._levels[pid] == 1: @@ -140,5 +162,9 @@ class PsTree(interfaces.plugins.PluginInterface): ("CreateTime", datetime.datetime), ("ExitTime", datetime.datetime), ], - self._generator(), + self._generator( + filter_func=pslist.PsList.create_pid_filter( + self.config.get("pid", None) + ), + ), ) From 3791b21695083f522fe6dd6009ffef1e5b05fc2f Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 6 Mar 2023 00:13:47 +0000 Subject: [PATCH 075/140] Layers: Fix new snappy implementation error --- volatility3/framework/layers/avml.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/layers/avml.py b/volatility3/framework/layers/avml.py index 1ba564c61..c9c682ac4 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -39,7 +39,7 @@ try: __snappy_uncompressed_length = lib_snappy.snappy_uncompressed_length HAS_SNAPPY = True -except OSError: +except (AttributeError, OSError): HAS_SNAPPY = False From f3dea3619cc2521c9616a530222a47bff74f738b Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 5 Mar 2023 23:14:09 +0000 Subject: [PATCH 076/140] Windows: Memoize part of the pool handling code --- volatility3/framework/contexts/__init__.py | 7 ++++- .../symbols/windows/extensions/pool.py | 29 +++++++++++-------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/volatility3/framework/contexts/__init__.py b/volatility3/framework/contexts/__init__.py index 226a303dd..ecce5041c 100644 --- a/volatility3/framework/contexts/__init__.py +++ b/volatility3/framework/contexts/__init__.py @@ -381,6 +381,7 @@ class ModuleCollection(interfaces.context.ModuleContainer): def __init__( self, modules: Optional[List[interfaces.context.ModuleInterface]] = None ) -> None: + self._prefix_count = {} super().__init__(modules) def deduplicate(self) -> "ModuleCollection": @@ -400,9 +401,13 @@ class ModuleCollection(interfaces.context.ModuleContainer): def free_module_name(self, prefix: str = "module") -> str: """Returns an unused module name""" - count = 1 + if prefix not in self._prefix_count: + self._prefix_count[prefix] = 1 + return prefix + count = self._prefix_count[prefix] while prefix + str(count) in self: count += 1 + self._prefix_count[prefix] = count return prefix + str(count) @property diff --git a/volatility3/framework/symbols/windows/extensions/pool.py b/volatility3/framework/symbols/windows/extensions/pool.py index ac7f36a99..052ae4dd6 100644 --- a/volatility3/framework/symbols/windows/extensions/pool.py +++ b/volatility3/framework/symbols/windows/extensions/pool.py @@ -396,13 +396,9 @@ class OBJECT_HEADER(objects.StructType): ) symbol_table_name = self.vol.type_name.split(constants.BANG)[0] - - try: - header_offset = self.NameInfoOffset - except AttributeError: - # http://codemachine.com/article_objectheader.html (Windows 7 and later) - name_info_bit = 0x2 - + if symbol_table_name in self._context.modules: + ntkrnlmp = self._context.modules[symbol_table_name] + else: layer = self._context.layers[self.vol.native_layer_name] kvo = layer.config.get("kernel_virtual_offset", None) @@ -411,16 +407,25 @@ class OBJECT_HEADER(objects.StructType): f"Could not find kernel_virtual_offset for layer: {self.vol.layer_name}" ) + # We know this symbol table name can't exist because we checked for it earlier ntkrnlmp = self._context.module( symbol_table_name, layer_name=self.vol.layer_name, offset=kvo ) + self._context.add_module(ntkrnlmp) + + try: + header_offset = self.NameInfoOffset + except AttributeError: + # http://codemachine.com/article_objectheader.html (Windows 7 and later) + name_info_bit = 0x2 + address = ntkrnlmp.get_symbol("ObpInfoMaskToOffset").address calculated_index = self.InfoMask & (name_info_bit | (name_info_bit - 1)) - header_offset = self._context.object( - symbol_table_name + constants.BANG + "unsigned char", + header_offset = ntkrnlmp.object( + "unsigned char", layer_name=self.vol.native_layer_name, - offset=kvo + address + calculated_index, + offset=address + calculated_index, ) if header_offset == 0: @@ -430,8 +435,8 @@ class OBJECT_HEADER(objects.StructType): ) ) - header = self._context.object( - symbol_table_name + constants.BANG + "_OBJECT_HEADER_NAME_INFO", + header = ntkrnlmp.object( + "_OBJECT_HEADER_NAME_INFO", layer_name=self.vol.layer_name, offset=self.vol.offset - header_offset, native_layer_name=self.vol.native_layer_name, From 21e34ec605d001a7f272907a13fe30210eeab278 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 5 Mar 2023 23:25:42 +0000 Subject: [PATCH 077/140] Windows: Fix up double adding the symbol from context.module --- volatility3/framework/symbols/windows/extensions/pool.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/windows/extensions/pool.py b/volatility3/framework/symbols/windows/extensions/pool.py index 052ae4dd6..b761ddad8 100644 --- a/volatility3/framework/symbols/windows/extensions/pool.py +++ b/volatility3/framework/symbols/windows/extensions/pool.py @@ -396,6 +396,7 @@ class OBJECT_HEADER(objects.StructType): ) symbol_table_name = self.vol.type_name.split(constants.BANG)[0] + if symbol_table_name in self._context.modules: ntkrnlmp = self._context.modules[symbol_table_name] else: @@ -411,7 +412,6 @@ class OBJECT_HEADER(objects.StructType): ntkrnlmp = self._context.module( symbol_table_name, layer_name=self.vol.layer_name, offset=kvo ) - self._context.add_module(ntkrnlmp) try: header_offset = self.NameInfoOffset @@ -440,5 +440,6 @@ class OBJECT_HEADER(objects.StructType): layer_name=self.vol.layer_name, offset=self.vol.offset - header_offset, native_layer_name=self.vol.native_layer_name, + absolute=True, ) return header From a34fb8497633394062e10366553a2c69ba10c85a Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 8 Mar 2023 13:31:37 +0000 Subject: [PATCH 078/140] Fix linux.psscan to use kernel offset when finding symbol location --- volatility3/framework/plugins/linux/psscan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index ba233e53d..60b96ba40 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -111,7 +111,7 @@ class PsScan(interfaces.plugins.PluginInterface): # find all sched_class names by searching by if they include '_sched_class', e.g. 'fair_sched_class' if "_sched_class" in symbol: # use canonicalize to set the appropriate sign extension for the addr - addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address) + addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address + vmlinux.offset) # append to needles list the packed hex for searching needles.append(struct.pack(pack_format, addr)) From bdf57f071697aa7688595efed438c8b80aa336d6 Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 8 Mar 2023 13:44:08 +0000 Subject: [PATCH 079/140] Add extra debug messages to linux.psscan when finding symbol locations --- volatility3/framework/plugins/linux/psscan.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index 60b96ba40..7cf0aa631 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -112,9 +112,16 @@ class PsScan(interfaces.plugins.PluginInterface): if "_sched_class" in symbol: # use canonicalize to set the appropriate sign extension for the addr addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address + vmlinux.offset) + packed_addr = struct.pack(pack_format, addr) + + # debug message to show needles being searched for and symbol names + vollog.debug( + f"Found a sched_class named {symbol} at offset {hex(addr)}. Will scan for these bytes: {packed_addr.hex()}" + ) # append to needles list the packed hex for searching - needles.append(struct.pack(pack_format, addr)) + needles.append(packed_addr) + # scan the memory_layer for these needles memory_layer = context.layers["memory_layer"] for address, _ in memory_layer.scan( From abfe104eb96e5ddd4a07e7a7a4dd5073fc88e5d0 Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 8 Mar 2023 14:02:13 +0000 Subject: [PATCH 080/140] Fix linux.pscan to find memory layer to scan using kernel layers dependencies rather than hard coded value. --- volatility3/framework/plugins/linux/psscan.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index 7cf0aa631..e8cc17a40 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -6,7 +6,7 @@ from typing import Iterable, List, Tuple import struct from enum import Enum -from volatility3.framework import renderers, interfaces, symbols, constants +from volatility3.framework import renderers, interfaces, symbols, constants, exceptions from volatility3.framework.configuration import requirements from volatility3.framework.objects import utility from volatility3.framework.layers import scanners @@ -122,8 +122,20 @@ class PsScan(interfaces.plugins.PluginInterface): # append to needles list the packed hex for searching needles.append(packed_addr) + # find the memory layer to scan + if len(kernel_layer.dependencies) > 1: + vollog.warning( + f"Kernel layer depends on multiple layers however only {kernel_layer.dependencies[0]} will be scanned by this plugin." + ) + elif len(kernel_layer.dependencies) == 0: + vollog.error( + f"Kernel layer has no dependencies, meaning there is no memory layer for this plugin to scan." + ) + raise exceptions.LayerException(kernel_layer_name, f"Layer {kernel_layer_name} has no dependencies") + + memory_layer = context.layers[kernel_layer.dependencies[0]] + # scan the memory_layer for these needles - memory_layer = context.layers["memory_layer"] for address, _ in memory_layer.scan( context, scanners.MultiStringScanner(needles) ): From 32db4c0e5f3804b33f4cc1a4f66fae90494c0df8 Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 8 Mar 2023 14:08:44 +0000 Subject: [PATCH 081/140] Fix black linting for linux.psscan. --- volatility3/framework/plugins/linux/psscan.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index e8cc17a40..f4bfd347e 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -111,7 +111,9 @@ class PsScan(interfaces.plugins.PluginInterface): # find all sched_class names by searching by if they include '_sched_class', e.g. 'fair_sched_class' if "_sched_class" in symbol: # use canonicalize to set the appropriate sign extension for the addr - addr = kernel_layer.canonicalize(vmlinux.get_symbol(symbol).address + vmlinux.offset) + addr = kernel_layer.canonicalize( + vmlinux.get_symbol(symbol).address + vmlinux.offset + ) packed_addr = struct.pack(pack_format, addr) # debug message to show needles being searched for and symbol names @@ -121,20 +123,20 @@ class PsScan(interfaces.plugins.PluginInterface): # append to needles list the packed hex for searching needles.append(packed_addr) - # find the memory layer to scan if len(kernel_layer.dependencies) > 1: vollog.warning( - f"Kernel layer depends on multiple layers however only {kernel_layer.dependencies[0]} will be scanned by this plugin." - ) + f"Kernel layer depends on multiple layers however only {kernel_layer.dependencies[0]} will be scanned by this plugin." + ) elif len(kernel_layer.dependencies) == 0: vollog.error( - f"Kernel layer has no dependencies, meaning there is no memory layer for this plugin to scan." - ) - raise exceptions.LayerException(kernel_layer_name, f"Layer {kernel_layer_name} has no dependencies") - + f"Kernel layer has no dependencies, meaning there is no memory layer for this plugin to scan." + ) + raise exceptions.LayerException( + kernel_layer_name, f"Layer {kernel_layer_name} has no dependencies" + ) memory_layer = context.layers[kernel_layer.dependencies[0]] - + # scan the memory_layer for these needles for address, _ in memory_layer.scan( context, scanners.MultiStringScanner(needles) From a35afd4f343c10d7f8d1df2cb5eec8364c3dbd5a Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 8 Mar 2023 20:40:04 +0000 Subject: [PATCH 082/140] Core: Bump framwork version after release branch --- volatility3/framework/constants/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/constants/__init__.py b/volatility3/framework/constants/__init__.py index 8f1163fe1..3a6b24ea8 100644 --- a/volatility3/framework/constants/__init__.py +++ b/volatility3/framework/constants/__init__.py @@ -45,7 +45,7 @@ BANG = "!" # We use the SemVer 2.0.0 versioning scheme VERSION_MAJOR = 2 # Number of releases of the library with a breaking change VERSION_MINOR = 4 # Number of changes that only add to the interface -VERSION_PATCH = 1 # Number of changes that do not change the interface +VERSION_PATCH = 2 # Number of changes that do not change the interface VERSION_SUFFIX = "" # TODO: At version 2.0.0, remove the symbol_shift feature From b8755ac574e8226321ed169a1d6cac3a39505617 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 8 Mar 2023 20:42:24 +0000 Subject: [PATCH 083/140] Linux: fix black lint issues --- volatility3/framework/plugins/linux/envars.py | 1 - volatility3/framework/plugins/linux/iomem.py | 1 - 2 files changed, 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/envars.py b/volatility3/framework/plugins/linux/envars.py index 028eb2a57..5cbf0f502 100644 --- a/volatility3/framework/plugins/linux/envars.py +++ b/volatility3/framework/plugins/linux/envars.py @@ -70,7 +70,6 @@ class Envars(plugins.PluginInterface): # if mm exists attempt to get envars if mm: - # get process layer to read envars from proc_layer_name = task.add_process_layer() if proc_layer_name is None: diff --git a/volatility3/framework/plugins/linux/iomem.py b/volatility3/framework/plugins/linux/iomem.py index fddea4668..8efbf3b57 100644 --- a/volatility3/framework/plugins/linux/iomem.py +++ b/volatility3/framework/plugins/linux/iomem.py @@ -128,7 +128,6 @@ class IOMem(interfaces.plugins.PluginInterface): # only continue if iomem_root address was located if iomem_root_offset is not None: - # recursively parse the resources starting from the root resource at 'iomem_resource' for depth, (name, start, end) in self.parse_resource( self.context, vmlinux_module_name, iomem_root_offset From 99417cdfcc87d93d82b7288d8ee06ec4e06ada07 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 14:17:37 +0000 Subject: [PATCH 084/140] Linux: Psscan check parent pointer is valid --- volatility3/framework/plugins/linux/psscan.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index f4bfd347e..ca9d30586 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -52,7 +52,10 @@ class PsScan(interfaces.plugins.PluginInterface): """ pid = task.tgid tid = task.pid - ppid = task.parent.tgid if task.parent else 0 + ppid = 0 + + if task.parent.is_readable(): + ppid = task.parent.tgid name = utility.array_to_string(task.comm) exit_state = DescExitStateEnum(task.exit_state).name From 36ec5164703d2b5eaf0b3ff0d5f3a5f59572a5ff Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 15:17:24 +0000 Subject: [PATCH 085/140] Linux: Fix psscan task native_layer --- volatility3/framework/plugins/linux/psscan.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/psscan.py b/volatility3/framework/plugins/linux/psscan.py index ca9d30586..462577e58 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -138,6 +138,7 @@ class PsScan(interfaces.plugins.PluginInterface): raise exceptions.LayerException( kernel_layer_name, f"Layer {kernel_layer_name} has no dependencies" ) + memory_layer_name = kernel_layer.dependencies[0] memory_layer = context.layers[kernel_layer.dependencies[0]] # scan the memory_layer for these needles @@ -148,7 +149,8 @@ class PsScan(interfaces.plugins.PluginInterface): ptask = context.object( vmlinux.symbol_table_name + constants.BANG + "task_struct", offset=address - sched_class_offset, - layer_name="memory_layer", + layer_name=memory_layer_name, + native_layer_name=kernel_layer_name, ) # sanity check exit_state From 46f56770af1785f5f0bcfb887849bb96e164f23c Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 15:18:58 +0000 Subject: [PATCH 086/140] Core: Pointer is_readable should check the native layer --- volatility3/framework/objects/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/objects/__init__.py b/volatility3/framework/objects/__init__.py index a04eedd87..3b1745718 100644 --- a/volatility3/framework/objects/__init__.py +++ b/volatility3/framework/objects/__init__.py @@ -442,7 +442,7 @@ class Pointer(Integer): def is_readable(self, layer_name: Optional[str] = None) -> bool: """Determines whether the address of this pointer can be read from memory.""" - layer_name = layer_name or self.vol.layer_name + layer_name = layer_name or self.vol.native_layer_name return self._context.layers[layer_name].is_valid(self, self.vol.subtype.size) def __getattr__(self, attr: str) -> Any: From 9216bab61b3187fc248760ffdda5b86c8a694c9a Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 17:32:46 +0000 Subject: [PATCH 087/140] Core: Improve import exception reporting --- volatility3/framework/__init__.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/__init__.py b/volatility3/framework/__init__.py index ec0edc2fe..479925fb7 100644 --- a/volatility3/framework/__init__.py +++ b/volatility3/framework/__init__.py @@ -26,6 +26,7 @@ import importlib import inspect import logging import os +import traceback from typing import Any, Dict, Generator, List, Tuple, Type, TypeVar from volatility3.framework import constants, interfaces @@ -183,7 +184,11 @@ def import_file(module: str, path: str, ignore_errors: bool = False) -> List[str try: importlib.import_module(module) except ImportError as e: - vollog.debug(str(e)) + vollog.debug( + "".join( + traceback.TracebackException.from_exception(e).format(chain=True) + ) + ) vollog.debug( "Failed to import module {} based on file: {}".format(module, path) ) From d9a365d96fcd990c7faba32ab7aa63523203e9f8 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 17:33:20 +0000 Subject: [PATCH 088/140] Linux: Rename linux.envars to linux.envvars --- volatility3/framework/plugins/linux/envars.py | 121 +----------------- .../framework/plugins/linux/envvars.py | 121 ++++++++++++++++++ 2 files changed, 127 insertions(+), 115 deletions(-) create mode 100644 volatility3/framework/plugins/linux/envvars.py diff --git a/volatility3/framework/plugins/linux/envars.py b/volatility3/framework/plugins/linux/envars.py index 5cbf0f502..c4e3ed3c9 100644 --- a/volatility3/framework/plugins/linux/envars.py +++ b/volatility3/framework/plugins/linux/envars.py @@ -1,121 +1,12 @@ -# This file is Copyright 2022 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.plugins import envvars import logging -from volatility3.framework import exceptions, renderers -from volatility3.framework.configuration import requirements -from volatility3.framework.interfaces import plugins -from volatility3.framework.objects import utility -from volatility3.plugins.linux import pslist - vollog = logging.getLogger(__name__) -class Envars(plugins.PluginInterface): - """Lists processes with their environment variables""" - - _required_framework_version = (2, 0, 0) - - @classmethod - def get_requirements(cls): - # Since we're calling the plugin, make sure we have the plugin's requirements - return [ - requirements.ModuleRequirement( - name="kernel", - description="Linux kernel", - architectures=["Intel32", "Intel64"], - ), - 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): - """Generates a listing of processes along with environment variables""" - - # walk the process list and return the envars - for task in tasks: - pid = task.pid - - # get process name as string - name = utility.array_to_string(task.comm) - - # try and get task parent - try: - ppid = task.parent.pid - except exceptions.InvalidAddressException: - vollog.debug( - f"Unable to read parent pid for task {pid} {name}, setting ppid to 0." - ) - ppid = 0 - - # kernel threads never have an mm as they do not have userland mappings - try: - mm = task.mm - except exceptions.InvalidAddressException: - # no mm so cannot get envars - vollog.debug( - f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars." - ) - mm = None - continue - - # if mm exists attempt to get envars - if mm: - # get process layer to read envars from - proc_layer_name = task.add_process_layer() - if proc_layer_name is None: - vollog.debug( - f"Unable to construct process layer for task {pid} {name}, will not extract any envars." - ) - continue - proc_layer = self.context.layers[proc_layer_name] - - # get the size of the envars with sanity checking - envars_size = task.mm.env_end - task.mm.env_start - if not (0 < envars_size <= 8192): - vollog.debug( - f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars." - ) - continue - - # attempt to read all envars data - try: - envar_data = proc_layer.read(task.mm.env_start, envars_size) - except exceptions.InvalidAddressException: - vollog.debug( - f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars." - ) - continue - - # parse envar data, envars are null terminated, keys and values are separated by '=' - envar_data = envar_data.rstrip(b"\x00") - for envar_pair in envar_data.split(b"\x00"): - try: - key, value = envar_pair.decode().split("=", 1) - except ValueError: - vollog.debug( - f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated" - ) - continue - yield (0, (pid, ppid, name, key, value)) - - def run(self): - filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) - - return renderers.TreeGrid( - [("PID", int), ("PPID", int), ("COMM", str), ("KEY", str), ("VALUE", str)], - self._generator( - pslist.PsList.list_tasks( - self.context, self.config["kernel"], filter_func=filter_func - ) - ), +class Envars(envvars.Envvars): + def run(self, *args, **kwargs): + vollog.warning( + "The linux.envars plugin has been renamed to linux.envvars and will only be accessible through the new name in a future release" ) + return super().run(*args, **kwargs) diff --git a/volatility3/framework/plugins/linux/envvars.py b/volatility3/framework/plugins/linux/envvars.py new file mode 100644 index 000000000..1d6c8b784 --- /dev/null +++ b/volatility3/framework/plugins/linux/envvars.py @@ -0,0 +1,121 @@ +# This file is Copyright 2022 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 exceptions, renderers +from volatility3.framework.configuration import requirements +from volatility3.framework.interfaces import plugins +from volatility3.framework.objects import utility +from volatility3.plugins.linux import pslist + +vollog = logging.getLogger(__name__) + + +class Envvars(plugins.PluginInterface): + """Lists processes with their environment variables""" + + _required_framework_version = (2, 0, 0) + + @classmethod + def get_requirements(cls): + # Since we're calling the plugin, make sure we have the plugin's requirements + return [ + requirements.ModuleRequirement( + name="kernel", + description="Linux kernel", + architectures=["Intel32", "Intel64"], + ), + 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): + """Generates a listing of processes along with environment variables""" + + # walk the process list and return the envars + for task in tasks: + pid = task.pid + + # get process name as string + name = utility.array_to_string(task.comm) + + # try and get task parent + try: + ppid = task.parent.pid + except exceptions.InvalidAddressException: + vollog.debug( + f"Unable to read parent pid for task {pid} {name}, setting ppid to 0." + ) + ppid = 0 + + # kernel threads never have an mm as they do not have userland mappings + try: + mm = task.mm + except exceptions.InvalidAddressException: + # no mm so cannot get envars + vollog.debug( + f"Unable to access mm for task {pid} {name} it is likely a kernel thread, will not extract any envars." + ) + mm = None + continue + + # if mm exists attempt to get envars + if mm: + # get process layer to read envars from + proc_layer_name = task.add_process_layer() + if proc_layer_name is None: + vollog.debug( + f"Unable to construct process layer for task {pid} {name}, will not extract any envars." + ) + continue + proc_layer = self.context.layers[proc_layer_name] + + # get the size of the envars with sanity checking + envars_size = task.mm.env_end - task.mm.env_start + if not (0 < envars_size <= 8192): + vollog.debug( + f"Task {pid} {name} appears to have envars of size {envars_size} bytes which fails the sanity checking, will not extract any envars." + ) + continue + + # attempt to read all envars data + try: + envar_data = proc_layer.read(task.mm.env_start, envars_size) + except exceptions.InvalidAddressException: + vollog.debug( + f"Unable to read full envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)} for {envars_size} bytes, will not extract any envars." + ) + continue + + # parse envar data, envars are null terminated, keys and values are separated by '=' + envar_data = envar_data.rstrip(b"\x00") + for envar_pair in envar_data.split(b"\x00"): + try: + key, value = envar_pair.decode().split("=", 1) + except ValueError: + vollog.debug( + f"Unable to extract envars for {pid} {name} starting at virtual offset {hex(task.mm.env_start)}, they don't appear to be '=' separated" + ) + continue + yield (0, (pid, ppid, name, key, value)) + + def run(self): + filter_func = pslist.PsList.create_pid_filter(self.config.get("pid", None)) + + return renderers.TreeGrid( + [("PID", int), ("PPID", int), ("COMM", str), ("KEY", str), ("VALUE", str)], + self._generator( + pslist.PsList.list_tasks( + self.context, self.config["kernel"], filter_func=filter_func + ) + ), + ) From 3d51b4a41a7110670826557ea82f3bd0ecf55e5c Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 24 Mar 2023 15:08:59 +0000 Subject: [PATCH 089/140] Add basic support for linux maple tree struct --- .../framework/symbols/linux/__init__.py | 2 + .../symbols/linux/extensions/__init__.py | 142 ++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index ce07167e5..7a22241a5 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -50,6 +50,8 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.optional_set_type_class("bt_sock", extensions.bt_sock) self.optional_set_type_class("xdp_sock", extensions.xdp_sock) + # Only found in 6.1+ kernels + self.optional_set_type_class("maple_tree", extensions.maple_tree) class LinuxUtilities(interfaces.configuration.VersionableInterface): """Class with multiple useful linux functions.""" diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 5ab8f1aa0..72173f471 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -294,11 +294,128 @@ class fs_struct(objects.StructType): raise AttributeError("Unable to find the root mount") +class maple_tree(objects.StructType): + # include/linux/maple_tree.h + # Mask for Maple Tree Flags + MT_FLAGS_HEIGHT_MASK = 0x7C + MT_FLAGS_HEIGHT_OFFSET = 0x02 + + # Shift and mask to extract information from maple tree node pointers + MAPLE_NODE_TYPE_SHIFT = 0x03 + MAPLE_NODE_TYPE_MASK = 0x0F + MAPLE_NODE_POINTER_MASK = 0xFF + + # types of Maple Tree Nodes + MAPLE_DENSE = 0 + MAPLE_LEAF_64 = 1 + MAPLE_RANGE_64 = 2 + MAPLE_ARANGE_64 = 3 + + def get_slot_iter(self): + """Parse the Maple Tree and return every non zero slot.""" + maple_tree_offset, _, _ = self._parse_maple_tree_entry(self.vol.offset) + maple_tree_depth = ( + self.ma_flags & self.MT_FLAGS_HEIGHT_MASK + ) >> self.MT_FLAGS_HEIGHT_OFFSET + yield from self._parse_maple_tree_node( + self.ma_root, maple_tree_offset, maple_tree_depth + ) + + def _parse_maple_tree_node( + self, maple_tree_entry, parent, maple_tree_depth, seen=set(), depth=1 + ): + """Recursively parse Maple Tree Nodes and yield all non empty slots""" + + # protect against unlikely loop + if maple_tree_entry in seen: + vollog.warning( + f"The mte {hex(maple_tree_entry)} has all ready been seen, no further results will be produced for this node." + ) + return + else: + seen.add(maple_tree_entry) + if maple_tree_depth < depth: + vollog.warning( + f"The depth for the maple tree at {hex(self.vol.offset)} is {maple_tree_depth}, however when parsing the nodes " + f"a depth of {depth} was reached. This is unexpected and may lead to incorrect results." + ) + # parse the mte to extract the pointer value, node type, and leaf status + pointer, node_type, is_leaf = self._parse_maple_tree_entry(maple_tree_entry) + + # create a pointer object for the node parent mte (note this will include flags in the low bits) + symbol_table_name = self.get_symbol_table_name() + node_parent_mte = self._context.object( + symbol_table_name + constants.BANG + "pointer", + layer_name=self.vol.layer_name, + offset=pointer, + ) + + # extract the actual pointer to the parent of this node + node_parent_pointer, _, _ = self._parse_maple_tree_entry(node_parent_mte) + + # verify that the node_parent_pointer correctly points to the parent + assert node_parent_pointer == parent + + # create a node object + node = self._context.object( + symbol_table_name + constants.BANG + "maple_node", + layer_name=self.vol.layer_name, + offset=pointer, + ) + + # parse the slots based on the node type + if node_type == self.MAPLE_DENSE: + assert is_leaf == True + for slot in node.alloc.slot: + if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: + yield slot + elif node_type == self.MAPLE_LEAF_64: + assert is_leaf == True + for slot in node.mr64.slot: + if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: + yield slot + elif node_type == self.MAPLE_RANGE_64: + assert is_leaf == False + for slot in node.mr64.slot: + if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: + yield from self._parse_maple_tree_node( + slot, pointer, maple_tree_depth, seen, depth + 1 + ) + elif node_type == self.MAPLE_ARANGE_64: + assert is_leaf == False + for slot in node.ma64.slot: + if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: + yield from self._parse_maple_tree_node( + slot, pointer, maple_tree_depth, seen, depth + 1 + ) + else: + # unkown maple node type + raise AttributeError( + f"Unkown Maple Tree node type {node_type} at offset {hex(pointer)}." + ) + + def _parse_maple_tree_entry(self, maple_tree_entry): + """Parse a Maple Tree Entry and return the pointer, node type, if the node is a leaf, if the node is the root""" + # Extract the node type + node_type = ( + maple_tree_entry >> self.MAPLE_NODE_TYPE_SHIFT + ) & self.MAPLE_NODE_TYPE_MASK + + # Determine if it's a leaf node or not + is_leaf = node_type < self.MAPLE_RANGE_64 + + # Clear the lower bits to get the true pointer value + pointer = maple_tree_entry & ~(self.MAPLE_NODE_POINTER_MASK) + + return pointer, node_type, is_leaf class mm_struct(objects.StructType): def get_mmap_iter(self) -> Iterable[interfaces.objects.ObjectInterface]: """Returns an iterator for the mmap list member of an mm_struct.""" + if not self.has_member('mmap'): + raise AttributeError("get_mmap_iter called on mm_struct where no mmap member exists.") + if not self.mmap: return @@ -312,7 +429,32 @@ class mm_struct(objects.StructType): seen.add(link.vol.offset) link = link.vm_next + def get_maple_tree_iter(self) -> Iterable[interfaces.objects.ObjectInterface]: + """Returns an iterator for the mm_mt member of an mm_struct.""" + + if not self.has_member('mm_mt'): + raise AttributeError("get_maple_tree_iter called on mm_struct where no mm_mt member exists.") + symbol_table_name = self.get_symbol_table_name() + for vma_pointer in self.mm_mt.get_slot_iter(): + # convert pointer to vm_area_struct and yield + vma = self._context.object( + symbol_table_name + constants.BANG + "vm_area_struct", + layer_name=self.vol.layer_name, + offset=vma_pointer + ) + yield vma + + def get_vma_iter(self) -> Iterable[interfaces.objects.ObjectInterface]: + """Returns an iterator for the VMAs in an mm_struct. Automatically choosing the mmap or mm_mt as required.""" + + if self.has_member('mmap'): + yield from self.get_mmap_iter() + elif self.has_member('mm_mt'): + yield from self.get_maple_tree_iter() + else: + raise AttributeError("Unable to find mmap or mm_mt in mm_struct") + class super_block(objects.StructType): # include/linux/kdev_t.h MINORBITS = 20 From 5207cfcb93cf7f8955b5a499ac13a46ba962da78 Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 24 Mar 2023 15:10:34 +0000 Subject: [PATCH 090/140] Modify linux plugins to support both mmap and mm_mt --- volatility3/framework/plugins/linux/elfs.py | 2 +- volatility3/framework/plugins/linux/malfind.py | 2 +- volatility3/framework/plugins/linux/proc.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/linux/elfs.py b/volatility3/framework/plugins/linux/elfs.py index 822a69dd6..2ff5eb591 100644 --- a/volatility3/framework/plugins/linux/elfs.py +++ b/volatility3/framework/plugins/linux/elfs.py @@ -48,7 +48,7 @@ class Elfs(plugins.PluginInterface): name = utility.array_to_string(task.comm) - for vma in task.mm.get_mmap_iter(): + for vma in task.mm.get_vma_iter(): hdr = proc_layer.read(vma.vm_start, 4, pad=True) if not ( hdr[0] == 0x7F diff --git a/volatility3/framework/plugins/linux/malfind.py b/volatility3/framework/plugins/linux/malfind.py index 18237b80c..1fd005de8 100644 --- a/volatility3/framework/plugins/linux/malfind.py +++ b/volatility3/framework/plugins/linux/malfind.py @@ -46,7 +46,7 @@ class Malfind(interfaces.plugins.PluginInterface): proc_layer = self.context.layers[proc_layer_name] - for vma in task.mm.get_mmap_iter(): + for vma in task.mm.get_vma_iter(): if vma.is_suspicious() and vma.get_name(self.context, task) != "[vdso]": data = proc_layer.read(vma.vm_start, 64, pad=True) yield vma, data diff --git a/volatility3/framework/plugins/linux/proc.py b/volatility3/framework/plugins/linux/proc.py index 9d8af482e..8979f6f63 100644 --- a/volatility3/framework/plugins/linux/proc.py +++ b/volatility3/framework/plugins/linux/proc.py @@ -44,7 +44,7 @@ class Maps(plugins.PluginInterface): name = utility.array_to_string(task.comm) - for vma in task.mm.get_mmap_iter(): + for vma in task.mm.get_vma_iter(): flags = vma.get_protection() page_offset = vma.get_page_offset() major = 0 From 89e477a068092cad50acfaa0fd965097ae5da8c7 Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 24 Mar 2023 20:07:32 +0000 Subject: [PATCH 091/140] Remove _parse_maple_tree_entry function and make parsing easier to read --- .../symbols/linux/extensions/__init__.py | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 72173f471..9375a288b 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -313,7 +313,7 @@ class maple_tree(objects.StructType): def get_slot_iter(self): """Parse the Maple Tree and return every non zero slot.""" - maple_tree_offset, _, _ = self._parse_maple_tree_entry(self.vol.offset) + maple_tree_offset = self.vol.offset & ~(self.MAPLE_NODE_POINTER_MASK) maple_tree_depth = ( self.ma_flags & self.MT_FLAGS_HEIGHT_MASK ) >> self.MT_FLAGS_HEIGHT_OFFSET @@ -340,7 +340,11 @@ class maple_tree(objects.StructType): f"a depth of {depth} was reached. This is unexpected and may lead to incorrect results." ) # parse the mte to extract the pointer value, node type, and leaf status - pointer, node_type, is_leaf = self._parse_maple_tree_entry(maple_tree_entry) + pointer = maple_tree_entry & ~(self.MAPLE_NODE_POINTER_MASK) + node_type = ( + maple_tree_entry >> self.MAPLE_NODE_TYPE_SHIFT + ) & self.MAPLE_NODE_TYPE_MASK + is_leaf = node_type < self.MAPLE_RANGE_64 # create a pointer object for the node parent mte (note this will include flags in the low bits) symbol_table_name = self.get_symbol_table_name() @@ -351,7 +355,7 @@ class maple_tree(objects.StructType): ) # extract the actual pointer to the parent of this node - node_parent_pointer, _, _ = self._parse_maple_tree_entry(node_parent_mte) + node_parent_pointer = node_parent_mte & ~(self.MAPLE_NODE_POINTER_MASK) # verify that the node_parent_pointer correctly points to the parent assert node_parent_pointer == parent @@ -394,21 +398,6 @@ class maple_tree(objects.StructType): f"Unkown Maple Tree node type {node_type} at offset {hex(pointer)}." ) - def _parse_maple_tree_entry(self, maple_tree_entry): - """Parse a Maple Tree Entry and return the pointer, node type, if the node is a leaf, if the node is the root""" - # Extract the node type - node_type = ( - maple_tree_entry >> self.MAPLE_NODE_TYPE_SHIFT - ) & self.MAPLE_NODE_TYPE_MASK - - # Determine if it's a leaf node or not - is_leaf = node_type < self.MAPLE_RANGE_64 - - # Clear the lower bits to get the true pointer value - pointer = maple_tree_entry & ~(self.MAPLE_NODE_POINTER_MASK) - - return pointer, node_type, is_leaf - class mm_struct(objects.StructType): def get_mmap_iter(self) -> Iterable[interfaces.objects.ObjectInterface]: """Returns an iterator for the mmap list member of an mm_struct.""" From 089234671c70b746c91c25ac2980a6a4f35607b0 Mon Sep 17 00:00:00 2001 From: Eve Date: Mon, 27 Mar 2023 06:29:30 +0100 Subject: [PATCH 092/140] Remove checks for leaf node in linux maple tree parsing, node type is enough. --- volatility3/framework/symbols/linux/extensions/__init__.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 9375a288b..a8bf1b6aa 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -344,7 +344,6 @@ class maple_tree(objects.StructType): node_type = ( maple_tree_entry >> self.MAPLE_NODE_TYPE_SHIFT ) & self.MAPLE_NODE_TYPE_MASK - is_leaf = node_type < self.MAPLE_RANGE_64 # create a pointer object for the node parent mte (note this will include flags in the low bits) symbol_table_name = self.get_symbol_table_name() @@ -369,24 +368,20 @@ class maple_tree(objects.StructType): # parse the slots based on the node type if node_type == self.MAPLE_DENSE: - assert is_leaf == True for slot in node.alloc.slot: if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield slot elif node_type == self.MAPLE_LEAF_64: - assert is_leaf == True for slot in node.mr64.slot: if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield slot elif node_type == self.MAPLE_RANGE_64: - assert is_leaf == False for slot in node.mr64.slot: if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield from self._parse_maple_tree_node( slot, pointer, maple_tree_depth, seen, depth + 1 ) elif node_type == self.MAPLE_ARANGE_64: - assert is_leaf == False for slot in node.ma64.slot: if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield from self._parse_maple_tree_node( From 60292c2da1efa886d0f46bd720af537a6069a623 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 29 Mar 2023 20:46:06 +0100 Subject: [PATCH 093/140] Linux: Fix slight issue in envvars renaming --- volatility3/framework/plugins/linux/envars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/envars.py b/volatility3/framework/plugins/linux/envars.py index c4e3ed3c9..758943312 100644 --- a/volatility3/framework/plugins/linux/envars.py +++ b/volatility3/framework/plugins/linux/envars.py @@ -1,4 +1,4 @@ -from volatility3.plugins import envvars +from volatility3.plugins.linux import envvars import logging vollog = logging.getLogger(__name__) From 41edab23099b1e011d1b0acc4ef538f9ad406ec1 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Tue, 4 Apr 2023 23:23:27 +0100 Subject: [PATCH 094/140] Plugins: Support yara-4.3.0 and above --- volatility3/framework/plugins/yarascan.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/yarascan.py b/volatility3/framework/plugins/yarascan.py index 1c548e5a6..1c8467689 100644 --- a/volatility3/framework/plugins/yarascan.py +++ b/volatility3/framework/plugins/yarascan.py @@ -34,13 +34,27 @@ class YaraScanner(interfaces.layers.ScannerInterface): if rules is None: raise ValueError("No rules provided to YaraScanner") self._rules = rules + self.st_object = not tuple([int(x) for x in yara.__version__.split(".")]) < ( + 4, + 3, + ) def __call__( self, data: bytes, data_offset: int ) -> Iterable[Tuple[int, str, str, bytes]]: for match in self._rules.match(data=data): - for offset, name, value in match.strings: - yield (offset + data_offset, match.rule, name, value) + if self.st_object: + for match_string in match.strings: + for instance in match_string.instances: + yield ( + instance.offset + data_offset, + match.rule, + match_string.identifier, + instance.matched_data, + ) + else: + for offset, name, value in match.strings: + yield (offset + data_offset, match.rule, name, value) class YaraScan(plugins.PluginInterface): From f33ea67e860837f579f7af2d60861baa68f350d5 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Tue, 4 Apr 2023 23:57:35 +0100 Subject: [PATCH 095/140] Automagic: Update clear-cache and do removals first --- volatility3/framework/__init__.py | 6 +----- volatility3/framework/automagic/symbol_cache.py | 17 ++++++++--------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/volatility3/framework/__init__.py b/volatility3/framework/__init__.py index 479925fb7..c7b23a9c3 100644 --- a/volatility3/framework/__init__.py +++ b/volatility3/framework/__init__.py @@ -224,8 +224,4 @@ def list_plugins() -> Dict[str, Type[interfaces.plugins.PluginInterface]]: def clear_cache(complete=False): - glob_pattern = "*.cache" - if not complete: - glob_pattern = "data_" + glob_pattern - for cache_filename in glob.glob(os.path.join(constants.CACHE_PATH, glob_pattern)): - os.unlink(cache_filename) + os.unlink(os.path.join(constants.CACHE_PATH, constants.IDENTIFIERS_FILENAME)) diff --git a/volatility3/framework/automagic/symbol_cache.py b/volatility3/framework/automagic/symbol_cache.py index a58bf0091..29f2cfd08 100644 --- a/volatility3/framework/automagic/symbol_cache.py +++ b/volatility3/framework/automagic/symbol_cache.py @@ -310,6 +310,14 @@ class SqliteCache(CacheManagerInterface): new_locations = on_disk_locations.difference(cached_locations) missing_locations = cached_locations.difference(on_disk_locations) + # Missing entries + if missing_locations: + self._database.cursor().execute( + f"DELETE FROM cache WHERE location IN ({','.join(['?'] * len(missing_locations))})", + [x for x in missing_locations], + ) + self._database.commit() + cache_update = set() files_to_timestamp = on_disk_locations.intersection(cached_locations) if files_to_timestamp: @@ -437,15 +445,6 @@ class SqliteCache(CacheManagerInterface): progress_callback(100, "Reading remote ISF list") self._database.commit() - # Missing entries - - if missing_locations: - self._database.cursor().execute( - f"DELETE FROM cache WHERE location IN ({','.join(['?'] * len(missing_locations))})", - [x for x in missing_locations], - ) - self._database.commit() - def get_identifier_dictionary( self, operating_system: Optional[str] = None, local_only: bool = False ) -> Dict[bytes, str]: From 2e0ecdd770b84aab991aba85ae2d33143212ba43 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Fri, 7 Apr 2023 18:04:56 +0900 Subject: [PATCH 096/140] Fix: typo for linux iomem plugin --- volatility3/framework/plugins/linux/iomem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/iomem.py b/volatility3/framework/plugins/linux/iomem.py index 8efbf3b57..2056851aa 100644 --- a/volatility3/framework/plugins/linux/iomem.py +++ b/volatility3/framework/plugins/linux/iomem.py @@ -42,7 +42,7 @@ class IOMem(interfaces.plugins.PluginInterface): Args: context: The context to retrieve required elements (layers, symbol tables) from vmlinux_module_name: The name of the kernel module on which to operate - resource_offset: The offset to the resouce to be parsed + resource_offset: The offset to the resource to be parsed seen: The set of resource offsets that have already been parsed depth: How deep into the resource structure we are @@ -57,7 +57,7 @@ class IOMem(interfaces.plugins.PluginInterface): except exceptions.InvalidAddressException: vollog.warning( f"Unable to create resource object at {resource_offset:#x}. This resource, " - "its sibling, and any of it's childern and will be missing from the output." + "its sibling, and any of it's children and will be missing from the output." ) return None From ebb74e2f9292b7cdd211c4886b06fb407e751c80 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 12 Apr 2023 19:57:15 +0100 Subject: [PATCH 097/140] Core: Bump the copyright year of the README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 502e26f10..471735af8 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ The latest generated copy of the documentation can be found at: Date: Fri, 14 Apr 2023 08:08:27 +0100 Subject: [PATCH 098/140] Volshell: Mark the script as executable in the repo --- volshell.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 volshell.py diff --git a/volshell.py b/volshell.py old mode 100644 new mode 100755 From 58af80df3c9adfbe8df382b2242087d385c319a7 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 16 Apr 2023 13:29:41 +0100 Subject: [PATCH 099/140] Requirements: Shift location_from_file to the requirement from the CLI class --- volatility3/cli/__init__.py | 21 +++++---------- volatility3/framework/automagic/windows.py | 3 +++ .../framework/configuration/requirements.py | 26 +++++++++++++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index 336902d50..99052d82e 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -353,7 +353,9 @@ class CommandLine: ### if args.file: try: - single_location = self.location_from_file(args.file) + single_location = requirements.URLRequirement.location_from_file( + args.file + ) ctx.config["automagic.LayerStacker.single_location"] = single_location except ValueError as excp: parser.error(str(excp)) @@ -456,19 +458,10 @@ class CommandLine: Returns: The URL for the location of the file """ - # We want to work in URLs, but we need to accept absolute and relative files (including on windows) - single_location = parse.urlparse(filename, "") - if single_location.scheme == "" or len(single_location.scheme) == 1: - single_location = parse.urlparse( - parse.urljoin("file:", request.pathname2url(os.path.abspath(filename))) - ) - if single_location.scheme == "file": - if not os.path.exists(request.url2pathname(single_location.path)): - filename = request.url2pathname(single_location.path) - if not filename: - raise ValueError("File URL looks incorrect (potentially missing /)") - raise ValueError(f"File does not exist: {filename}") - return parse.urlunparse(single_location) + vollog.debug( + f"{__name__}.location_from_file has been deprecated and moved to requirements.URIRequirement.location_from_file" + ) + return requirements.URIRequirement.location_from_file(filename) def process_exceptions(self, excp): """Provide useful feedback if an exception occurs during a run of a plugin.""" diff --git a/volatility3/framework/automagic/windows.py b/volatility3/framework/automagic/windows.py index 986eeae22..ccc8de2eb 100644 --- a/volatility3/framework/automagic/windows.py +++ b/volatility3/framework/automagic/windows.py @@ -367,6 +367,7 @@ class WinSwapLayers(interfaces.automagic.AutomagicInterface): progress_callback: constants.ProgressCallback = None, ) -> None: """Finds translation layers that can have swap layers added.""" + path_join = interfaces.configuration.path_join self._translation_requirement = self.find_requirements( context, @@ -382,11 +383,13 @@ class WinSwapLayers(interfaces.automagic.AutomagicInterface): swap_sub_config, swap_req = self.find_swap_requirement( trans_sub_config, trans_req ) + counter = 0 swap_config = interfaces.configuration.parent_path(swap_sub_config) if swap_req and swap_req.unsatisfied(context, swap_config): # See if any of them need constructing + for swap_location in self.config.get("single_swap_locations", []): # Setup config locations/paths current_layer_name = swap_req.name + str(counter) diff --git a/volatility3/framework/configuration/requirements.py b/volatility3/framework/configuration/requirements.py index 6b64b1cb9..abdffdbe4 100644 --- a/volatility3/framework/configuration/requirements.py +++ b/volatility3/framework/configuration/requirements.py @@ -10,7 +10,9 @@ expect to be in the context (such as particular layers or symboltables). """ import abc import logging +import os from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type +from urllib import parse, request from volatility3.framework import constants, interfaces @@ -55,6 +57,30 @@ class URIRequirement(StringRequirement): # TODO: Maybe a a check that to unsatisfied that the path really is a URL? + @classmethod + def location_from_file(cls, filename: str) -> str: + """Returns the URL location from a file parameter (which may be a URL) + + Args: + filename: The path to the file (either an absolute, relative, or URL path) + + Returns: + The URL for the location of the file + """ + # We want to work in URLs, but we need to accept absolute and relative files (including on windows) + single_location = parse.urlparse(filename, "") + if single_location.scheme == "" or len(single_location.scheme) == 1: + single_location = parse.urlparse( + parse.urljoin("file:", request.pathname2url(os.path.abspath(filename))) + ) + if single_location.scheme == "file": + if not os.path.exists(request.url2pathname(single_location.path)): + filename = request.url2pathname(single_location.path) + if not filename: + raise ValueError("File URL looks incorrect (potentially missing /)") + raise ValueError(f"File does not exist: {filename}") + return parse.urlunparse(single_location) + class BytesRequirement(interfaces.configuration.SimpleTypeRequirement): """A requirement type that contains a byte string.""" From a5e6c550e3fbaa0110d6398b57a570c16914f8aa Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 16 Apr 2023 13:30:55 +0100 Subject: [PATCH 100/140] Automagic: Handle file swap locations and throw a warning if they don't exist --- volatility3/framework/automagic/windows.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/automagic/windows.py b/volatility3/framework/automagic/windows.py index ccc8de2eb..a8530829b 100644 --- a/volatility3/framework/automagic/windows.py +++ b/volatility3/framework/automagic/windows.py @@ -401,7 +401,17 @@ class WinSwapLayers(interfaces.automagic.AutomagicInterface): # Fill in the config if swap_location: context.config[current_layer_path] = current_layer_name - context.config[layer_loc_path] = swap_location + try: + context.config[ + layer_loc_path + ] = requirements.URIRequirement.location_from_file( + swap_location + ) + except ValueError: + vollog.warning( + f"Volatility swap_location {swap_location} could not be validated - swap layer disabled" + ) + continue context.config[ layer_class_path ] = "volatility3.framework.layers.physical.FileLayer" From 33f54f8cf7be2039c3e976917f66ced49ce8365b Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Wed, 26 Apr 2023 00:33:35 +1000 Subject: [PATCH 101/140] Replace LinuxUtilities._do_get_path() with the new mountinfo _do_get_path() avoiding duplicate code. It also fixes issue #930 --- .../framework/plugins/linux/mountinfo.py | 40 +---- .../framework/symbols/linux/__init__.py | 149 ++++++++++-------- 2 files changed, 84 insertions(+), 105 deletions(-) diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index ebd6e55a0..1de776412 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -9,6 +9,7 @@ from typing import Tuple, List, Iterable, Union from volatility3.framework import renderers, interfaces from volatility3.framework.configuration import requirements from volatility3.framework.interfaces import plugins +from volatility3.framework.symbols import linux from volatility3.plugins.linux import pslist vollog = logging.getLogger(__name__) @@ -71,40 +72,9 @@ class MountInfo(plugins.PluginInterface): ), ] - @classmethod - def _do_get_path(cls, mnt, fs_root) -> Union[None, str]: - """It mimics the Linux kernel prepend_path function.""" - vfsmnt = mnt.mnt - dentry = vfsmnt.get_mnt_root() - - path_reversed = [] - while dentry != fs_root.dentry or vfsmnt.vol.offset != fs_root.mnt: - if dentry == vfsmnt.get_mnt_root() or dentry.is_root(): - parent = mnt.get_mnt_parent().dereference() - # Escaped? - if dentry != vfsmnt.get_mnt_root(): - return None - - # Global root? - if mnt.vol.offset != parent.vol.offset: - dentry = mnt.get_mnt_mountpoint() - mnt = parent - vfsmnt = mnt.mnt - continue - - return None - - parent = dentry.d_parent - dname = dentry.d_name.name_as_str() - path_reversed.append(dname.strip("/")) - dentry = parent - - path = "/" + "/".join(reversed(path_reversed)) - return path - @classmethod def get_mountinfo( - cls, mnt, task + cls, mnt, task, context ) -> Union[ None, Tuple[int, int, str, str, str, List[str], List[str], str, str, List[str]] ]: @@ -115,8 +85,8 @@ class MountInfo(plugins.PluginInterface): if not mnt_root: return None - path_root = cls._do_get_path(mnt, task.fs.root) - if path_root is None: + path_root = linux.LinuxUtilities._get_path_root(context, mnt, task.fs.root) + if not path_root: return None mnt_root_path = mnt_root.path() @@ -207,7 +177,7 @@ class MountInfo(plugins.PluginInterface): if mnt_ns_ids and mnt_ns_id not in mnt_ns_ids: continue - mnt_info = self.get_mountinfo(mnt, task) + mnt_info = MountInfo.get_mountinfo(mnt, task, self.context) if mnt_info is None: continue diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index ce07167e5..80f5e9990 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -1,11 +1,11 @@ # 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 typing import Iterator, List, Tuple, Optional +from typing import Iterator, List, Tuple, Optional, Union from volatility3 import framework from volatility3.framework import constants, exceptions, interfaces, objects -from volatility3.framework.objects import utility +from volatility3.framework.objects import utility, Pointer from volatility3.framework.symbols import intermed from volatility3.framework.symbols.linux import extensions @@ -59,83 +59,92 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): framework.require_interface_version(*_required_framework_version) - # based on __d_path from the Linux kernel @classmethod - def _do_get_path(cls, rdentry, rmnt, dentry, vfsmnt) -> str: - ret_path: List[str] = [] - - while dentry != rdentry or vfsmnt != rmnt: - dname = dentry.path() - if dname == "": - break - - ret_path.insert(0, dname.strip("/")) - if dentry == vfsmnt.get_mnt_root() or dentry == dentry.d_parent: - if vfsmnt.get_mnt_parent() == vfsmnt: - break - - dentry = vfsmnt.get_mnt_mountpoint() - vfsmnt = vfsmnt.get_mnt_parent() - - continue - - parent = dentry.d_parent - dentry = parent - - # if we did not gather any valid dentrys in the path, then the entire file is - # either 1) smeared out of memory or 2) de-allocated and corresponding structures overwritten - # we return an empty string in this case to avoid confusion with something like a handle to the root - # directory (e.g., "/") - if not ret_path: - return "" - - ret_val = "/".join([str(p) for p in ret_path if p != ""]) - - if ret_val.startswith(("socket:", "pipe:")): - if ret_val.find("]") == -1: - try: - inode = dentry.d_inode - ino = inode.i_ino - except exceptions.InvalidAddressException: - ino = 0 - - ret_val = ret_val[:-1] + f":[{ino}]" - else: - ret_val = ret_val.replace("/", "") - - elif ret_val != "inotify": - ret_val = "/" + ret_val - - return ret_val - - # method used by 'older' kernels - # TODO: lookup when dentry_operations->d_name was merged into the mainline kernel for exact version - @classmethod - def _get_path_file(cls, task, filp) -> str: + def _get_path_file(cls, context, task, filp) -> str: rdentry = task.fs.get_root_dentry() rmnt = task.fs.get_root_mnt() - dentry = filp.get_dentry() vfsmnt = filp.get_vfsmnt() + dentry = filp.get_dentry() - return LinuxUtilities._do_get_path(rdentry, rmnt, dentry, vfsmnt) + return cls.do_get_path(rdentry, rmnt, dentry, vfsmnt, context) + + @classmethod + def _get_path_root(cls, context, mnt, fs_root) -> str: + rdentry = fs_root.dentry + rmnt = fs_root.mnt + vfsmnt = mnt.mnt + dentry = vfsmnt.mnt_root + + return cls.do_get_path(rdentry, rmnt, dentry, vfsmnt, context) + + @classmethod + def _get_vmlinux_from_volobj(cls, volobj, context): + symbol_table_arr = volobj.vol.type_name.split("!", 1) + symbol_table = symbol_table_arr[0] if len(symbol_table_arr) == 2 else None + + module_names = context.modules.get_modules_by_symbol_tables(symbol_table) + module_names = list(module_names) + + if not module_names: + raise ValueError(f"No module using the symbol table '{symbol_table}'") + + kernel_module_name = module_names[0] + kernel = context.modules[kernel_module_name] + + return kernel + + @classmethod + def _get_mnt_from_vfsmnt(cls, vfsmnt, dentry, context): + vmlinux = cls._get_vmlinux_from_volobj(dentry, context) + + # When it's called from _get_path_file(), 'vfsmnt' is a Pointer + # struct file->f_path->mnt is "struct vfsmount *". + # However, when called from _get_path_root() + # struct mount -> mnt is "struct vfsmount" + vfsmnt_ptr = vfsmnt if type(vfsmnt) == Pointer else vfsmnt.vol.offset + + mnt = cls.container_of(vfsmnt_ptr, "mount", "mnt", vmlinux) + + return mnt + + @classmethod + def do_get_path(cls, rdentry, rmnt, dentry, vfsmnt, context) -> Union[None, str]: + """It mimics the Linux kernel prepend_path function.""" + + mnt = cls._get_mnt_from_vfsmnt(vfsmnt, dentry, context) + + path_reversed = [] + while dentry != rdentry or vfsmnt.vol.offset != rmnt: + if dentry == vfsmnt.get_mnt_root() or dentry.is_root(): + parent = mnt.get_mnt_parent().dereference() + # Escaped? + if dentry != vfsmnt.get_mnt_root(): + break + + # Global root? + if mnt.vol.offset != parent.vol.offset: + dentry = mnt.get_mnt_mountpoint() + mnt = parent + vfsmnt = mnt.mnt + continue + + break + + parent = dentry.d_parent + dname = dentry.d_name.name_as_str() + path_reversed.append(dname.strip("/")) + dentry = parent + + path = "/" + "/".join(reversed(path_reversed)) + return path @classmethod def _get_new_sock_pipe_path(cls, context, task, filp) -> str: dentry = filp.get_dentry() + kernel_module = cls._get_vmlinux_from_volobj(dentry, context) + sym_addr = dentry.d_op.d_dname - - symbol_table_arr = sym_addr.vol.type_name.split("!") - symbol_table = None - if len(symbol_table_arr) == 2: - symbol_table = symbol_table_arr[0] - - for module_name in context.modules.get_modules_by_symbol_tables(symbol_table): - kernel_module = context.modules[module_name] - break - else: - raise ValueError(f"No module using the symbol table {symbol_table}") - symbs = list(kernel_module.get_symbols_by_absolute_location(sym_addr)) if len(symbs) == 1: @@ -151,7 +160,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): pre_name = "pipe" elif sym == "simple_dname": - pre_name = cls._get_path_file(task, filp) + pre_name = cls._get_path_file(context, task, filp) else: pre_name = f"" @@ -192,7 +201,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): if dname_is_valid: ret = LinuxUtilities._get_new_sock_pipe_path(context, task, filp) else: - ret = LinuxUtilities._get_path_file(task, filp) + ret = LinuxUtilities._get_path_file(context, task, filp) return ret From b2d33c2cb0cf8535647f77feac35c44bc124ace5 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Wed, 26 Apr 2023 00:36:58 +1000 Subject: [PATCH 102/140] Check if 'mnt_namespace' has the 'ns' member before using it --- volatility3/framework/symbols/linux/extensions/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 5ab8f1aa0..da5bf5dad 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -825,7 +825,7 @@ class mnt_namespace(objects.StructType): def get_inode(self): if self.has_member("proc_inum"): return self.proc_inum - elif self.ns.has_member("inum"): + elif self.has_member("ns") and self.ns.has_member("inum"): return self.ns.inum else: raise AttributeError("Unable to find mnt_namespace inode") From 317f565685c4d63b26d315cc77f74afcb65a525b Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sun, 30 Apr 2023 08:28:22 +0100 Subject: [PATCH 103/140] Core: Renable the import protection for volatility3.framework.plugins In commit 21d916b (about 10 months ago) the logic for the warning about volatility3.framework importing was disabled. This re-enables it. Also updates to support pyinstaller 5.10 function stack depth. Closes #944 --- volatility3/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/volatility3/__init__.py b/volatility3/__init__.py index 28df7da5a..94a6721e1 100644 --- a/volatility3/__init__.py +++ b/volatility3/__init__.py @@ -38,10 +38,13 @@ class WarningFindSpec(abc.MetaPathFinder): """Mock find_spec method that just checks the name, this must go first.""" if fullname.startswith("volatility3.framework.plugins."): - warning = "Please do not use the volatility3.framework.plugins namespace directly, only use volatility3.plugins" + warning = f"Import {fullname}: Please do not use the volatility3.framework.plugins namespace directly, only use volatility3.plugins" # Pyinstaller uses walk_packages/_collect_submodules to import, but needs to read the modules to figure out dependencies # As such, we only print the warning when directly imported rather than from within walk_packages/_collect_submodules - if inspect.stack()[-2].function in ["walk_packages", "_collect_submodules"]: + if inspect.stack()[-2].function not in [ + "walk_packages", + "_collect_submodules", + ] and inspect.stack()[-3].function not in ["_collect_submodules"]: raise Warning(warning) From 77081d3f6832d187eeff5fc0c2d7b64c2655a4b8 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Tue, 2 May 2023 05:51:24 +0900 Subject: [PATCH 104/140] Add: code comment for plugins --- volatility3/framework/plugins/windows/crashinfo.py | 2 ++ volatility3/framework/plugins/windows/ldrmodules.py | 2 ++ volatility3/plugins/windows/statistics.py | 2 ++ 3 files changed, 6 insertions(+) diff --git a/volatility3/framework/plugins/windows/crashinfo.py b/volatility3/framework/plugins/windows/crashinfo.py index a9f32f63d..4ecd85087 100644 --- a/volatility3/framework/plugins/windows/crashinfo.py +++ b/volatility3/framework/plugins/windows/crashinfo.py @@ -14,6 +14,8 @@ vollog = logging.getLogger(__name__) class Crashinfo(interfaces.plugins.PluginInterface): + """Lists the information from a Windows crash dump.""" + _required_framework_version = (2, 0, 0) @classmethod diff --git a/volatility3/framework/plugins/windows/ldrmodules.py b/volatility3/framework/plugins/windows/ldrmodules.py index 9642810a5..3c2b8a42d 100644 --- a/volatility3/framework/plugins/windows/ldrmodules.py +++ b/volatility3/framework/plugins/windows/ldrmodules.py @@ -7,6 +7,8 @@ from volatility3.plugins.windows import pslist, vadinfo class LdrModules(interfaces.plugins.PluginInterface): + """Lists the loaded modules in a particular windows memory image.""" + _required_framework_version = (2, 0, 0) _version = (1, 0, 0) diff --git a/volatility3/plugins/windows/statistics.py b/volatility3/plugins/windows/statistics.py index e921b3565..4cc05440c 100644 --- a/volatility3/plugins/windows/statistics.py +++ b/volatility3/plugins/windows/statistics.py @@ -13,6 +13,8 @@ vollog = logging.getLogger(__name__) class Statistics(plugins.PluginInterface): + """Lists statistics about the memory space.""""" + _required_framework_version = (2, 0, 0) @classmethod From 79cd3fe2feddf4f1041f2941eb34149c5083ab58 Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Tue, 2 May 2023 05:57:56 +0900 Subject: [PATCH 105/140] Lint: black issue for windows.statistics --- volatility3/plugins/windows/statistics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/plugins/windows/statistics.py b/volatility3/plugins/windows/statistics.py index 4cc05440c..9915312e3 100644 --- a/volatility3/plugins/windows/statistics.py +++ b/volatility3/plugins/windows/statistics.py @@ -13,7 +13,7 @@ vollog = logging.getLogger(__name__) class Statistics(plugins.PluginInterface): - """Lists statistics about the memory space.""""" + """Lists statistics about the memory space.""" _required_framework_version = (2, 0, 0) From c06836e45dfe56dfc5c5dc9977cdc638aed4785e Mon Sep 17 00:00:00 2001 From: Donghyun Kim Date: Tue, 2 May 2023 05:58:57 +0900 Subject: [PATCH 106/140] Lint: black issue for windows.ldrmodules --- volatility3/framework/plugins/windows/ldrmodules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/ldrmodules.py b/volatility3/framework/plugins/windows/ldrmodules.py index 3c2b8a42d..4c8456fa9 100644 --- a/volatility3/framework/plugins/windows/ldrmodules.py +++ b/volatility3/framework/plugins/windows/ldrmodules.py @@ -8,7 +8,7 @@ from volatility3.plugins.windows import pslist, vadinfo class LdrModules(interfaces.plugins.PluginInterface): """Lists the loaded modules in a particular windows memory image.""" - + _required_framework_version = (2, 0, 0) _version = (1, 0, 0) From 159e5a2fbd7393934f73a50b6512df9334357e27 Mon Sep 17 00:00:00 2001 From: cpuu Date: Thu, 4 May 2023 11:59:30 +0900 Subject: [PATCH 107/140] Fix requirements.URLRequirement to requirements.URIRequirement [Refactor] Fix requirements.URLRequirement to requirements.URIRequirement in single_location assignment --- volatility3/cli/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/cli/__init__.py b/volatility3/cli/__init__.py index 99052d82e..9bfd14c6c 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -353,7 +353,7 @@ class CommandLine: ### if args.file: try: - single_location = requirements.URLRequirement.location_from_file( + single_location = requirements.URIRequirement.location_from_file( args.file ) ctx.config["automagic.LayerStacker.single_location"] = single_location From 14f449367d68c38211c5cba095dee41d38c60f0a Mon Sep 17 00:00:00 2001 From: cpuu Date: Thu, 4 May 2023 12:52:00 +0900 Subject: [PATCH 108/140] Delete getting-started-macos-tutorial.rst --- doc/source/getting-started-macos-tutorial.rst | 154 ------------------ 1 file changed, 154 deletions(-) delete mode 100644 doc/source/getting-started-macos-tutorial.rst diff --git a/doc/source/getting-started-macos-tutorial.rst b/doc/source/getting-started-macos-tutorial.rst deleted file mode 100644 index bc0cb1b92..000000000 --- a/doc/source/getting-started-macos-tutorial.rst +++ /dev/null @@ -1,154 +0,0 @@ -macOS Tutorial -============== - -This guide will give you a brief overview of how volatility3 works as well as a demonstration of several of the plugins available in the suite. - -Acquiring memory ----------------- - -Volatility3 does not provide the ability to acquire memory. The example below is an open source tool. Other commercial tools are also available. - -* `osxpmem `_ - - - -Procedure to create symbol tables for macOS --------------------------------------------- - -To create a symbol table please refer to :ref:`symbol-tables:Mac or Linux symbol tables`. - -.. tip:: It may be possible to locate pre-made ISF files from the `download link `_ , - which is built and maintained by `volatilityfoundation `_. - After creating the file or downloading it from the link, place the file under the directory ``volatility3/symbols/mac``. - If necessary create a mac directory under the symbols directory (this will become unnecessary in future versions). - - -Listing plugins ---------------- - -The following is a sample of the macOS plugins available for volatility3, it is not complete and more more plugins may -be added. For a complete reference, please see the volatility 3 :doc:`list of plugins `. -For plugin requests, please create an issue with a description of the requested plugin. - -.. code-block:: shell-session - - $ python3 vol.py --help | grep -i mac. | head -n 5 - mac.bash.Bash Recovers bash command history from memory. - mac.check_syscall.Check_syscall - mac.check_sysctl.Check_sysctl - mac.check_trap_table.Check_trap_table - -.. note:: Here the the command is piped to grep and head in-order to provide the start of the list of macOS plugins. - - -Using plugins -------------- - -The following is the syntax to run the volatility CLI. - -.. code-block:: shell-session - - $ python3 vol.py -f - - -Example -------- - -banners -~~~~~~~ - -In this example we will be using a memory dump from the Securinets CTF Quals 2019 Challenge called Contact_me. We will limit the discussion to memory forensics with volatility 3 and not extend it to other parts of the challenge. -Thanks go to `stuxnet `_ for providing this memory dump and `writeup `_. - - -.. code-block:: shell-session - - $ python3 vol.py -f contact_me banners.Banners - - Volatility 3 Framework 2.1.0 - - Progress: 100.00 PDB scanning finished - Offset Banner - - 0x4d2c7d0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 - 0xb42b180 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 - 0xcda9100 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 - 0x1275e7d0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 - 0x1284fba4 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 - 0x34ad0180 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 - - -The above command helps us to find the memory dump's Darwin kernel version. Now using the above banner we can search for the needed ISF file. -If ISF file cannot be found then, follow the instructions on :ref:`getting-started-macos-tutorial:Procedure to create symbol tables for macOS`. After that, place the ISF file under the ``volatility3/symbols/mac`` directory. - -mac.pslist -~~~~~~~~~~~~ - -.. code-block:: shell-session - - $ python3 vol.py -f contact_me mac.pslist - - Volatility 3 Framework 2.1.0 Stacking attempts finished - - PID PPID COMM - - 0 0 kernel_task - 1 0 launchd - 35 1 UserEventAgent - 38 1 kextd - 39 1 fseventsd - 37 1 uninstalld - 45 1 configd - 46 1 powerd - 52 1 logd - 58 1 warmd - ..... - -``mac.pslist`` helps us to list the processes which are running, their PIDs and PPIDs. - -mac.pstree -~~~~~~~~~~~~ - -.. code-block:: shell-session - - $ python3 vol.py -f contact_me mac.pstree - Volatility 3 Framework 2.1.0 - Progress: 100.00 Stacking attempts finished - PID PPID COMM - - 35 1 UserEventAgent - 38 1 kextd - 39 1 fseventsd - 37 1 uninstalld - 204 1 softwareupdated - * 449 204 SoftwareUpdateCo - 337 1 system_installd - * 455 337 update_dyld_shar - -``mac.pstree`` helps us to display the parent child relationships between processes. - -mac.ifconfig -~~~~~~~~~~ - -we can use the ``mac.ifconfig`` plugin to get information about the configuration of the network interfaces of the host under investigation. - - -.. code-block:: shell-session - - $ python3 vol.py -f contact_me mac.ifconfig - - Volatility 3 Framework 2.1.0 - Progress: 100.00 Stacking attempts finished - Interface IP Address Mac Address Promiscuous - - lo0 False - lo0 127.0.0.1 False - lo0 ::1 False - lo0 fe80:1::1 False - gif0 False - stf0 False - en0 00:0C:29:89:8B:F0 00:0C:29:89:8B:F0 False - en0 fe80:4::10fb:c89d:217f:52ae 00:0C:29:89:8B:F0 False - en0 192.168.140.128 00:0C:29:89:8B:F0 False - utun0 False - utun0 fe80:5::2a95:bb15:87e3:977c False From c9e0b6695f2fe88eb2021df504c3ed5eece559e7 Mon Sep 17 00:00:00 2001 From: cpuu Date: Thu, 4 May 2023 12:52:16 +0900 Subject: [PATCH 109/140] Update index.rst --- doc/source/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/source/index.rst b/doc/source/index.rst index e096731c7..9b1d05858 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -26,7 +26,6 @@ There is also some information to get you started quickly: getting-started-linux-tutorial getting-started-windows-tutorial - getting-started-macos-tutorial .. toctree:: From 1ad8c950dab7005e7f603bbf1c34c962c27eb943 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Thu, 4 May 2023 13:47:57 +0300 Subject: [PATCH 110/140] Added modules name flag --- volatility3/framework/plugins/windows/modules.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/volatility3/framework/plugins/windows/modules.py b/volatility3/framework/plugins/windows/modules.py index eba6d1ce7..e25d0425e 100644 --- a/volatility3/framework/plugins/windows/modules.py +++ b/volatility3/framework/plugins/windows/modules.py @@ -42,6 +42,7 @@ class Modules(interfaces.plugins.PluginInterface): default=False, optional=True, ), + requirements.StringRequirement(name="name", description="module name/sub string", optional=True, default=""), ] def _generator(self): @@ -64,6 +65,9 @@ class Modules(interfaces.plugins.PluginInterface): except exceptions.InvalidAddressException: FullDllName = "" + if self.config['name'] and self.config['name'] not in BaseDllName: + continue + file_output = "Disabled" if self.config["dump"]: file_handle = dlllist.DllList.dump_pe( From 9249758dde5c7a1325f25b16ca49b0577d00d714 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Thu, 4 May 2023 15:15:23 +0300 Subject: [PATCH 111/140] formatting --- volatility3/framework/plugins/windows/modules.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/windows/modules.py b/volatility3/framework/plugins/windows/modules.py index e25d0425e..a1d480bb4 100644 --- a/volatility3/framework/plugins/windows/modules.py +++ b/volatility3/framework/plugins/windows/modules.py @@ -42,7 +42,12 @@ class Modules(interfaces.plugins.PluginInterface): default=False, optional=True, ), - requirements.StringRequirement(name="name", description="module name/sub string", optional=True, default=""), + requirements.StringRequirement( + name="name", + description="module name/sub string", + optional=True, + default="", + ), ] def _generator(self): @@ -66,7 +71,7 @@ class Modules(interfaces.plugins.PluginInterface): FullDllName = "" if self.config['name'] and self.config['name'] not in BaseDllName: - continue + continue file_output = "Disabled" if self.config["dump"]: From 8952c6c985c8cafd038ae4ed8db0ea4db6b04517 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Thu, 4 May 2023 15:18:37 +0300 Subject: [PATCH 112/140] formatting --- volatility3/framework/plugins/windows/modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/modules.py b/volatility3/framework/plugins/windows/modules.py index a1d480bb4..879da4a5f 100644 --- a/volatility3/framework/plugins/windows/modules.py +++ b/volatility3/framework/plugins/windows/modules.py @@ -70,7 +70,7 @@ class Modules(interfaces.plugins.PluginInterface): except exceptions.InvalidAddressException: FullDllName = "" - if self.config['name'] and self.config['name'] not in BaseDllName: + if self.config["name"] and self.config["name"] not in BaseDllName: continue file_output = "Disabled" From 093fc9ab797e2953ecdfb8cd17c096b3bf573a29 Mon Sep 17 00:00:00 2001 From: cpuu Date: Fri, 5 May 2023 23:39:33 +0900 Subject: [PATCH 113/140] Add tutorial for macOS Analysis Add tutorial for macOS Analysis --- doc/source/getting-started-mac-tutorial.rst | 155 ++++++++++++++++++++ doc/source/index.rst | 1 + 2 files changed, 156 insertions(+) create mode 100644 doc/source/getting-started-mac-tutorial.rst diff --git a/doc/source/getting-started-mac-tutorial.rst b/doc/source/getting-started-mac-tutorial.rst new file mode 100644 index 000000000..3e650fede --- /dev/null +++ b/doc/source/getting-started-mac-tutorial.rst @@ -0,0 +1,155 @@ +macOS Tutorial +============== + +This guide will give you a brief overview of how volatility3 works as well as a demonstration of several of the plugins available in the suite. + +Acquiring memory +---------------- + +Volatility3 does not provide the ability to acquire memory. The example below is an open source tool. Other commercial tools are also available. + +* `osxpmem `_ + + + +Procedure to create symbol tables for macOS +-------------------------------------------- + +To create a symbol table please refer to :ref:`symbol-tables:Mac or Linux symbol tables`. + +.. tip:: It may be possible to locate pre-made ISF files from the `download link `_ , + which is built and maintained by `volatilityfoundation `_. + After creating the file or downloading it from the link, place the file under the directory ``volatility3/symbols/mac``. + If necessary create a mac directory under the symbols directory (this will become unnecessary in future versions). + + +Listing plugins +--------------- + +The following is a sample of the macOS plugins available for volatility3, it is not complete and more more plugins may +be added. For a complete reference, please see the volatility 3 :doc:`list of plugins `. +For plugin requests, please create an issue with a description of the requested plugin. + +.. code-block:: shell-session + + $ python3 vol.py --help | grep -i mac. | head -n 4 + mac.bash.Bash Recovers bash command history from memory. + mac.check_syscall.Check_syscall + mac.check_sysctl.Check_sysctl + mac.check_trap_table.Check_trap_table + +.. note:: Here the the command is piped to grep and head in-order to provide the start of the list of macOS plugins. + + +Using plugins +------------- + +The following is the syntax to run the volatility CLI. + +.. code-block:: shell-session + + $ python3 vol.py -f + + +Example +------- + +banners +~~~~~~~ + +In this example we will be using a memory dump from the Securinets CTF Quals 2019 Challenge called Contact_me. We will limit the discussion to memory forensics with volatility 3 and not extend it to other parts of the challenge. +Thanks go to `stuxnet `_ for providing this memory dump and `writeup `_. + + +.. code-block:: shell-session + + $ python3 vol.py -f contact_me banners.Banners + + Volatility 3 Framework 2.4.2 + + Progress: 100.00 PDB scanning finished + Offset Banner + + 0x4d2c7d0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + 0xb42b180 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + 0xcda9100 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + 0x1275e7d0 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + 0x1284fba4 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + 0x34ad0180 Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64 + + +The above command helps us to find the memory dump's Darwin kernel version. Now using the above banner we can search for the needed ISF file. +If ISF file cannot be found then, follow the instructions on :ref:`getting-started-macos-tutorial:Procedure to create symbol tables for macOS`. After that, place the ISF file under the ``volatility3/symbols/mac`` directory. + +mac.pslist +~~~~~~~~~~~~ + +.. code-block:: shell-session + + $ python3 vol.py -f contact_me mac.pslist.PsList + + Volatility 3 Framework 2.4.2 + Progress: 100.00 Stacking attempts finished + + PID PPID COMM + + 0 0 kernel_task + 1 0 launchd + 35 1 UserEventAgent + 38 1 kextd + 39 1 fseventsd + 37 1 uninstalld + 45 1 configd + 46 1 powerd + 52 1 logd + 58 1 warmd + ..... + +``mac.pslist`` helps us to list the processes which are running, their PIDs and PPIDs. + +mac.pstree +~~~~~~~~~~~~ + +.. code-block:: shell-session + + $ python3 vol.py -f contact_me mac.pstree.PsTree + Volatility 3 Framework 2.4.2 + Progress: 100.00 Stacking attempts finished + PID PPID COMM + + 35 1 UserEventAgent + 38 1 kextd + 39 1 fseventsd + 37 1 uninstalld + 204 1 softwareupdated + * 449 204 SoftwareUpdateCo + 337 1 system_installd + * 455 337 update_dyld_shar + +``mac.pstree`` helps us to display the parent child relationships between processes. + +mac.ifconfig +~~~~~~~~~~ + +we can use the ``mac.ifconfig`` plugin to get information about the configuration of the network interfaces of the host under investigation. + + +.. code-block:: shell-session + + $ python3 vol.py -f contact_me mac.ifconfig.Ifconfig + + Volatility 3 Framework 2.4.2 + Progress: 100.00 Stacking attempts finished + Interface IP Address Mac Address Promiscuous + + lo0 False + lo0 127.0.0.1 False + lo0 ::1 False + lo0 fe80:1::1 False + gif0 False + stf0 False + en0 00:0C:29:89:8B:F0 00:0C:29:89:8B:F0 False + en0 fe80:4::10fb:c89d:217f:52ae 00:0C:29:89:8B:F0 False + en0 192.168.140.128 00:0C:29:89:8B:F0 False + utun0 False + utun0 fe80:5::2a95:bb15:87e3:977c False \ No newline at end of file diff --git a/doc/source/index.rst b/doc/source/index.rst index 9b1d05858..7f35e9bcb 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -25,6 +25,7 @@ There is also some information to get you started quickly: :caption: Getting Started getting-started-linux-tutorial + getting-started-mac-tutorial getting-started-windows-tutorial From 7063e6094481e940026b9654f4a52580aa6805cb Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Sat, 6 May 2023 14:12:09 +0200 Subject: [PATCH 114/140] mountinfo improvements: + Fixes issue with repeated path suffixes. + Adds support for older kernels (<3.3.8) and + Adds a function which simplify the way the framework internally can gets a context and a vmlinux. This is obtaining the context and symbol space from the same vol object instead of drag a context everywhere. This changes also affects other plugins such as elfs, malfind and proc.maps. It also adds doc strings to some of the existent functions. --- volatility3/framework/plugins/linux/elfs.py | 2 +- .../framework/plugins/linux/malfind.py | 2 +- .../framework/plugins/linux/mountinfo.py | 4 +- volatility3/framework/plugins/linux/proc.py | 2 +- .../framework/symbols/linux/__init__.py | 161 ++++++++----- .../symbols/linux/extensions/__init__.py | 216 ++++++++++++++++-- 6 files changed, 308 insertions(+), 79 deletions(-) diff --git a/volatility3/framework/plugins/linux/elfs.py b/volatility3/framework/plugins/linux/elfs.py index 822a69dd6..fa14dcd49 100644 --- a/volatility3/framework/plugins/linux/elfs.py +++ b/volatility3/framework/plugins/linux/elfs.py @@ -58,7 +58,7 @@ class Elfs(plugins.PluginInterface): ): continue - path = vma.get_name(self.context, task) + path = vma.get_name(task) yield ( 0, diff --git a/volatility3/framework/plugins/linux/malfind.py b/volatility3/framework/plugins/linux/malfind.py index 18237b80c..552fb8f53 100644 --- a/volatility3/framework/plugins/linux/malfind.py +++ b/volatility3/framework/plugins/linux/malfind.py @@ -47,7 +47,7 @@ class Malfind(interfaces.plugins.PluginInterface): proc_layer = self.context.layers[proc_layer_name] for vma in task.mm.get_mmap_iter(): - if vma.is_suspicious() and vma.get_name(self.context, task) != "[vdso]": + if vma.is_suspicious() and vma.get_name(task) != "[vdso]": data = proc_layer.read(vma.vm_start, 64, pad=True) yield vma, data diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index 1de776412..dfb2e23b4 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -74,7 +74,7 @@ class MountInfo(plugins.PluginInterface): @classmethod def get_mountinfo( - cls, mnt, task, context + cls, mnt, task ) -> Union[ None, Tuple[int, int, str, str, str, List[str], List[str], str, str, List[str]] ]: @@ -85,7 +85,7 @@ class MountInfo(plugins.PluginInterface): if not mnt_root: return None - path_root = linux.LinuxUtilities._get_path_root(context, mnt, task.fs.root) + path_root = linux.LinuxUtilities._get_path_mnt(task, mnt) if not path_root: return None diff --git a/volatility3/framework/plugins/linux/proc.py b/volatility3/framework/plugins/linux/proc.py index 9d8af482e..fa7bc1629 100644 --- a/volatility3/framework/plugins/linux/proc.py +++ b/volatility3/framework/plugins/linux/proc.py @@ -59,7 +59,7 @@ class Maps(plugins.PluginInterface): minor = inode_object.i_sb.minor inode = inode_object.i_ino - path = vma.get_name(self.context, task) + path = vma.get_name(task) yield ( 0, diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 80f5e9990..486314dd5 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -5,7 +5,7 @@ from typing import Iterator, List, Tuple, Optional, Union from volatility3 import framework from volatility3.framework import constants, exceptions, interfaces, objects -from volatility3.framework.objects import utility, Pointer +from volatility3.framework.objects import utility from volatility3.framework.symbols import intermed from volatility3.framework.symbols.linux import extensions @@ -60,75 +60,74 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): framework.require_interface_version(*_required_framework_version) @classmethod - def _get_path_file(cls, context, task, filp) -> str: + def _get_path_file(cls, task, filp) -> str: + """Returns the file pathname relative to the task's root directory. + + Args: + task (task_struct): A reference task + filp (file *): A pointer to an open file + + Returns: + str: File pathname relative to the task's root directory. + """ rdentry = task.fs.get_root_dentry() rmnt = task.fs.get_root_mnt() vfsmnt = filp.get_vfsmnt() dentry = filp.get_dentry() - return cls.do_get_path(rdentry, rmnt, dentry, vfsmnt, context) + return cls.do_get_path(rdentry, rmnt, dentry, vfsmnt) @classmethod - def _get_path_root(cls, context, mnt, fs_root) -> str: - rdentry = fs_root.dentry - rmnt = fs_root.mnt - vfsmnt = mnt.mnt - dentry = vfsmnt.mnt_root + def _get_path_mnt(cls, task, mnt) -> str: + """Returns the mount point pathname relative to the task's root directory. - return cls.do_get_path(rdentry, rmnt, dentry, vfsmnt, context) + Args: + task (task_struct): A reference task + mnt (vfsmount or mount): A mounted filesystem or a mount point. + - kernels < 3.3.8 type is 'vfsmount' + - kernels >= 3.3.8 type is 'mount' + + Returns: + str: Pathname of the mount point relative to the task's root directory. + """ + rdentry = task.fs.get_root_dentry() + rmnt = task.fs.get_root_mnt() + + vfsmnt = mnt.get_vfsmnt_current() + dentry = mnt.get_dentry_current() + + return cls.do_get_path(rdentry, rmnt, dentry, vfsmnt) @classmethod - def _get_vmlinux_from_volobj(cls, volobj, context): - symbol_table_arr = volobj.vol.type_name.split("!", 1) - symbol_table = symbol_table_arr[0] if len(symbol_table_arr) == 2 else None + def do_get_path(cls, rdentry, rmnt, dentry, vfsmnt) -> Union[None, str]: + """Returns a pathname of the mount point or file + It mimics the Linux kernel prepend_path function. - module_names = context.modules.get_modules_by_symbol_tables(symbol_table) - module_names = list(module_names) + Args: + rdentry (dentry *): A pointer to the root dentry + rmnt (vfsmount *): A pointer to the root vfsmount + dentry (dentry *): A pointer to the dentry + vfsmnt (vfsmount *): A pointer to the vfsmount - if not module_names: - raise ValueError(f"No module using the symbol table '{symbol_table}'") - - kernel_module_name = module_names[0] - kernel = context.modules[kernel_module_name] - - return kernel - - @classmethod - def _get_mnt_from_vfsmnt(cls, vfsmnt, dentry, context): - vmlinux = cls._get_vmlinux_from_volobj(dentry, context) - - # When it's called from _get_path_file(), 'vfsmnt' is a Pointer - # struct file->f_path->mnt is "struct vfsmount *". - # However, when called from _get_path_root() - # struct mount -> mnt is "struct vfsmount" - vfsmnt_ptr = vfsmnt if type(vfsmnt) == Pointer else vfsmnt.vol.offset - - mnt = cls.container_of(vfsmnt_ptr, "mount", "mnt", vmlinux) - - return mnt - - @classmethod - def do_get_path(cls, rdentry, rmnt, dentry, vfsmnt, context) -> Union[None, str]: - """It mimics the Linux kernel prepend_path function.""" - - mnt = cls._get_mnt_from_vfsmnt(vfsmnt, dentry, context) + Returns: + str: Pathname of the mount point or file + """ path_reversed = [] - while dentry != rdentry or vfsmnt.vol.offset != rmnt: + while dentry != rdentry or not vfsmnt.is_equal(rmnt): if dentry == vfsmnt.get_mnt_root() or dentry.is_root(): - parent = mnt.get_mnt_parent().dereference() # Escaped? if dentry != vfsmnt.get_mnt_root(): break # Global root? - if mnt.vol.offset != parent.vol.offset: - dentry = mnt.get_mnt_mountpoint() - mnt = parent - vfsmnt = mnt.mnt - continue + if not vfsmnt.has_parent(): + break - break + dentry = vfsmnt.get_dentry_parent() + vfsmnt = vfsmnt.get_vfsmnt_parent() + + continue parent = dentry.d_parent dname = dentry.d_name.name_as_str() @@ -139,10 +138,19 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): return path @classmethod - def _get_new_sock_pipe_path(cls, context, task, filp) -> str: + def _get_new_sock_pipe_path(cls, task, filp) -> str: + """Returns the sock pipe pathname relative to the task's root directory. + + Args: + task (task_struct): A reference task + filp (file *): A pointer to a sock pipe open file + + Returns: + str: Sock pipe pathname relative to the task's root directory. + """ dentry = filp.get_dentry() - kernel_module = cls._get_vmlinux_from_volobj(dentry, context) + kernel_module = cls.get_vmlinux_from_volobj(dentry) sym_addr = dentry.d_op.d_dname symbs = list(kernel_module.get_symbols_by_absolute_location(sym_addr)) @@ -160,7 +168,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): pre_name = "pipe" elif sym == "simple_dname": - pre_name = cls._get_path_file(context, task, filp) + pre_name = cls._get_path_file(task, filp) else: pre_name = f"" @@ -172,10 +180,20 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): return ret - # a 'file' structure doesn't have enough information to properly restore its full path - # we need the root mount information from task_struct to determine this @classmethod - def path_for_file(cls, context, task, filp) -> str: + def path_for_file(cls, task, filp) -> str: + """Returns a file (or sock pipe) pathname relative to the task's root directory. + + A 'file' structure doesn't have enough information to properly restore its + full path we need the root mount information from task_struct to determine this + + Args: + task (task_struct): A reference task + filp (file *): A pointer to an open file + + Returns: + str: A file (or sock pipe) pathname relative to the task's root directory. + """ try: dentry = filp.get_dentry() except exceptions.InvalidAddressException: @@ -199,9 +217,9 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): dname_is_valid = False if dname_is_valid: - ret = LinuxUtilities._get_new_sock_pipe_path(context, task, filp) + ret = LinuxUtilities._get_new_sock_pipe_path(task, filp) else: - ret = LinuxUtilities._get_path_file(context, task, filp) + ret = LinuxUtilities._get_path_file(task, filp) return ret @@ -234,7 +252,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): for fd_num, filp in enumerate(fds): if filp != 0: - full_path = LinuxUtilities.path_for_file(context, task, filp) + full_path = LinuxUtilities.path_for_file(task, filp) yield fd_num, filp, full_path @@ -357,3 +375,30 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): return vmlinux.object( object_type=type_name, offset=container_addr, absolute=True ) + + @classmethod + def get_vmlinux_from_volobj(cls, volobj): + """Get the vmlinux from a vol obj + + Args: + volobj (vol object): A vol object + + Raises: + ValueError: If it cannot obtain any module from the symbol table + + Returns: + volatility3.framework.contexts.Module: A kernel object (vmlinux) + """ + symbol_table_arr = volobj.vol.type_name.split("!", 1) + symbol_table = symbol_table_arr[0] if len(symbol_table_arr) == 2 else None + + module_names = volobj._context.modules.get_modules_by_symbol_tables(symbol_table) + module_names = list(module_names) + + if not module_names: + raise ValueError(f"No module using the symbol table '{symbol_table}'") + + kernel_module_name = module_names[0] + kernel = volobj._context.modules[kernel_module_name] + + return kernel diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index da5bf5dad..b2225f764 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -435,9 +435,9 @@ class vm_area_struct(objects.StructType): return self.vm_pgoff << constants.linux.PAGE_SHIFT - def get_name(self, context, task): + def get_name(self, task): if self.vm_file != 0: - fname = linux.LinuxUtilities.path_for_file(context, task, self.vm_file) + fname = linux.LinuxUtilities.path_for_file(task, self.vm_file) elif self.vm_start <= task.mm.start_brk and self.vm_end >= task.mm.brk: fname = "[heap]" elif self.vm_start <= task.mm.start_stack <= self.vm_end: @@ -544,6 +544,7 @@ class struct_file(objects.StructType): raise AttributeError("Unable to find file -> dentry") def get_vfsmnt(self) -> interfaces.objects.ObjectInterface: + """Returns the fs (vfsmount) where this file is mounted""" if self.has_member("f_vfsmnt"): return self.f_vfsmnt elif self.has_member("f_path"): @@ -675,11 +676,70 @@ class mount(objects.StructType): raise AttributeError("Unable to find mount -> mount flags") def get_mnt_parent(self): + """Gets the fs where we are mounted on + + Returns: + A 'mount *' + """ return self.mnt_parent def get_mnt_mountpoint(self): + """Gets the dentry of the mountpoint + + Returns: + A 'dentry *' + """ + return self.mnt_mountpoint + def get_parent_mount(self): + return self.mnt.get_parent_mount() + + def has_parent(self) -> bool: + """Checks if this mount has a parent + + Returns: + bool: 'True' if this mount has a parent + """ + return self.mnt_parent != self.vol.offset + + def get_vfsmnt_current(self): + """Returns the fs where we are mounted on + + Returns: + A 'vfsmount' + """ + return self.mnt + + def get_vfsmnt_parent(self): + """Gets the parent fs (vfsmount) to where it's mounted on + + Returns: + A 'vfsmount' + """ + + return self.get_mnt_parent().get_vfsmnt_current() + + def get_dentry_current(self): + """Returns the root of the mounted tree + + Returns: + A 'dentry *' + """ + vfsmnt = self.get_vfsmnt_current() + dentry = vfsmnt.mnt_root + + return dentry + + def get_dentry_parent(self): + """Returns the parent root of the mounted tree + + Returns: + A 'dentry *' + """ + + return self.get_mnt_parent().get_dentry_current() + def get_flags_access(self) -> str: return "ro" if self.get_mnt_flags() & self.MNT_READONLY else "rw" @@ -703,9 +763,6 @@ class mount(objects.StructType): def get_devname(self) -> str: return utility.pointer_to_string(self.mnt_devname, count=255) - def has_parent(self) -> bool: - return self.vol.offset != self.mnt_parent - def get_dominating_id(self, root) -> int: """Get ID of closest dominating peer group having a representative under the given root.""" mnt_seen = set() @@ -783,24 +840,117 @@ class vfsmount(objects.StructType): and self.get_mnt_parent() != 0 ) - def _get_real_mnt(self): - table_name = self.vol.type_name.split(constants.BANG)[0] - mount_struct = f"{table_name}{constants.BANG}mount" - offset = self._context.symbol_space.get_type( - mount_struct - ).relative_child_offset("mnt") + def _is_kernel_prior_to_struct_mount(self) -> bool: + """Helper to distinguish between kernels prior to version 3.3.8 that + lacked the 'mount' structure and later versions that have it. - return self._context.object( - mount_struct, self.vol.layer_name, offset=self.vol.offset - offset - ) + The 'mnt_parent' member was moved from struct 'vfsmount' to struct + 'mount' when the latter was introduced. + + Alternatively, vmlinux.has_type('mount') can be used here but it is faster. + + Returns: + bool: 'True' if the kernel + """ + + return self.has_member("mnt_parent") + + def is_equal(self, vfsmount_ptr) -> bool: + """Helper to make sure it is comparing two pointers to 'vfsmount'. + + Depending on the kernel version, the calling object (self) could be + a 'vfsmount *' (<3.3.8) or a 'vfsmount' (>=3.3.8). This way we trust + in the framework "auto" dereferencing ability to assure that when we + reach this point 'self' will be a 'vfsmount' already and self.vol.offset + a 'vfsmount *' and not a 'vfsmount **'. The argument must be a 'vfsmount *'. + Typically, it's called from do_get_path(). + + Args: + vfsmount_ptr (vfsmount *): A pointer to a 'vfsmount' + + Raises: + exceptions.VolatilityException: If vfsmount_ptr is not a 'vfsmount *' + + Returns: + bool: 'True' if the given argument points to the the same 'vfsmount' + as 'self'. + """ + if type(vfsmount_ptr) == objects.Pointer: + return self.vol.offset == vfsmount_ptr + else: + raise exceptions.VolatilityException("Unexpected argument type. It has to be a 'vfsmount *'") + + def _get_real_mnt(self): + """Gets the struct 'mount' containing this 'vfsmount'. + + It should be only called from kernels >= 3.3.8 when 'struct mount' was introduced. + + Returns: + mount: the struct 'mount' containing this 'vfsmount'. + """ + vmlinux = linux.LinuxUtilities.get_vmlinux_from_volobj(self) + return linux.LinuxUtilities.container_of(self.vol.offset, "mount", "mnt", vmlinux) + + def get_vfsmnt_current(self): + """Returns the current fs where we are mounted on + + Returns: + A 'vfsmount *' + """ + return self.get_mnt_parent() + + def get_vfsmnt_parent(self): + """Gets the parent fs (vfsmount) to where it's mounted on + + Returns: + For kernels < 3.3.8: A 'vfsmount *' + For kernels >= 3.3.8: A 'vfsmount' + """ + if self._is_kernel_prior_to_struct_mount(): + return self.get_mnt_parent() + else: + return self._get_real_mnt().get_vfsmnt_parent() + + def get_dentry_current(self): + """Returns the root of the mounted tree + + Returns: + A 'dentry *' + """ + if self._is_kernel_prior_to_struct_mount(): + return self.get_mnt_mountpoint() + else: + return self._get_real_mnt().get_dentry_current() + + def get_dentry_parent(self): + """Returns the parent root of the mounted tree + + Returns: + A 'dentry *' + """ + if self._is_kernel_prior_to_struct_mount(): + return self.get_mnt_mountpoint() + else: + return self._get_real_mnt().get_mnt_mountpoint() def get_mnt_parent(self): - if self.has_member("mnt_parent"): + """Gets the mnt_parent member. + + Returns: + For kernels < 3.3.8: A 'vfsmount *' + For kernels >= 3.3.8: A 'mount *' + """ + if self._is_kernel_prior_to_struct_mount(): return self.mnt_parent else: - return self._get_real_mnt().mnt_parent + return self._get_real_mnt().get_mnt_parent() def get_mnt_mountpoint(self): + """Gets the dentry of the mountpoint + + Returns: + A 'dentry *' + """ if self.has_member("mnt_mountpoint"): return self.mnt_mountpoint else: @@ -809,6 +959,40 @@ class vfsmount(objects.StructType): def get_mnt_root(self): return self.mnt_root + def has_parent(self) -> bool: + if self._is_kernel_prior_to_struct_mount(): + return self.mnt_parent != self.vol.offset + else: + return self._get_real_mnt().has_parent() + + def get_mnt_sb(self): + return self.mnt_sb + + def get_flags_access(self) -> str: + return "ro" if self.mnt_flags & mount.MNT_READONLY else "rw" + + def get_flags_opts(self) -> Iterable[str]: + flags = [ + mntflagtxt + for mntflag, mntflagtxt in mount.MNT_FLAGS.items() + if mntflag & self.mnt_flags != 0 + ] + return flags + + def get_mnt_flags(self): + return self.mnt_flags + + def is_shared(self) -> bool: + return self.get_mnt_flags() & mount.MNT_SHARED + + def is_unbindable(self) -> bool: + return self.get_mnt_flags() & mount.MNT_UNBINDABLE + + def is_slave(self) -> bool: + return self.mnt_master and self.mnt_master.vol.offset != 0 + + def get_devname(self) -> str: + return utility.pointer_to_string(self.mnt_devname, count=255) class kobject(objects.StructType): def reference_count(self): From 2d922a8cd645c318e4e10c5d286bc86589f64f8b Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Sat, 6 May 2023 14:18:33 +0200 Subject: [PATCH 115/140] Improves the way the mount points and the namespaces are filtered. Previously, it was filtering by mount namespaces id. Even that approach was working as expected, it's not viable for older kernels were there was not a mount namespace ID. With this changes we filtered the mount points individually by the mount ID which is unique system wide, no mather the namespace to which it belongs to. --- .../framework/plugins/linux/mountinfo.py | 70 ++++++++++++------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index dfb2e23b4..e03659aec 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -12,6 +12,7 @@ from volatility3.framework.interfaces import plugins from volatility3.framework.symbols import linux from volatility3.plugins.linux import pslist + vollog = logging.getLogger(__name__) MountInfoData = namedtuple( @@ -140,30 +141,38 @@ class MountInfo(plugins.PluginInterface): ) def _get_tasks_mountpoints( - self, tasks: Iterable[interfaces.objects.ObjectInterface], per_namespace: bool + self, tasks: Iterable[interfaces.objects.ObjectInterface], filtered_by_pids: bool ): - seen_namespaces = set() + seen_mountpoints = set() for task in tasks: if not ( - task - and task.fs - and task.fs.root - and task.nsproxy - and task.nsproxy.mnt_ns + task and + task.fs and + task.fs.root and + task.nsproxy and + task.nsproxy.mnt_ns ): - # This task doesn't have all the information required + # This task doesn't have all the information required. + # It should be a kernel < 2.6.30 continue mnt_namespace = task.nsproxy.mnt_ns - mnt_ns_id = mnt_namespace.get_inode() - - if per_namespace: - if mnt_ns_id in seen_namespaces: - continue - else: - seen_namespaces.add(mnt_ns_id) + try: + mnt_ns_id = str(mnt_namespace.get_inode()) + except AttributeError: + mnt_ns_id = renderers.NotAvailableValue() for mount in mnt_namespace.get_mount_points(): + # When PIDs are filtered, it makes sense that the user want to + # see each of those processes mount points. So we don't filter + # by mount id in this case. + if not filtered_by_pids: + mnt_id = int(mount.mnt_id) + if mnt_id in seen_mountpoints: + continue + else: + seen_mountpoints.add(mnt_id) + yield task, mount, mnt_ns_id def _generator( @@ -171,13 +180,26 @@ class MountInfo(plugins.PluginInterface): tasks: Iterable[interfaces.objects.ObjectInterface], mnt_ns_ids: List[int], mount_format: bool, - per_namespace: bool, + filtered_by_pids: bool, ) -> Iterable[Tuple[int, Tuple]]: - for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(tasks, per_namespace): - if mnt_ns_ids and mnt_ns_id not in mnt_ns_ids: + warning_shown = False + for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(tasks, filtered_by_pids): + if ( + not warning_shown and + mnt_ns_ids and + isinstance(mnt_ns_id, renderers.NotAvailableValue) + ): + vollog.warning("Cannot filter by namespace id, it is not available in this kernel.") + warning_shown = True + + if ( + not isinstance(mnt_ns_id, renderers.NotAvailableValue) and + mnt_ns_ids and + mnt_ns_id not in mnt_ns_ids + ): continue - mnt_info = MountInfo.get_mountinfo(mnt, task, self.context) + mnt_info = self.get_mountinfo(mnt, task) if mnt_info is None: continue @@ -212,7 +234,7 @@ class MountInfo(plugins.PluginInterface): ] fields_values = [mnt_ns_id] - if not per_namespace: + if filtered_by_pids: fields_values.append(task.pid) fields_values.extend(extra_fields_values) @@ -228,14 +250,14 @@ class MountInfo(plugins.PluginInterface): self.context, self.config["kernel"], filter_func=pid_filter ) - columns = [("MNT_NS_ID", int)] + columns = [("MNT_NS_ID", str)] # The PID column does not make sense when a PID filter is not specified. In that case, the default behavior is # to displays the mountpoints per namespace. if pids: columns.append(("PID", int)) - per_namespace = False + filtered_by_pids = True else: - per_namespace = True + filtered_by_pids = False if self.config.get("mount-format"): extra_columns = [ @@ -262,5 +284,5 @@ class MountInfo(plugins.PluginInterface): columns.extend(extra_columns) return renderers.TreeGrid( - columns, self._generator(tasks, mount_ns_ids, mount_format, per_namespace) + columns, self._generator(tasks, mount_ns_ids, mount_format, filtered_by_pids) ) From a09f77897b24b4b0da06a8b45a353ee730cadd08 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Sat, 6 May 2023 14:21:58 +0200 Subject: [PATCH 116/140] sockstat improvements: + Fixes issues with netlink sockets for older kernels, supporting now kernels < 3.7.10 + Fixes issue with network namespace id for older kernels. --- .../framework/plugins/linux/sockstat.py | 26 +++++++++++++++---- .../symbols/linux/extensions/__init__.py | 24 ++++++++++++++++- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index f03a2ad8e..e37b8a1bb 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -28,7 +28,11 @@ class SockHandlers(interfaces.configuration.VersionableInterface): self._vmlinux = vmlinux self._task = task - netns_id = task.nsproxy.net_ns.get_inode() + try: + netns_id = task.nsproxy.net_ns.get_inode() + except AttributeError: + netns_id = NotAvailableValue() + self._netdevices = self._build_network_devices_map(netns_id) self._sock_family_handlers = { @@ -61,7 +65,7 @@ class SockHandlers(interfaces.configuration.VersionableInterface): self._vmlinux.symbol_table_name + constants.BANG + "net_device" ) for net_dev in net.dev_base_head.to_list(net_device_symname, "dev_list"): - if net.get_inode() != netns_id: + if isinstance(netns_id, NotAvailableValue) or net.get_inode() != netns_id: continue dev_name = utility.array_to_string(net_dev.name) netdevices_map[net_dev.ifindex] = dev_name @@ -227,14 +231,22 @@ class SockHandlers(interfaces.configuration.VersionableInterface): if netlink_sock.groups: groups_bitmap = netlink_sock.groups.dereference() src_addr = f"groups:0x{groups_bitmap:08x}" - src_port = netlink_sock.portid + + try: + # Kernel >= 3.7.10 + src_port = netlink_sock.get_portid() + except AttributeError: + src_port = NotAvailableValue() dst_addr = f"group:0x{netlink_sock.dst_group:08x}" module = netlink_sock.module if module and module.name: module_name_str = utility.array_to_string(module.name) dst_addr = f"{dst_addr},lkm:{module_name_str}" - dst_port = netlink_sock.dst_portid + try: + dst_port = netlink_sock.get_dst_portid() + except AttributeError: + dst_port = NotAvailableValue() state = netlink_sock.get_state() @@ -518,7 +530,11 @@ class Sockstat(plugins.PluginInterface): protocol = child_sock.get_protocol() net = task.nsproxy.net_ns - netns_id = net.get_inode() + try: + netns_id = net.get_inode() + except AttributeError: + netns_id = NotAvailableValue() + yield task, netns_id, fd_num, family, sock_type, protocol, sock_fields def _format_fields(self, sock_stat, protocol): diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index b2225f764..d97916b74 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -1028,10 +1028,13 @@ class mnt_namespace(objects.StructType): class net(objects.StructType): def get_inode(self): if self.has_member("proc_inum"): + # 3.8.13 <= kernel < 3.19.8 return self.proc_inum - elif self.ns.has_member("inum"): + elif self.has_member("ns") and self.ns.has_member("inum"): + # kernel >= 3.19.8 return self.ns.inum else: + # kernel < 3.8.13 raise AttributeError("Unable to find net_namespace inode") @@ -1239,6 +1242,25 @@ class netlink_sock(objects.StructType): # Return the generic socket state return self.sk.sk_socket.get_state() + def get_portid(self): + if self.has_member("pid"): + # kernel < 3.7.10 + return self.pid + if self.has_member("portid"): + # kernel >= 3.7.10 + return self.portid + else: + raise AttributeError("Unable to find a source port id") + + def get_dst_portid(self): + if self.has_member("dst_pid"): + # kernel < 3.7.10 + return self.dst_pid + if self.has_member("dst_portid"): + # kernel >= 3.7.10 + return self.dst_portid + else: + raise AttributeError("Unable to find a destination port id") class vsock_sock(objects.StructType): def get_protocol(self): From c40b3a043f2c91506514c9fa7cdc7d3bcce81116 Mon Sep 17 00:00:00 2001 From: Paul Kermann Date: Mon, 8 May 2023 05:07:19 +0300 Subject: [PATCH 117/140] CR fixes --- volatility3/framework/plugins/windows/modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/modules.py b/volatility3/framework/plugins/windows/modules.py index 879da4a5f..c1be1b0a6 100644 --- a/volatility3/framework/plugins/windows/modules.py +++ b/volatility3/framework/plugins/windows/modules.py @@ -46,7 +46,7 @@ class Modules(interfaces.plugins.PluginInterface): name="name", description="module name/sub string", optional=True, - default="", + default=None, ), ] From ea09c4732843f27830d2afe859203d825fa15b76 Mon Sep 17 00:00:00 2001 From: cpuu Date: Mon, 8 May 2023 17:32:14 +0900 Subject: [PATCH 118/140] Update getting-started-mac-tutorial.rst --- doc/source/getting-started-mac-tutorial.rst | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/doc/source/getting-started-mac-tutorial.rst b/doc/source/getting-started-mac-tutorial.rst index 3e650fede..cb0dda845 100644 --- a/doc/source/getting-started-mac-tutorial.rst +++ b/doc/source/getting-started-mac-tutorial.rst @@ -17,16 +17,11 @@ Procedure to create symbol tables for macOS To create a symbol table please refer to :ref:`symbol-tables:Mac or Linux symbol tables`. -.. tip:: It may be possible to locate pre-made ISF files from the `download link `_ , - which is built and maintained by `volatilityfoundation `_. - After creating the file or downloading it from the link, place the file under the directory ``volatility3/symbols/mac``. - If necessary create a mac directory under the symbols directory (this will become unnecessary in future versions). - Listing plugins --------------- -The following is a sample of the macOS plugins available for volatility3, it is not complete and more more plugins may +The following is a sample of the macOS plugins available for volatility3, it is not complete and more plugins may be added. For a complete reference, please see the volatility 3 :doc:`list of plugins `. For plugin requests, please create an issue with a description of the requested plugin. @@ -79,7 +74,7 @@ Thanks go to `stuxnet `_ for providing this memo The above command helps us to find the memory dump's Darwin kernel version. Now using the above banner we can search for the needed ISF file. -If ISF file cannot be found then, follow the instructions on :ref:`getting-started-macos-tutorial:Procedure to create symbol tables for macOS`. After that, place the ISF file under the ``volatility3/symbols/mac`` directory. +If ISF file cannot be found then, follow the instructions on :ref:`getting-started-macos-tutorial:Procedure to create symbol tables for macOS`. After that, place the ISF file under the ``volatility3/symbols`` directory. mac.pslist ~~~~~~~~~~~~ @@ -131,9 +126,6 @@ mac.pstree mac.ifconfig ~~~~~~~~~~ -we can use the ``mac.ifconfig`` plugin to get information about the configuration of the network interfaces of the host under investigation. - - .. code-block:: shell-session $ python3 vol.py -f contact_me mac.ifconfig.Ifconfig @@ -152,4 +144,6 @@ we can use the ``mac.ifconfig`` plugin to get information about the configuratio en0 fe80:4::10fb:c89d:217f:52ae 00:0C:29:89:8B:F0 False en0 192.168.140.128 00:0C:29:89:8B:F0 False utun0 False - utun0 fe80:5::2a95:bb15:87e3:977c False \ No newline at end of file + utun0 fe80:5::2a95:bb15:87e3:977c False + + we can use the ``mac.ifconfig`` plugin to get information about the configuration of the network interfaces of the host under investigation. From e86ff963e2325d33bf2fff612e9f494afbea836b Mon Sep 17 00:00:00 2001 From: cpuu Date: Mon, 8 May 2023 17:43:24 +0900 Subject: [PATCH 119/140] Update getting-started-mac-tutorial.rst --- doc/source/getting-started-mac-tutorial.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/source/getting-started-mac-tutorial.rst b/doc/source/getting-started-mac-tutorial.rst index cb0dda845..d70d260ca 100644 --- a/doc/source/getting-started-mac-tutorial.rst +++ b/doc/source/getting-started-mac-tutorial.rst @@ -17,6 +17,10 @@ Procedure to create symbol tables for macOS To create a symbol table please refer to :ref:`symbol-tables:Mac or Linux symbol tables`. +.. tip:: It may be possible to locate pre-made ISF files from the `download link `_ , + which is built and maintained by `volatilityfoundation `_. + After creating the file or downloading it from the link, place the file under the directory ``volatility3/symbols/``. + Listing plugins --------------- From 6b2ae6bd653b384d7e5001272878450370da74d0 Mon Sep 17 00:00:00 2001 From: cpuu Date: Mon, 8 May 2023 17:44:17 +0900 Subject: [PATCH 120/140] Update getting-started-mac-tutorial.rst --- doc/source/getting-started-mac-tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/getting-started-mac-tutorial.rst b/doc/source/getting-started-mac-tutorial.rst index d70d260ca..cfb0afa9a 100644 --- a/doc/source/getting-started-mac-tutorial.rst +++ b/doc/source/getting-started-mac-tutorial.rst @@ -150,4 +150,4 @@ mac.ifconfig utun0 False utun0 fe80:5::2a95:bb15:87e3:977c False - we can use the ``mac.ifconfig`` plugin to get information about the configuration of the network interfaces of the host under investigation. +we can use the ``mac.ifconfig`` plugin to get information about the configuration of the network interfaces of the host under investigation. From ced6ff346cd8694ed02f835da8bec62f60ff9b56 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Mon, 8 May 2023 11:54:48 +0200 Subject: [PATCH 121/140] Apply 'Black' suggestions --- .../framework/plugins/linux/mountinfo.py | 37 +++++++++++-------- .../framework/plugins/linux/sockstat.py | 5 ++- .../framework/symbols/linux/__init__.py | 4 +- .../symbols/linux/extensions/__init__.py | 10 ++++- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index e03659aec..0606884ff 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -141,16 +141,18 @@ class MountInfo(plugins.PluginInterface): ) def _get_tasks_mountpoints( - self, tasks: Iterable[interfaces.objects.ObjectInterface], filtered_by_pids: bool + self, + tasks: Iterable[interfaces.objects.ObjectInterface], + filtered_by_pids: bool, ): seen_mountpoints = set() for task in tasks: if not ( - task and - task.fs and - task.fs.root and - task.nsproxy and - task.nsproxy.mnt_ns + task + and task.fs + and task.fs.root + and task.nsproxy + and task.nsproxy.mnt_ns ): # This task doesn't have all the information required. # It should be a kernel < 2.6.30 @@ -183,19 +185,23 @@ class MountInfo(plugins.PluginInterface): filtered_by_pids: bool, ) -> Iterable[Tuple[int, Tuple]]: warning_shown = False - for task, mnt, mnt_ns_id in self._get_tasks_mountpoints(tasks, filtered_by_pids): + for task, mnt, mnt_ns_id in self._get_tasks_mountpoints( + tasks, filtered_by_pids + ): if ( - not warning_shown and - mnt_ns_ids and - isinstance(mnt_ns_id, renderers.NotAvailableValue) + not warning_shown + and mnt_ns_ids + and isinstance(mnt_ns_id, renderers.NotAvailableValue) ): - vollog.warning("Cannot filter by namespace id, it is not available in this kernel.") + vollog.warning( + "Cannot filter by namespace id, it is not available in this kernel." + ) warning_shown = True if ( - not isinstance(mnt_ns_id, renderers.NotAvailableValue) and - mnt_ns_ids and - mnt_ns_id not in mnt_ns_ids + not isinstance(mnt_ns_id, renderers.NotAvailableValue) + and mnt_ns_ids + and mnt_ns_id not in mnt_ns_ids ): continue @@ -284,5 +290,6 @@ class MountInfo(plugins.PluginInterface): columns.extend(extra_columns) return renderers.TreeGrid( - columns, self._generator(tasks, mount_ns_ids, mount_format, filtered_by_pids) + columns, + self._generator(tasks, mount_ns_ids, mount_format, filtered_by_pids), ) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index e37b8a1bb..72a1e453e 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -65,7 +65,10 @@ class SockHandlers(interfaces.configuration.VersionableInterface): self._vmlinux.symbol_table_name + constants.BANG + "net_device" ) for net_dev in net.dev_base_head.to_list(net_device_symname, "dev_list"): - if isinstance(netns_id, NotAvailableValue) or net.get_inode() != netns_id: + if ( + isinstance(netns_id, NotAvailableValue) + or net.get_inode() != netns_id + ): continue dev_name = utility.array_to_string(net_dev.name) netdevices_map[net_dev.ifindex] = dev_name diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 486314dd5..858845125 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -392,7 +392,9 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): symbol_table_arr = volobj.vol.type_name.split("!", 1) symbol_table = symbol_table_arr[0] if len(symbol_table_arr) == 2 else None - module_names = volobj._context.modules.get_modules_by_symbol_tables(symbol_table) + module_names = volobj._context.modules.get_modules_by_symbol_tables( + symbol_table + ) module_names = list(module_names) if not module_names: diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index d97916b74..3dbf560c6 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -878,7 +878,9 @@ class vfsmount(objects.StructType): if type(vfsmount_ptr) == objects.Pointer: return self.vol.offset == vfsmount_ptr else: - raise exceptions.VolatilityException("Unexpected argument type. It has to be a 'vfsmount *'") + raise exceptions.VolatilityException( + "Unexpected argument type. It has to be a 'vfsmount *'" + ) def _get_real_mnt(self): """Gets the struct 'mount' containing this 'vfsmount'. @@ -889,7 +891,9 @@ class vfsmount(objects.StructType): mount: the struct 'mount' containing this 'vfsmount'. """ vmlinux = linux.LinuxUtilities.get_vmlinux_from_volobj(self) - return linux.LinuxUtilities.container_of(self.vol.offset, "mount", "mnt", vmlinux) + return linux.LinuxUtilities.container_of( + self.vol.offset, "mount", "mnt", vmlinux + ) def get_vfsmnt_current(self): """Returns the current fs where we are mounted on @@ -994,6 +998,7 @@ class vfsmount(objects.StructType): def get_devname(self) -> str: return utility.pointer_to_string(self.mnt_devname, count=255) + class kobject(objects.StructType): def reference_count(self): refcnt = self.kref.refcount @@ -1262,6 +1267,7 @@ class netlink_sock(objects.StructType): else: raise AttributeError("Unable to find a destination port id") + class vsock_sock(objects.StructType): def get_protocol(self): # The protocol should always be 0 for vsocks From 200099f8b3f92e2ad228e0a481a835f1fbc9d8b6 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Mon, 8 May 2023 14:58:51 +0200 Subject: [PATCH 122/140] Improve support for socket filters in kernels < 4.1.52 --- .../framework/plugins/linux/sockstat.py | 23 +++++++++++++++---- .../framework/symbols/linux/__init__.py | 1 + .../symbols/linux/extensions/__init__.py | 17 ++++++++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 72a1e453e..f06b3ad8e 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -150,19 +150,32 @@ class SockHandlers(interfaces.configuration.VersionableInterface): return bpfprog = sock_filter.prog - if bpfprog.type == 0: - # BPF_PROG_TYPE_UNSPEC = 0 + + BPF_PROG_TYPE_UNSPEC = 0 # cBPF filter + try: + bpfprog_type = bpfprog.get_type() + if bpfprog_type == BPF_PROG_TYPE_UNSPEC: + return # cBPF filter + except AttributeError: + # kernel < 3.18.140, it's a cBPF filter + return + + BPF_PROG_TYPE_SOCKET_FILTER = 1 # eBPF filter + if bpfprog_type != BPF_PROG_TYPE_SOCKET_FILTER: + socket_filter["bpf_filter_type"] = f"UNK({bpfprog_type})" + vollog.warning(f"Unexpected BPF type {bpfprog_type} for a socket") return socket_filter["bpf_filter_type"] = "eBPF" if not bpfprog.has_member("aux") or not bpfprog.aux: - return + return # kernel < 3.18.140 bpfprog_aux = bpfprog.aux + if bpfprog_aux.has_member("id"): - # `id` member was added to `bpf_prog_aux` in kernels 4.13 + # `id` member was added to `bpf_prog_aux` in kernels 4.13.16 socket_filter["bpf_filter_id"] = str(bpfprog_aux.id) if bpfprog_aux.has_member("name"): - # `name` was added to `bpf_prog_aux` in kernels 4.15 + # `name` was added to `bpf_prog_aux` in kernels 4.15.18 bpfprog_name = utility.array_to_string(bpfprog_aux.name) if bpfprog_name: socket_filter["bpf_filter_name"] = bpfprog_name diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 858845125..3780a86f0 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -30,6 +30,7 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): self.set_type_class("kobject", extensions.kobject) # Might not exist in the current symbols self.optional_set_type_class("module", extensions.module) + self.optional_set_type_class("bpf_prog", extensions.bpf_prog) # Mount self.set_type_class("vfsmount", extensions.vfsmount) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 3dbf560c6..87bd76554 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -1313,3 +1313,20 @@ class xdp_sock(objects.StructType): def get_state(self): # xdp_sock.state is an enum return self.state.lookup() + + +class bpf_prog(objects.StructType): + def get_type(self): + # The program type was in `bpf_prog_aux::prog_type` from 3.18.140 to + # 4.1.52 before it was moved to `bpf_prog::type` + if self.has_member("type"): + # kernel >= 4.1.52 + return self.type + + if self.has_member("aux") and self.aux: + if self.aux.has_member("prog_type"): + # 3.18.140 <= kernel < 4.1.52 + return self.aux.prog_type + + # kernel < 3.18.140 + raise AttributeError("Unable to find the BPF type") From 7d65c20cc0b971c862889065d8a90d98e24819c9 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Mon, 8 May 2023 14:59:58 +0200 Subject: [PATCH 123/140] Fix f-string --- volatility3/framework/plugins/linux/iomem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/linux/iomem.py b/volatility3/framework/plugins/linux/iomem.py index 2056851aa..785405ef3 100644 --- a/volatility3/framework/plugins/linux/iomem.py +++ b/volatility3/framework/plugins/linux/iomem.py @@ -66,7 +66,7 @@ class IOMem(interfaces.plugins.PluginInterface): name = utility.pointer_to_string(resource.name, 128) except exceptions.InvalidAddressException: vollog.warning( - "Unable to follow pointer to name for resource object at {resource_offset:#x}, " + f"Unable to follow pointer to name for resource object at {resource_offset:#x}, " "replaced with UnreadableValue" ) name = renderers.UnreadableValue() From de28b5ab7077c04ac776018264c22484d1766c37 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Mon, 8 May 2023 19:59:53 +0200 Subject: [PATCH 124/140] Rollback explicit context --- volatility3/framework/plugins/linux/elfs.py | 2 +- .../framework/plugins/linux/malfind.py | 2 +- volatility3/framework/plugins/linux/proc.py | 2 +- .../framework/symbols/linux/__init__.py | 27 +++++++++++-------- .../symbols/linux/extensions/__init__.py | 6 ++--- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/volatility3/framework/plugins/linux/elfs.py b/volatility3/framework/plugins/linux/elfs.py index fa14dcd49..822a69dd6 100644 --- a/volatility3/framework/plugins/linux/elfs.py +++ b/volatility3/framework/plugins/linux/elfs.py @@ -58,7 +58,7 @@ class Elfs(plugins.PluginInterface): ): continue - path = vma.get_name(task) + path = vma.get_name(self.context, task) yield ( 0, diff --git a/volatility3/framework/plugins/linux/malfind.py b/volatility3/framework/plugins/linux/malfind.py index 552fb8f53..18237b80c 100644 --- a/volatility3/framework/plugins/linux/malfind.py +++ b/volatility3/framework/plugins/linux/malfind.py @@ -47,7 +47,7 @@ class Malfind(interfaces.plugins.PluginInterface): proc_layer = self.context.layers[proc_layer_name] for vma in task.mm.get_mmap_iter(): - if vma.is_suspicious() and vma.get_name(task) != "[vdso]": + if vma.is_suspicious() and vma.get_name(self.context, task) != "[vdso]": data = proc_layer.read(vma.vm_start, 64, pad=True) yield vma, data diff --git a/volatility3/framework/plugins/linux/proc.py b/volatility3/framework/plugins/linux/proc.py index fa7bc1629..9d8af482e 100644 --- a/volatility3/framework/plugins/linux/proc.py +++ b/volatility3/framework/plugins/linux/proc.py @@ -59,7 +59,7 @@ class Maps(plugins.PluginInterface): minor = inode_object.i_sb.minor inode = inode_object.i_ino - path = vma.get_name(task) + path = vma.get_name(self.context, task) yield ( 0, diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 3780a86f0..3f51d3c0c 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -139,10 +139,11 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): return path @classmethod - def _get_new_sock_pipe_path(cls, task, filp) -> str: + def _get_new_sock_pipe_path(cls, context, task, filp) -> str: """Returns the sock pipe pathname relative to the task's root directory. Args: + context: The context to retrieve required elements (layers, symbol tables) from task (task_struct): A reference task filp (file *): A pointer to a sock pipe open file @@ -151,7 +152,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): """ dentry = filp.get_dentry() - kernel_module = cls.get_vmlinux_from_volobj(dentry) + kernel_module = cls.get_vmlinux_from_volobj(context, dentry) sym_addr = dentry.d_op.d_dname symbs = list(kernel_module.get_symbols_by_absolute_location(sym_addr)) @@ -182,13 +183,14 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): return ret @classmethod - def path_for_file(cls, task, filp) -> str: + def path_for_file(cls, context, task, filp) -> str: """Returns a file (or sock pipe) pathname relative to the task's root directory. A 'file' structure doesn't have enough information to properly restore its full path we need the root mount information from task_struct to determine this Args: + context: The context to retrieve required elements (layers, symbol tables) from task (task_struct): A reference task filp (file *): A pointer to an open file @@ -218,7 +220,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): dname_is_valid = False if dname_is_valid: - ret = LinuxUtilities._get_new_sock_pipe_path(task, filp) + ret = LinuxUtilities._get_new_sock_pipe_path(context, task, filp) else: ret = LinuxUtilities._get_path_file(task, filp) @@ -253,7 +255,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): for fd_num, filp in enumerate(fds): if filp != 0: - full_path = LinuxUtilities.path_for_file(task, filp) + full_path = LinuxUtilities.path_for_file(context, task, filp) yield fd_num, filp, full_path @@ -378,30 +380,33 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): ) @classmethod - def get_vmlinux_from_volobj(cls, volobj): + def get_vmlinux_from_volobj( + cls, + context: interfaces.context.ContextInterface, + volobj: interfaces.objects.ObjectInterface, + ) -> interfaces.context.ModuleInterface: """Get the vmlinux from a vol obj Args: + context: The context to retrieve required elements (layers, symbol tables) from volobj (vol object): A vol object Raises: ValueError: If it cannot obtain any module from the symbol table Returns: - volatility3.framework.contexts.Module: A kernel object (vmlinux) + A kernel object (vmlinux) """ symbol_table_arr = volobj.vol.type_name.split("!", 1) symbol_table = symbol_table_arr[0] if len(symbol_table_arr) == 2 else None - module_names = volobj._context.modules.get_modules_by_symbol_tables( - symbol_table - ) + module_names = context.modules.get_modules_by_symbol_tables(symbol_table) module_names = list(module_names) if not module_names: raise ValueError(f"No module using the symbol table '{symbol_table}'") kernel_module_name = module_names[0] - kernel = volobj._context.modules[kernel_module_name] + kernel = context.modules[kernel_module_name] return kernel diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 87bd76554..1caab3ce5 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -435,9 +435,9 @@ class vm_area_struct(objects.StructType): return self.vm_pgoff << constants.linux.PAGE_SHIFT - def get_name(self, task): + def get_name(self, context, task): if self.vm_file != 0: - fname = linux.LinuxUtilities.path_for_file(task, self.vm_file) + fname = linux.LinuxUtilities.path_for_file(context, task, self.vm_file) elif self.vm_start <= task.mm.start_brk and self.vm_end >= task.mm.brk: fname = "[heap]" elif self.vm_start <= task.mm.start_stack <= self.vm_end: @@ -890,7 +890,7 @@ class vfsmount(objects.StructType): Returns: mount: the struct 'mount' containing this 'vfsmount'. """ - vmlinux = linux.LinuxUtilities.get_vmlinux_from_volobj(self) + vmlinux = linux.LinuxUtilities.get_vmlinux_from_volobj(self._context, self) return linux.LinuxUtilities.container_of( self.vol.offset, "mount", "mnt", vmlinux ) From 859482445907fd9c2ae579d37e0a898b10857690 Mon Sep 17 00:00:00 2001 From: Eve Date: Tue, 9 May 2023 06:47:07 +0100 Subject: [PATCH 125/140] Linux: Ensure that objects made when parsing maple tree use the native_layer_name --- volatility3/framework/symbols/linux/extensions/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index a8bf1b6aa..931003b60 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -349,7 +349,7 @@ class maple_tree(objects.StructType): symbol_table_name = self.get_symbol_table_name() node_parent_mte = self._context.object( symbol_table_name + constants.BANG + "pointer", - layer_name=self.vol.layer_name, + layer_name=self.vol.native_layer_name, offset=pointer, ) @@ -424,7 +424,7 @@ class mm_struct(objects.StructType): # convert pointer to vm_area_struct and yield vma = self._context.object( symbol_table_name + constants.BANG + "vm_area_struct", - layer_name=self.vol.layer_name, + layer_name=self.vol.native_layer_name, offset=vma_pointer ) yield vma From a2906ad270b3ec09dd38db3bc1d24b58de0c7054 Mon Sep 17 00:00:00 2001 From: Eve Date: Tue, 9 May 2023 07:07:39 +0100 Subject: [PATCH 126/140] Linux: Update comments and var names for maple tree depth warnings --- .../symbols/linux/extensions/__init__.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 931003b60..67a4cd2d0 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -314,15 +314,15 @@ class maple_tree(objects.StructType): def get_slot_iter(self): """Parse the Maple Tree and return every non zero slot.""" maple_tree_offset = self.vol.offset & ~(self.MAPLE_NODE_POINTER_MASK) - maple_tree_depth = ( + expected_maple_tree_depth = ( self.ma_flags & self.MT_FLAGS_HEIGHT_MASK ) >> self.MT_FLAGS_HEIGHT_OFFSET yield from self._parse_maple_tree_node( - self.ma_root, maple_tree_offset, maple_tree_depth + self.ma_root, maple_tree_offset, expected_maple_tree_depth ) def _parse_maple_tree_node( - self, maple_tree_entry, parent, maple_tree_depth, seen=set(), depth=1 + self, maple_tree_entry, parent, expected_maple_tree_depth, seen=set(), current_depth=1 ): """Recursively parse Maple Tree Nodes and yield all non empty slots""" @@ -334,11 +334,16 @@ class maple_tree(objects.StructType): return else: seen.add(maple_tree_entry) - if maple_tree_depth < depth: + + # check if we have exceeded the expected depth of this maple tree. + # e.g. when current_depth is larger than expected_maple_tree_depth there may be an issue. + # it is normal that expected_maple_tree_depth is equal to current_depth. + if expected_maple_tree_depth < current_depth: vollog.warning( - f"The depth for the maple tree at {hex(self.vol.offset)} is {maple_tree_depth}, however when parsing the nodes " - f"a depth of {depth} was reached. This is unexpected and may lead to incorrect results." + f"The depth for the maple tree at {hex(self.vol.offset)} is {expected_maple_tree_depth}, however when parsing the nodes " + f"a depth of {current_depth} was reached. This is unexpected and may lead to incorrect results." ) + # parse the mte to extract the pointer value, node type, and leaf status pointer = maple_tree_entry & ~(self.MAPLE_NODE_POINTER_MASK) node_type = ( @@ -379,13 +384,13 @@ class maple_tree(objects.StructType): for slot in node.mr64.slot: if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield from self._parse_maple_tree_node( - slot, pointer, maple_tree_depth, seen, depth + 1 + slot, pointer, expected_maple_tree_depth, seen, current_depth + 1 ) elif node_type == self.MAPLE_ARANGE_64: for slot in node.ma64.slot: if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield from self._parse_maple_tree_node( - slot, pointer, maple_tree_depth, seen, depth + 1 + slot, pointer, expected_maple_tree_depth, seen, current_depth + 1 ) else: # unkown maple node type From d367b973a5cc305aad8f3a5f0b92e4b525122d11 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 9 May 2023 09:05:37 +0200 Subject: [PATCH 127/140] Renamed get_vmlinux_from_volobj() to get_module_from_volobj_type() --- volatility3/framework/symbols/linux/__init__.py | 4 ++-- volatility3/framework/symbols/linux/extensions/__init__.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 3f51d3c0c..dbdf1b777 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -152,7 +152,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): """ dentry = filp.get_dentry() - kernel_module = cls.get_vmlinux_from_volobj(context, dentry) + kernel_module = cls.get_module_from_volobj_type(context, dentry) sym_addr = dentry.d_op.d_dname symbs = list(kernel_module.get_symbols_by_absolute_location(sym_addr)) @@ -380,7 +380,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): ) @classmethod - def get_vmlinux_from_volobj( + def get_module_from_volobj_type( cls, context: interfaces.context.ContextInterface, volobj: interfaces.objects.ObjectInterface, diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 1caab3ce5..060da4d7d 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -890,7 +890,7 @@ class vfsmount(objects.StructType): Returns: mount: the struct 'mount' containing this 'vfsmount'. """ - vmlinux = linux.LinuxUtilities.get_vmlinux_from_volobj(self._context, self) + vmlinux = linux.LinuxUtilities.get_module_from_volobj_type(self._context, self) return linux.LinuxUtilities.container_of( self.vol.offset, "mount", "mnt", vmlinux ) From 6c5db21ae76519ffff538eba822dccfb2c63f4c2 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 9 May 2023 09:22:02 +0200 Subject: [PATCH 128/140] Undo mnt_ns_id cast to str --- volatility3/framework/plugins/linux/mountinfo.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index 0606884ff..87ba4f9c1 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -160,7 +160,7 @@ class MountInfo(plugins.PluginInterface): mnt_namespace = task.nsproxy.mnt_ns try: - mnt_ns_id = str(mnt_namespace.get_inode()) + mnt_ns_id = mnt_namespace.get_inode() except AttributeError: mnt_ns_id = renderers.NotAvailableValue() @@ -256,7 +256,7 @@ class MountInfo(plugins.PluginInterface): self.context, self.config["kernel"], filter_func=pid_filter ) - columns = [("MNT_NS_ID", str)] + columns = [("MNT_NS_ID", int)] # The PID column does not make sense when a PID filter is not specified. In that case, the default behavior is # to displays the mountpoints per namespace. if pids: From 89ed65cc56bdce8b11c3480f5dcc93d0e1021961 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 9 May 2023 09:59:21 +0200 Subject: [PATCH 129/140] Improve filter warning implementation --- .../framework/plugins/linux/mountinfo.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index 87ba4f9c1..e4081dc83 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -184,19 +184,12 @@ class MountInfo(plugins.PluginInterface): mount_format: bool, filtered_by_pids: bool, ) -> Iterable[Tuple[int, Tuple]]: - warning_shown = False + show_filter_warning = False for task, mnt, mnt_ns_id in self._get_tasks_mountpoints( tasks, filtered_by_pids ): - if ( - not warning_shown - and mnt_ns_ids - and isinstance(mnt_ns_id, renderers.NotAvailableValue) - ): - vollog.warning( - "Cannot filter by namespace id, it is not available in this kernel." - ) - warning_shown = True + if mnt_ns_ids and isinstance(mnt_ns_id, renderers.NotAvailableValue): + show_filter_warning = True if ( not isinstance(mnt_ns_id, renderers.NotAvailableValue) @@ -246,6 +239,11 @@ class MountInfo(plugins.PluginInterface): yield (0, fields_values) + if show_filter_warning: + vollog.warning( + "Could not filter by mount namespace id. This field is not available in this kernel." + ) + def run(self): pids = self.config.get("pids") mount_ns_ids = self.config.get("mntns") From 7f9afccf6f44fb69cd1039ab478854317bad5d5b Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 10 May 2023 08:49:46 +0100 Subject: [PATCH 130/140] Linux: apply black formating to maple tree parsing --- .../symbols/linux/extensions/__init__.py | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 67a4cd2d0..7398a9ecd 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -294,6 +294,7 @@ class fs_struct(objects.StructType): raise AttributeError("Unable to find the root mount") + class maple_tree(objects.StructType): # include/linux/maple_tree.h # Mask for Maple Tree Flags @@ -322,7 +323,12 @@ class maple_tree(objects.StructType): ) def _parse_maple_tree_node( - self, maple_tree_entry, parent, expected_maple_tree_depth, seen=set(), current_depth=1 + self, + maple_tree_entry, + parent, + expected_maple_tree_depth, + seen=set(), + current_depth=1, ): """Recursively parse Maple Tree Nodes and yield all non empty slots""" @@ -384,13 +390,21 @@ class maple_tree(objects.StructType): for slot in node.mr64.slot: if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield from self._parse_maple_tree_node( - slot, pointer, expected_maple_tree_depth, seen, current_depth + 1 + slot, + pointer, + expected_maple_tree_depth, + seen, + current_depth + 1, ) elif node_type == self.MAPLE_ARANGE_64: for slot in node.ma64.slot: if (slot & ~(self.MAPLE_NODE_TYPE_MASK)) != 0: yield from self._parse_maple_tree_node( - slot, pointer, expected_maple_tree_depth, seen, current_depth + 1 + slot, + pointer, + expected_maple_tree_depth, + seen, + current_depth + 1, ) else: # unkown maple node type @@ -398,13 +412,16 @@ class maple_tree(objects.StructType): f"Unkown Maple Tree node type {node_type} at offset {hex(pointer)}." ) + class mm_struct(objects.StructType): def get_mmap_iter(self) -> Iterable[interfaces.objects.ObjectInterface]: """Returns an iterator for the mmap list member of an mm_struct.""" - if not self.has_member('mmap'): - raise AttributeError("get_mmap_iter called on mm_struct where no mmap member exists.") - + if not self.has_member("mmap"): + raise AttributeError( + "get_mmap_iter called on mm_struct where no mmap member exists." + ) + if not self.mmap: return @@ -420,9 +437,11 @@ class mm_struct(objects.StructType): def get_maple_tree_iter(self) -> Iterable[interfaces.objects.ObjectInterface]: """Returns an iterator for the mm_mt member of an mm_struct.""" - - if not self.has_member('mm_mt'): - raise AttributeError("get_maple_tree_iter called on mm_struct where no mm_mt member exists.") + + if not self.has_member("mm_mt"): + raise AttributeError( + "get_maple_tree_iter called on mm_struct where no mm_mt member exists." + ) symbol_table_name = self.get_symbol_table_name() for vma_pointer in self.mm_mt.get_slot_iter(): @@ -430,20 +449,21 @@ class mm_struct(objects.StructType): vma = self._context.object( symbol_table_name + constants.BANG + "vm_area_struct", layer_name=self.vol.native_layer_name, - offset=vma_pointer + offset=vma_pointer, ) yield vma def get_vma_iter(self) -> Iterable[interfaces.objects.ObjectInterface]: """Returns an iterator for the VMAs in an mm_struct. Automatically choosing the mmap or mm_mt as required.""" - if self.has_member('mmap'): + if self.has_member("mmap"): yield from self.get_mmap_iter() - elif self.has_member('mm_mt'): + elif self.has_member("mm_mt"): yield from self.get_maple_tree_iter() else: raise AttributeError("Unable to find mmap or mm_mt in mm_struct") - + + class super_block(objects.StructType): # include/linux/kdev_t.h MINORBITS = 20 From 834372a6f89dc4c49c0a2fb563c1dd1cea1392ae Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 10 May 2023 09:12:03 +0100 Subject: [PATCH 131/140] Linux: apply black v23.3.0 formating to volatility3/framework/symbols/linux/extensions/__init__.py --- .../symbols/linux/extensions/__init__.py | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/volatility3/framework/symbols/linux/extensions/__init__.py b/volatility3/framework/symbols/linux/extensions/__init__.py index 7398a9ecd..eecb200f9 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -34,10 +34,8 @@ class module(generic.GenericIntelProcess): def get_init_size(self): if self.has_member("init_layout"): return self.init_layout.size - elif self.has_member("init_size"): return self.init_size - raise AttributeError( "module -> get_init_size: Unable to determine .init section size of module" ) @@ -45,10 +43,8 @@ class module(generic.GenericIntelProcess): def get_core_size(self): if self.has_member("core_layout"): return self.core_layout.size - elif self.has_member("core_size"): return self.core_size - raise AttributeError( "module -> get_core_size: Unable to determine core size of module" ) @@ -58,7 +54,6 @@ class module(generic.GenericIntelProcess): return self.core_layout.base elif self.has_member("module_core"): return self.module_core - raise AttributeError("module -> get_module_core: Unable to get module core") def get_module_init(self): @@ -66,7 +61,6 @@ class module(generic.GenericIntelProcess): return self.init_layout.base elif self.has_member("module_init"): return self.module_init - raise AttributeError("module -> get_module_core: Unable to get module init") def get_name(self): @@ -88,7 +82,6 @@ class module(generic.GenericIntelProcess): idx = 0 while arr[idx]: idx = idx + 1 - return idx def get_sections(self): @@ -97,7 +90,6 @@ class module(generic.GenericIntelProcess): num_sects = self.sect_attrs.nsections else: num_sects = self._get_sect_count(self.sect_attrs.grp) - arr = self._context.object( self.get_symbol_table().name + constants.BANG + "array", layer_name=self.vol.layer_name, @@ -116,7 +108,6 @@ class module(generic.GenericIntelProcess): prefix = "Elf64_" else: prefix = "Elf32_" - elf_table_name = intermed.IntermediateSymbolTable.create( self.context, self.config_path, @@ -155,7 +146,6 @@ class module(generic.GenericIntelProcess): return self.kallsyms.symtab elif self.has_member("symtab"): return self.symtab - raise AttributeError("module -> symtab: Unable to get symtab") @property @@ -164,7 +154,6 @@ class module(generic.GenericIntelProcess): return int(self.kallsyms.num_symtab) elif self.has_member("num_symtab"): return int(self.num_symtab) - raise AttributeError( "module -> num_symtab: Unable to determine number of symbols" ) @@ -177,7 +166,6 @@ class module(generic.GenericIntelProcess): # Older kernels elif self.has_member("strtab"): return self.strtab - raise AttributeError("module -> strtab: Unable to get strtab") @@ -195,19 +183,15 @@ class task_struct(generic.GenericIntelProcess): pgd = self.mm.pgd except exceptions.InvalidAddressException: return None - if not isinstance(parent_layer, linear.LinearlyMappedLayer): raise TypeError( "Parent layer is not a translation layer, unable to construct process layer" ) - dtb, layer_name = parent_layer.translate(pgd) if not dtb: return None - if preferred_name is None: preferred_name = self.vol.layer_name + f"_Process{self.pid}" - # Add the constructed layer and return the name return self._add_process_layer( self._context, dtb, config_prefix, preferred_name @@ -229,7 +213,6 @@ class task_struct(generic.GenericIntelProcess): vollog.info( f"adding vma: {start:x} {self.mm.brk:x} | {end:x} {self.mm.start_brk:x}" ) - yield (start, end - start) @property @@ -282,7 +265,6 @@ class fs_struct(objects.StructType): return self.root elif self.root.has_member("dentry"): return self.root.dentry - raise AttributeError("Unable to find the root dentry") def get_root_mnt(self): @@ -291,7 +273,6 @@ class fs_struct(objects.StructType): return self.rootmnt elif self.root.has_member("mnt"): return self.root.mnt - raise AttributeError("Unable to find the root mount") @@ -340,7 +321,6 @@ class maple_tree(objects.StructType): return else: seen.add(maple_tree_entry) - # check if we have exceeded the expected depth of this maple tree. # e.g. when current_depth is larger than expected_maple_tree_depth there may be an issue. # it is normal that expected_maple_tree_depth is equal to current_depth. @@ -349,7 +329,6 @@ class maple_tree(objects.StructType): f"The depth for the maple tree at {hex(self.vol.offset)} is {expected_maple_tree_depth}, however when parsing the nodes " f"a depth of {current_depth} was reached. This is unexpected and may lead to incorrect results." ) - # parse the mte to extract the pointer value, node type, and leaf status pointer = maple_tree_entry & ~(self.MAPLE_NODE_POINTER_MASK) node_type = ( @@ -421,10 +400,8 @@ class mm_struct(objects.StructType): raise AttributeError( "get_mmap_iter called on mm_struct where no mmap member exists." ) - if not self.mmap: return - yield self.mmap seen = {self.mmap.vol.offset} @@ -442,7 +419,6 @@ class mm_struct(objects.StructType): raise AttributeError( "get_maple_tree_iter called on mm_struct where no mm_mt member exists." ) - symbol_table_name = self.get_symbol_table_name() for vma_pointer in self.mm_mt.get_slot_iter(): # convert pointer to vm_area_struct and yield @@ -569,7 +545,6 @@ class vm_area_struct(objects.StructType): retval = retval + char else: retval = retval + "-" - return retval # only parse the rwx bits @@ -583,7 +558,6 @@ class vm_area_struct(objects.StructType): def get_page_offset(self) -> int: if self.vm_file == 0: return 0 - return self.vm_pgoff << constants.linux.PAGE_SHIFT def get_name(self, context, task): @@ -600,7 +574,6 @@ class vm_area_struct(objects.StructType): fname = "[vdso]" else: fname = "Anonymous Mapping" - return fname # used by malfind @@ -611,10 +584,8 @@ class vm_area_struct(objects.StructType): if flags_str == "rwx": ret = True - elif flags_str == "r-x" and self.vm_file.dereference().vol.offset == 0: ret = True - return ret @@ -624,12 +595,10 @@ class qstr(objects.StructType): str_length = self.len + 1 # Maximum length should include null terminator else: str_length = 255 - try: ret = objects.utility.pointer_to_string(self.name, str_length) except (exceptions.InvalidAddressException, ValueError): ret = "" - return ret @@ -660,7 +629,6 @@ class dentry(objects.StructType): """ if self.vol.offset == old_dentry: return True - return self.d_ancestor(old_dentry) def d_ancestor(self, ancestor_dentry): @@ -678,10 +646,8 @@ class dentry(objects.StructType): ): if current_dentry.d_parent == ancestor_dentry.vol.offset: return current_dentry - dentry_seen.add(current_dentry.vol.offset) current_dentry = current_dentry.d_parent - return None @@ -738,12 +704,10 @@ class list_head(objects.StructType, collections.abc.Iterable): link = getattr(self, direction).dereference() except exceptions.InvalidAddressException: return - if not sentinel: yield self._context.object( symbol_type, layer, offset=self.vol.offset - relative_offset ) - seen = {self.vol.offset} while link.vol.offset not in seen: obj = self._context.object( @@ -869,7 +833,6 @@ class mount(objects.StructType): peer = current_mnt.get_peer_under_root(self.mnt_ns, root) if peer and peer.vol.offset != 0: return peer.mnt_group_id - mnt_seen.add(current_mnt.vol.offset) current_mnt = current_mnt.mnt_master return 0 @@ -885,12 +848,10 @@ class mount(objects.StructType): current_mnt.mnt.mnt_root, root ): return current_mnt - mnt_seen.add(current_mnt.vol.offset) current_mnt = current_mnt.next_peer() if current_mnt.vol.offset == self.vol.offset: break - return None def is_path_reachable(self, current_dentry, root): @@ -907,7 +868,6 @@ class mount(objects.StructType): current_dentry = current_mnt.mnt_mountpoint mnt_seen.add(current_mnt.vol.offset) current_mnt = current_mnt.mnt_parent - return current_mnt.mnt.vol.offset == root.mnt and current_dentry.is_subdir( root.dentry ) @@ -968,7 +928,6 @@ class kobject(objects.StructType): ret = refcnt.counter else: ret = refcnt.refs.counter - return ret @@ -987,7 +946,6 @@ class mnt_namespace(objects.StructType): if not self._context.symbol_space.has_type(mnt_type): # Old kernels ~ 2.6 mnt_type = table_name + constants.BANG + "vfsmount" - for mount in self.list.to_list(mnt_type, "mnt_list"): yield mount @@ -1012,7 +970,6 @@ class socket(objects.StructType): ) if not module_names: raise ValueError(f"No module using the symbol table {symbol_table}") - kernel_module_name = module_names[0] kernel = self._context.modules[kernel_module_name] return kernel @@ -1022,7 +979,6 @@ class socket(objects.StructType): kernel = self._get_vol_kernel() except ValueError: return 0 - socket_alloc = linux.LinuxUtilities.container_of( self.vol.offset, "socket_alloc", "socket", kernel ) @@ -1048,7 +1004,6 @@ class sock(objects.StructType): def get_inode(self): if not self.sk_socket: return 0 - return self.sk_socket.get_inode() def get_protocol(self): @@ -1058,7 +1013,6 @@ class sock(objects.StructType): # Return the generic socket state if self.has_member("sk"): return self.sk.sk_socket.get_state() - return self.sk_socket.get_state() @@ -1066,7 +1020,6 @@ class unix_sock(objects.StructType): def get_name(self): if not self.addr: return - sockaddr_un = self.addr.name.cast("sockaddr_un") saddr = str(utility.array_to_string(sockaddr_un.sun_path)) return saddr @@ -1102,7 +1055,6 @@ class inet_sock(objects.StructType): protocol = IP_PROTOCOLS.get(self.sk.sk_protocol) if self.get_family() == "AF_INET6": protocol = IPV6_PROTOCOLS.get(self.sk.sk_protocol, protocol) - return protocol def get_state(self): @@ -1133,7 +1085,6 @@ class inet_sock(objects.StructType): dport_le = sk_common.skc_dport else: return - return socket_module.htons(dport_le) def get_src_addr(self): @@ -1152,7 +1103,6 @@ class inet_sock(objects.StructType): saddr = self.pinet6.saddr else: return - parent_layer = self._context.layers[self.vol.layer_name] try: addr_bytes = parent_layer.read(saddr.vol.offset, addr_size) @@ -1161,7 +1111,6 @@ class inet_sock(objects.StructType): f"Unable to read socket src address from {saddr.vol.offset:#x}" ) return - return socket_module.inet_ntop(family, addr_bytes) def get_dst_addr(self): @@ -1183,7 +1132,6 @@ class inet_sock(objects.StructType): addr_size = 16 else: return - parent_layer = self._context.layers[self.vol.layer_name] try: addr_bytes = parent_layer.read(daddr.vol.offset, addr_size) @@ -1192,7 +1140,6 @@ class inet_sock(objects.StructType): f"Unable to read socket dst address from {daddr.vol.offset:#x}" ) return - return socket_module.inet_ntop(family, addr_bytes) From b3c348820f341ea43a5bb215a5841349f46fb16c Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 10 May 2023 09:18:05 +0100 Subject: [PATCH 132/140] Linux: apply black v23.3.0 formating to volatility3/framework/symbols/linux/__init__.py --- volatility3/framework/symbols/linux/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 7a22241a5..c012f9cc7 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -53,6 +53,7 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): # Only found in 6.1+ kernels self.optional_set_type_class("maple_tree", extensions.maple_tree) + class LinuxUtilities(interfaces.configuration.VersionableInterface): """Class with multiple useful linux functions.""" From 7516346b649d7678c2996d1f4e02c48637fb050f Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Wed, 10 May 2023 10:55:19 +0200 Subject: [PATCH 133/140] Adjust framework versioning --- volatility3/framework/plugins/linux/mountinfo.py | 5 ++++- volatility3/framework/symbols/linux/__init__.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/volatility3/framework/plugins/linux/mountinfo.py b/volatility3/framework/plugins/linux/mountinfo.py index e4081dc83..da743bb60 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -50,6 +50,9 @@ class MountInfo(plugins.PluginInterface): requirements.PluginRequirement( name="pslist", plugin=pslist.PsList, version=(2, 0, 0) ), + requirements.VersionRequirement( + name="linuxutils", component=linux.LinuxUtilities, version=(2, 1, 0) + ), requirements.ListRequirement( name="pids", description="Filter on specific process IDs.", @@ -86,7 +89,7 @@ class MountInfo(plugins.PluginInterface): if not mnt_root: return None - path_root = linux.LinuxUtilities._get_path_mnt(task, mnt) + path_root = linux.LinuxUtilities.get_path_mnt(task, mnt) if not path_root: return None diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index dbdf1b777..9ae8479b6 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -55,7 +55,7 @@ class LinuxKernelIntermedSymbols(intermed.IntermediateSymbolTable): class LinuxUtilities(interfaces.configuration.VersionableInterface): """Class with multiple useful linux functions.""" - _version = (2, 0, 0) + _version = (2, 1, 0) _required_framework_version = (2, 0, 0) framework.require_interface_version(*_required_framework_version) @@ -79,7 +79,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): return cls.do_get_path(rdentry, rmnt, dentry, vfsmnt) @classmethod - def _get_path_mnt(cls, task, mnt) -> str: + def get_path_mnt(cls, task, mnt) -> str: """Returns the mount point pathname relative to the task's root directory. Args: From e28b42731e663baf09051978a669d47e72506aaf Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 10 May 2023 21:55:16 +0100 Subject: [PATCH 134/140] Core: Change readthedocs python version Attempting to fix #953 --- .readthedocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 4d21d9b40..3f10db145 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -14,6 +14,6 @@ formats: all # Optionally set the version of Python and requirements required to build your docs python: - version: 3.7 + version: 3.11 install: - requirements: doc/requirements.txt From 3f1797f7e5b47a9bfd03f521604cd014012a0c69 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 10 May 2023 21:57:50 +0100 Subject: [PATCH 135/140] Core: Change readthedocs build platform Fixes #953 --- .readthedocs.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 3f10db145..e7c2b25d5 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -12,8 +12,12 @@ sphinx: # Optionally build your docs in additional formats such as PDF and ePub formats: all +build: + os: ubuntu-22.04 + tools: + python: "3.11" + # Optionally set the version of Python and requirements required to build your docs python: - version: 3.11 install: - requirements: doc/requirements.txt From 8e56cb39d13c22575ba5133ffbc6b83344bed0ba Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 10 May 2023 22:07:38 +0100 Subject: [PATCH 136/140] Docs: Fix the documentation dependencies --- doc/requirements.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/requirements.txt b/doc/requirements.txt index 93d6ea70a..b715e59f5 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,4 +1,8 @@ # These packages are required for building the documentation. -sphinx>=4.0.0 +sphinx>=4.0.0,<7 sphinx_autodoc_typehints>=1.4.0 sphinx-rtd-theme>=0.4.3 + +yara-python +pycryptodome +pefile From bea981180d5044aa979d39e6f2e06e13909513a1 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Mon, 22 May 2023 01:32:00 +0100 Subject: [PATCH 137/140] Prevent potential dentry pointer memory smear --- volatility3/framework/symbols/linux/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 36314b2c6..3ddffb49a 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -200,8 +200,11 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): Returns: str: A file (or sock pipe) pathname relative to the task's root directory. """ + + # Memory smear protection: Check that both the file and dentry pointers are valids. try: dentry = filp.get_dentry() + dentry.is_root() except exceptions.InvalidAddressException: return "" From 0f42f0eaf1bbc47b86a180289a5d393cbcafc6d7 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Mon, 22 May 2023 01:39:14 +0100 Subject: [PATCH 138/140] fix typo --- volatility3/framework/symbols/linux/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/volatility3/framework/symbols/linux/__init__.py b/volatility3/framework/symbols/linux/__init__.py index 3ddffb49a..339f6417f 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -201,7 +201,7 @@ class LinuxUtilities(interfaces.configuration.VersionableInterface): str: A file (or sock pipe) pathname relative to the task's root directory. """ - # Memory smear protection: Check that both the file and dentry pointers are valids. + # Memory smear protection: Check that both the file and dentry pointers are valid. try: dentry = filp.get_dentry() dentry.is_root() From b9e5cfb393c35d005235996804be6c6853a34b73 Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Mon, 22 May 2023 15:05:39 +0100 Subject: [PATCH 139/140] Core: Protect from clearing a non-existant cache Issue kindly raised by @garanews, thanks! 5:D --- volatility3/framework/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/__init__.py b/volatility3/framework/__init__.py index c7b23a9c3..9c17846a8 100644 --- a/volatility3/framework/__init__.py +++ b/volatility3/framework/__init__.py @@ -224,4 +224,7 @@ def list_plugins() -> Dict[str, Type[interfaces.plugins.PluginInterface]]: def clear_cache(complete=False): - os.unlink(os.path.join(constants.CACHE_PATH, constants.IDENTIFIERS_FILENAME)) + try: + os.unlink(os.path.join(constants.CACHE_PATH, constants.IDENTIFIERS_FILENAME)) + except FileNotFoundError: + vollog.log(constants.LOGLEVEL_VVVV, "Attempting to clear a non-existant cache") From aa04b8ca3d6ba9db3d7658436927a2430cc6371e Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Fri, 16 Jun 2023 18:03:07 +0100 Subject: [PATCH 140/140] Windows: Fix VAD offset canonicalization #969 --- volatility3/framework/plugins/windows/vadinfo.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/volatility3/framework/plugins/windows/vadinfo.py b/volatility3/framework/plugins/windows/vadinfo.py index 812affe86..abc6142fe 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -198,6 +198,7 @@ class VadInfo(interfaces.plugins.PluginInterface): def _generator(self, procs): kernel = self.context.modules[self.config["kernel"]] + kernel_layer = self.context.layers[kernel.layer_name] def passthrough(_: interfaces.objects.ObjectInterface) -> bool: return False @@ -229,7 +230,7 @@ class VadInfo(interfaces.plugins.PluginInterface): ( proc.UniqueProcessId, process_name, - format_hints.Hex(vad.vol.offset), + format_hints.Hex(kernel_layer.canonicalize(vad.vol.offset)), format_hints.Hex(vad.get_start()), format_hints.Hex(vad.get_end()), vad.get_tag(),