From 02bf609fd14f64161ca5ad648860ed530b794d6b Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Fri, 10 Dec 2021 17:50:52 +1100 Subject: [PATCH 01/77] 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 a074f57446..9ce7027f38 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 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] 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 02/77] 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 c25ea0e2f0..6b63de6c56 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -11,3 +11,219 @@ # 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 0000000000..3be3594635 --- /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 36e23a35d7..739ecbedb0 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -30,6 +30,16 @@ def __init__(self, *args, **kwargs) -> None: 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 @@ def path_for_file(cls, context, task, filp) -> str: 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 @@ def walk_internal_list(cls, vmlinux, struct_name, list_member, list_start): 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 0edd60608f..2af5f56b00 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 @@ def reference_count(self): 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 03/77] 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 9ce7027f38..5ebd8e5c90 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 3be3594635..4611d52e04 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 04/77] 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 5ebd8e5c90..30ceafdabe 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -60,13 +60,7 @@ def list_fds(cls, 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 @@ def _generator(self): 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 05/77] 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 4611d52e04..c53091d864 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -16,7 +16,12 @@ 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 @@ 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", From 6970776c4c330e1bc96e77df6c13d823503d1295 Mon Sep 17 00:00:00 2001 From: Gustavo Moreira Date: Tue, 14 Dec 2021 13:07:50 +1100 Subject: [PATCH 06/77] 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 c53091d864..eab8993ba9 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -158,11 +158,11 @@ def _packet_sock(self, sock, extended): 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 @@ def _packet_sock(self, sock, extended): 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 07/77] 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 eab8993ba9..943c497b12 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -183,8 +183,8 @@ def _update_extended_socket_bpf(self, sock_filter, extended): 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 08/77] 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 943c497b12..d147f3e5e0 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -76,7 +76,7 @@ def process_sock(self, sock): 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 @@ def _unix_sock(self, sock, _extended): 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 @@ def _inet_sock(self, sock, _extended): 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 @@ def _netlink_sock(self, sock, _extended): 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 @@ def _update_extended_socket_bpf(self, sock_filter, extended): 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 @@ def _xdp_sock(self, sock, _extended): 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 09/77] 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 d147f3e5e0..f00169cf68 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -49,7 +49,7 @@ def _build_network_devices_map(self, netns_id): 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 @@ def _update_extended_socket_bpf(self, sock_filter, extended): 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 @@ def _xdp_sock(self, sock, extended): 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 10/77] 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 30ceafdabe..983f62562d 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -60,7 +60,12 @@ def list_fds(cls, 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 @@ def _generator(self, fds_generator): 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 11/77] 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 f00169cf68..1af232b079 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -310,13 +310,13 @@ def list_sockets(cls, 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 12/77] 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 1af232b079..2e71dcd619 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -189,7 +189,7 @@ def _update_extended_socket_bpf(self, sock_filter, extended): 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 @@ def list_sockets(cls, 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 13/77] 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 2e71dcd619..c133a400dc 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -333,14 +333,13 @@ def list_sockets(cls, 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 @@ def _generator(self): 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 @@ def run(self): ("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 14/77] 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 c133a400dc..e1fbfeddb7 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 @@ def __init__(self, vmlinux, task): "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 @@ def _build_network_devices_map(self, netns_id): 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 @@ def process_sock(self, sock): 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 @@ def _unix_sock(self, sock, extended): 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 @@ def _inet_sock(self, sock, extended): 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 @@ def _netlink_sock(self, sock, extended): 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 @@ def _vsock_sock(self, sock, extended): 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 @@ def _packet_sock(self, sock, extended): 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 @@ def _update_extended_socket_bpf(self, sock_filter, extended): 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 @@ def _xdp_sock(self, sock, extended): 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 15/77] 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 e1fbfeddb7..5f9b6fb07d 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -63,9 +63,9 @@ def process_sock(self, sock: objects.StructType): 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 @@ def bt_addr(addr): 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 16/77] 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 2af5f56b00..d551f3b02f 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -766,7 +766,6 @@ def protocol(self): @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 17/77] 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 d551f3b02f..3f286e1a3b 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -748,7 +748,8 @@ def protocol(self): 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 18/77] 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 5f9b6fb07d..323f022131 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -59,7 +59,9 @@ def process_sock(self, sock: objects.StructType): 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 @@ def process_sock(self, sock: objects.StructType): 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 @@ def _unix_sock(self, sock: objects.StructType, extended: dict): 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 @@ def _inet_sock(self, sock: objects.StructType, extended: dict): 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 @@ def _netlink_sock(self, sock: objects.StructType, extended: dict): 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 @@ def _vsock_sock(self, sock: objects.StructType, extended: dict): 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 @@ def _xdp_sock(self, sock: objects.StructType, extended: dict): 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 19/77] 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 323f022131..4f9ac64de5 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -336,7 +336,7 @@ def list_sockets(cls, 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 20/77] 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 4f9ac64de5..a73d5581ad 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 @@ 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 @@ def __init__(self, vmlinux, task): "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 @@ def _build_network_devices_map(self, netns_id: int): 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 @@ def _update_extended_socket_filters_info(self, sock: objects.Pointer, extended: 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 @@ def _extract_socket_filter_info(self, sock_filter: objects.Pointer, extended: di 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 @@ def _unix_sock(self, sock: objects.StructType): 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 @@ def _inet_sock(self, sock: objects.StructType): 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 @@ def _netlink_sock(self, sock: objects.StructType): 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 @@ def _vsock_sock(self, sock: objects.StructType): 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 @@ def _packet_sock(self, sock: objects.StructType): 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 @@ def _xdp_sock(self, sock: objects.StructType): 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 @@ def get_requirements(cls): @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 @@ def list_sockets(cls, 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 @@ def _generator(self, pids, netns_arg, symbol_table): 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 @@ def _generator(self, pids, netns_arg, symbol_table): 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 @@ def run(self): ("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 21/77] 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 a73d5581ad..f526d614e0 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -416,8 +416,7 @@ def list_sockets(cls, 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 22/77] 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 f526d614e0..0346cf7e4f 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -80,10 +80,10 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 23/77] 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 3f286e1a3b..c9ad48044d 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -565,7 +565,7 @@ def get_inode(self): 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 @@ def inode(self): 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 24/77] 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 a35b6f02e8..550690b903 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -12,6 +12,9 @@ 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 @@ "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 25/77] 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 0346cf7e4f..fd172fc43f 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 26/77] 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 739ecbedb0..347d0b3661 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 @@ def walk_internal_list(cls, vmlinux, struct_name, list_member, list_start): 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 27/77] 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 05a88ba5aa..fe495f88d9 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -634,7 +634,11 @@ def inode(self): 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 28/77] 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 fd172fc43f..7d4b20da05 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -74,7 +74,7 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def _unix_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tupl 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 @@ def _inet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tupl 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 @@ def _netlink_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, T 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 @@ def _packet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def bt_addr(addr): 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 @@ def bt_addr(addr): 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 @@ def list_sockets(cls, 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 @@ def list_sockets(cls, 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 fe495f88d9..80180ad1b6 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -617,20 +617,17 @@ def _get_vol_kernel_module_name(self): 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 @@ def inode(self): 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 @@ def name(self): 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 @@ def state(self): 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 @@ def state(self): 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 @@ def dst_port(self): 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 @@ def src_addr(self): 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 @@ def dst_addr(self): 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 @@ def state(self): 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 @@ def protocol(self): 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 @@ def protocol(self): 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 29/77] 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 550690b903..0c4d3c3761 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -137,6 +137,16 @@ "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 7d4b20da05..0306bec028 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -92,7 +92,8 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu # 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 @@ def _vsock_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tup 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 @@ def bt_addr(addr): 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 347d0b3661..977066eaca 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -32,10 +32,12 @@ def __init__(self, *args, **kwargs) -> None: # 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 80180ad1b6..644a88632e 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 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 @@ def get_inode(self): 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 @@ def _get_vol_kernel_module_name(self): 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 @@ def get_inode(self): 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 @@ def get_state(self): 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 @@ def get_state(self): 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 @@ def get_dst_port(self): 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 @@ def get_src_addr(self): 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 @@ def get_src_addr(self): 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 @@ def get_dst_addr(self): 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 @@ def get_dst_addr(self): 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 @@ def get_protocol(self): 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() + + +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 "UNCONNECTED" + 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 @@ def get_protocol(self): 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 30/77] 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 644a88632e..53ad489427 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -754,7 +754,12 @@ def get_src_addr(self): 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 @@ def get_dst_addr(self): 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 31/77] 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 e0f927ec93..e91b0cd4e5 100644 --- a/volatility3/framework/objects/__init__.py +++ b/volatility3/framework/objects/__init__.py @@ -9,7 +9,7 @@ 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 32/77] 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 e357b150ac..3c51263b79 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -52,6 +52,10 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] "(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 @@ def filter_function(x: interfaces.objects.ObjectInterface) -> bool: 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 33/77] 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 3c51263b79..dc7e4dff47 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -49,13 +49,8 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] # 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 @@ def passthrough(_: interfaces.objects.ObjectInterface) -> bool: 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 34/77] 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 0306bec028..29be883092 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -100,7 +100,7 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def _generator(self, pids: List[int], netns_id_arg: int, symbol_table: str): 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 35/77] 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 29be883092..6d50c296df 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 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu # 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 @@ def _extract_socket_filter_info(self, sock_filter: objects.Pointer, extended: di 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 @@ def run(self): ("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 bd8748ec8f..1945bb3ef4 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -27,8 +27,15 @@ def __init__(self, *args, **kwargs) -> None: 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 @@ def __init__(self, *args, **kwargs) -> None: 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 27fa00a8c2..d193200c42 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -16,8 +16,7 @@ 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 @@ def get_inode(self): 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 @@ def get_state(self): # 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 @@ 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_module.htons(self.num) @@ -1017,7 +1020,6 @@ def get_state(self): # 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 @@ def get_state(self): 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 36/77] 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 | 162 +++++++++--------- .../symbols/linux/extensions/__init__.py | 49 ++---- 2 files changed, 99 insertions(+), 112 deletions(-) diff --git a/volatility3/framework/plugins/linux/sockstat.py b/volatility3/framework/plugins/linux/sockstat.py index 6d50c296df..f927c0c831 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -72,7 +72,7 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu # 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 @@ def _unix_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tupl 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: + src_addr = unix_sock.get_name() + src_port = unix_sock.get_inode() + + if unix_sock.peer: peer = unix_sock.peer.dereference().cast("unix_sock") - daddr = peer.get_name() - dinode = peer.get_inode() + dst_addr = peer.get_name() + dst_port = peer.get_inode() else: - daddr = dinode = "" + dst_addr = dst_port = None - saddr_tag = f"{saddr} {sinode}" - daddr_tag = f"{daddr} {dinode}" - sock_stat = saddr_tag, daddr_tag, state + 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 @@ def _inet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tupl 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 @@ def _netlink_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, T 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) - - 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) + src_addr = f"groups:0x{groups_bitmap:08x}" + src_port = netlink_sock.portid + + 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 @@ def _vsock_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tup 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 @@ def _packet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def _xdp_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple 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 @@ def _bluetooth_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, 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 @@ def bt_addr(addr): 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 @@ def _generator(self, pids: List[int], netns_id_arg: int, symbol_table: str): 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 @@ def _generator(self, pids: List[int], netns_id_arg: int, symbol_table: str): 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 @@ def _generator(self, pids: List[int], netns_id_arg: int, symbol_table: str): 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 @@ def run(self): ("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 d193200c42..ab0622cf0c 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -819,16 +819,12 @@ 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): return SOCK_FAMILY[family_idx] - else: - return "UNKNOWN" def get_type(self): return SOCK_TYPES.get(self.sk_type, "") @@ -840,7 +836,7 @@ def get_inode(self): 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 @@ def get_state(self): 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 @@ def get_state(self): 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 @@ 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" 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 @@ def get_state(self): 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 @@ def get_src_addr(self): 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 @@ def get_dst_addr(self): 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 @@ 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" def get_state(self): # Return the generic socket state @@ -997,10 +986,7 @@ def get_state(self): 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 @@ def get_protocol(self): 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 37/77] 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 f927c0c831..ad3eee01fe 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -6,7 +6,7 @@ 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 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def _extract_socket_filter_info(self, sock_filter: objects.Pointer, extended: di # 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 @@ def _unix_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tupl 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 @@ def _inet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tupl 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 @@ def _netlink_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, T 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 @@ def _vsock_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tup 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 @@ def _packet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def _xdp_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple 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 @@ def _xdp_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple 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 @@ def _bluetooth_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, 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 @@ def bt_addr(addr): 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 @@ def list_sockets(cls, 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 @@ def _generator(self, pids: List[int], netns_id_arg: int, symbol_table: str): 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 @@ def _generator(self, pids: List[int], netns_id_arg: int, symbol_table: str): 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 ab0622cf0c..c26d68b0e4 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -1023,5 +1023,5 @@ def get_protocol(self): 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 92c7b3e5500b03fa68d8893204b31f50cef7b4dc Mon Sep 17 00:00:00 2001 From: Eve Date: Fri, 9 Dec 2022 15:37:20 +0000 Subject: [PATCH 38/77] 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 0000000000..b62400c455 --- /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 39/77] 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 b62400c455..028eb2a572 100644 --- a/volatility3/framework/plugins/linux/envars.py +++ b/volatility3/framework/plugins/linux/envars.py @@ -12,6 +12,7 @@ vollog = logging.getLogger(__name__) + class Envars(plugins.PluginInterface): """Lists processes with their environment variables""" @@ -51,7 +52,9 @@ def _generator(self, tasks): 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 @@ def _generator(self, tasks): 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 @@ def _generator(self, tasks): # 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 40/77] 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 dce207fd57..ecfb6bf112 100644 --- a/volatility3/framework/layers/intel.py +++ b/volatility3/framework/layers/intel.py @@ -56,6 +56,11 @@ def __init__( ) 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 @@ def _page_is_valid(entry: int) -> bool: """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 41/77] 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 0000000000..6b0469d60e --- /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 42/77] 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 | 196 +++++++++++++----- .../framework/symbols/linux/__init__.py | 60 +++--- .../symbols/linux/extensions/__init__.py | 29 ++- 5 files changed, 221 insertions(+), 111 deletions(-) diff --git a/volatility3/framework/constants/linux/__init__.py b/volatility3/framework/constants/linux/__init__.py index 0c4d3c3761..1b133eb42b 100644 --- a/volatility3/framework/constants/linux/__init__.py +++ b/volatility3/framework/constants/linux/__init__.py @@ -13,7 +13,7 @@ """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 @@ # 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 @@ 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 920aaf7f13..62bade1f10 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -46,10 +46,12 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] ] @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 @@ def list_fds(cls, 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 @@ def _generator(self, pids, symbol_table): 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 ad3eee01fe..f03a2ad8eb 100644 --- a/volatility3/framework/plugins/linux/sockstat.py +++ b/volatility3/framework/plugins/linux/sockstat.py @@ -16,6 +16,7 @@ vollog = logging.getLogger(__name__) + class SockHandlers(interfaces.configuration.VersionableInterface): """Handles several socket families extracting the sockets information.""" @@ -56,7 +57,9 @@ def _build_network_devices_map(self, netns_id: int) -> Dict: 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 @@ def _build_network_devices_map(self, netns_id: int) -> Dict: 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 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def process_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def _update_socket_filters_info(self, sock: objects.Pointer, socket_filter: dict 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 @@ def _extract_socket_filter_info(self, sock_filter: objects.Pointer, socket_filte 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 @@ def _unix_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tupl 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 @@ def _inet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tupl 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 @@ def _netlink_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, T 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 @@ def _vsock_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tup 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 @@ def _packet_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tu 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 @@ def _xdp_sock(self, sock: objects.StructType) -> Tuple[objects.StructType, Tuple 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 @@ def bt_addr(addr): 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 @@ def bt_addr(addr): 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 @@ def bt_addr(addr): 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 @@ def list_sockets(cls, 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 @@ def _format_fields(self, sock_stat, protocol): 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 @@ def _generator(self, pids: List[int], netns_id_arg: int, symbol_table: str): 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) - - for task, netns_id, fd_num, family, sock_type, protocol, sock_fields in socket_generator: + 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: 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() - - fields = (netns_id, task.pid, fd_num, format_hints.Hex(sock.vol.offset), - family, sock_type, protocol, *sock_stat, socket_filter_str) + 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, + ) 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 1d1419ae92..0d7cbb7e47 100644 --- a/volatility3/framework/symbols/linux/__init__.py +++ b/volatility3/framework/symbols/linux/__init__.py @@ -17,38 +17,38 @@ def __init__(self, *args, **kwargs) -> None: 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 @@ def walk_internal_list(cls, vmlinux, struct_name, list_member, list_start): @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 @@ def container_of( 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 0ea61a3bba..55f139730d 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -812,6 +812,7 @@ def get_mnt_mountpoint(self): 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 @@ def get_mount_points(self): 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 @@ def get_inode(self): 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 @@ def get_inode(self): 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 @@ def get_state(self): 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 @@ def get_state(self): return self.sk_socket.get_state() + class unix_sock(objects.StructType): def get_name(self): if not self.addr: @@ -932,6 +941,7 @@ def get_state(self): 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 @@ def get_src_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 + dport_le = sk_common.skc_portpair & 0xFFFF elif hasattr(self, "dport"): dport_le = self.dport elif hasattr(self, "inet_dport"): @@ -999,7 +1009,9 @@ def get_src_addr(self): 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 @@ def get_dst_addr(self): 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 @@ def get_state(self): # 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 @@ 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_module.htons(self.num) @@ -1066,6 +1083,7 @@ def get_state(self): # 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 @@ def get_state(self): 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 43/77] 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 8293549878..501b8655e3 100644 --- a/volatility3/framework/layers/qemu.py +++ b/volatility3/framework/layers/qemu.py @@ -117,13 +117,15 @@ def _read_configuration( 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 44/77] 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 bcafa9aedf..a10d365926 100644 --- a/volatility3/framework/layers/elf.py +++ b/volatility3/framework/layers/elf.py @@ -119,4 +119,8 @@ def stack( 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 1641a6e4c43aaf8ff8baf993319c61fb6163206c Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Thu, 12 Jan 2023 19:58:51 +0000 Subject: [PATCH 45/77] 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 6cd4535504..3214c71341 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -206,7 +206,7 @@ def passthrough(_: interfaces.objects.ObjectInterface) -> bool: 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 46/77] 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 1c3260fede..74fd0e4e86 100644 --- a/volatility3/framework/symbols/windows/pdbutil.py +++ b/volatility3/framework/symbols/windows/pdbutil.py @@ -54,6 +54,7 @@ def symbol_table_from_offset( """ 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 47/77] 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 3bde95cf3a..98e09d9258 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -203,7 +203,10 @@ def _list_registry_callbacks_new( 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 48/77] 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 98e09d9258..a1283d3943 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -203,7 +203,7 @@ def _list_registry_callbacks_new( 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 49/77] 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 a1283d3943..6898935fc7 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -82,7 +82,7 @@ def list_notify_routines( 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 @@ def _list_registry_callbacks_new( 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 @@ def list_registry_callbacks( 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 @@ def list_bugcheck_reason_callbacks( 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 @@ def list_bugcheck_callbacks( 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 50/77] 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 6898935fc7..56609a73a8 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 51/77] 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 5df378d089..bb0f41ca36 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -241,7 +241,7 @@ def render(self, grid: interfaces.renderers.TreeGrid) -> None: # 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 52/77] 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 bb0f41ca36..7e2167b6b0 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -241,7 +241,9 @@ def render(self, grid: interfaces.renderers.TreeGrid) -> None: # 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 53/77] 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 6b0469d60e..08a61bb46b 100644 --- a/volatility3/framework/plugins/linux/iomem.py +++ b/volatility3/framework/plugins/linux/iomem.py @@ -48,15 +48,32 @@ def parse_resource( 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 54/77] 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 078af2abf9..b9300251ac 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 55/77] 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 b9300251ac..fa9bd7ef64 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 56/77] 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 08a61bb46b..fddea46684 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 @@ def parse_resource( ) 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 @@ def parse_resource( 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 @@ def _generator(self): 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 @@ def _generator(self): "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 57/77] 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 fb124a3c90..336902d501 100644 --- a/volatility3/cli/__init__.py +++ b/volatility3/cli/__init__.py @@ -443,7 +443,7 @@ def run(self): # 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 5df378d089..4df04e2a3f 100644 --- a/volatility3/cli/text_renderer.py +++ b/volatility3/cli/text_renderer.py @@ -346,7 +346,7 @@ def visitor( 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 dd89a64fdb..3048a0885e 100644 --- a/volatility3/cli/volargparse.py +++ b/volatility3/cli/volargparse.py @@ -31,7 +31,6 @@ def __call__( 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 ceed2fe50f..239f0cfb6a 100644 --- a/volatility3/framework/automagic/construct_layers.py +++ b/volatility3/framework/automagic/construct_layers.py @@ -36,7 +36,6 @@ def __call__( 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 3ca0b4ea2e..aa75fbc3d7 100644 --- a/volatility3/framework/automagic/mac.py +++ b/volatility3/framework/automagic/mac.py @@ -251,7 +251,6 @@ def _scan_generator(cls, context, layer_name, progress_callback): 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 44a76506c2..63c6fc7fac 100644 --- a/volatility3/framework/automagic/symbol_cache.py +++ b/volatility3/framework/automagic/symbol_cache.py @@ -161,7 +161,8 @@ def get_location_statistics( """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 0143e74b13..f30dff456c 100644 --- a/volatility3/framework/automagic/symbol_finder.py +++ b/volatility3/framework/automagic/symbol_finder.py @@ -82,13 +82,13 @@ def __call__( 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 a3c31a953d..68592f8cbc 100644 --- a/volatility3/framework/interfaces/layers.py +++ b/volatility3/framework/interfaces/layers.py @@ -294,7 +294,7 @@ def _coalesce_sections( 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 @@ def _scan_chunk( 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 @@ def _scan_iterator( 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 b12fdd01c3..66f3f0e4fc 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -73,7 +73,7 @@ def _load_segments(self) -> None: ) 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 64166cfba9..8efd4f7c7a 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 ecfb6bf112..478eb168f0 100644 --- a/volatility3/framework/layers/intel.py +++ b/volatility3/framework/layers/intel.py @@ -173,7 +173,7 @@ def _translate_entry(self, offset: int) -> Tuple[int, int]: ) # 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 73700dd3c0..542fd6ca2d 100644 --- a/volatility3/framework/layers/leechcore.py +++ b/volatility3/framework/layers/leechcore.py @@ -91,7 +91,6 @@ def in_memmap(self, start, size): 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 19203eb66d..47170df7b9 100644 --- a/volatility3/framework/layers/linear.py +++ b/volatility3/framework/layers/linear.py @@ -42,7 +42,7 @@ def read(self, offset: int, length: int, pad: bool = False) -> bytes: 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 @@ def write(self, offset: int, value: bytes) -> None: 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 660e0a2994..cc8ce1f4ca 100644 --- a/volatility3/framework/layers/registry.py +++ b/volatility3/framework/layers/registry.py @@ -269,7 +269,6 @@ def _translate(self, offset: int) -> int: 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 c177ee642d..90e714eaab 100644 --- a/volatility3/framework/plugins/linux/check_afinfo.py +++ b/volatility3/framework/plugins/linux/check_afinfo.py @@ -68,7 +68,6 @@ def _check_afinfo(self, var_name, var, op_members, seq_members): 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 @@ def _generator(self): ) 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 @@ def _generator(self): 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 6d4e2bc8af..ab6ee4935c 100644 --- a/volatility3/framework/plugins/linux/check_creds.py +++ b/volatility3/framework/plugins/linux/check_creds.py @@ -46,7 +46,6 @@ def _generator(self): 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 @@ def _generator(self): 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 766858888c..9b3594c5e9 100644 --- a/volatility3/framework/plugins/linux/check_modules.py +++ b/volatility3/framework/plugins/linux/check_modules.py @@ -37,7 +37,6 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] def get_kset_modules( cls, context: interfaces.context.ContextInterface, vmlinux_name: str ): - vmlinux = context.modules[vmlinux_name] try: @@ -57,7 +56,6 @@ def get_kset_modules( 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 6b11038ec0..b1d2919f9c 100644 --- a/volatility3/framework/plugins/linux/check_syscall.py +++ b/volatility3/framework/plugins/linux/check_syscall.py @@ -110,7 +110,7 @@ def _get_table_info_disassembly(self, ptr_sz, vmlinux): 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 @@ def _generator(self): 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 @@ def _generator(self): 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 @@ def _generator(self): ) 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 1c1e094c3e..a65b0d00bc 100644 --- a/volatility3/framework/plugins/linux/lsmod.py +++ b/volatility3/framework/plugins/linux/lsmod.py @@ -60,7 +60,6 @@ def list_modules( 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 62bade1f10..d970ad8a95 100644 --- a/volatility3/framework/plugins/linux/lsof.py +++ b/volatility3/framework/plugins/linux/lsof.py @@ -52,7 +52,6 @@ def list_fds( 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 c849d51c64..ebd6e55a0f 100644 --- a/volatility3/framework/plugins/linux/mountinfo.py +++ b/volatility3/framework/plugins/linux/mountinfo.py @@ -203,7 +203,6 @@ def _generator( 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 dcc9f3e065..45238ef8c9 100644 --- a/volatility3/framework/plugins/linux/tty_check.py +++ b/volatility3/framework/plugins/linux/tty_check.py @@ -61,7 +61,6 @@ def _generator(self): 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 @@ def _generator(self): 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 a7a32e9abc..5c22e6463d 100644 --- a/volatility3/framework/plugins/mac/check_syscall.py +++ b/volatility3/framework/plugins/mac/check_syscall.py @@ -47,7 +47,7 @@ def _generator(self): 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 0b945a8fd9..ed43bfb427 100644 --- a/volatility3/framework/plugins/mac/kauth_listeners.py +++ b/volatility3/framework/plugins/mac/kauth_listeners.py @@ -49,7 +49,6 @@ def _generator(self): 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 bfd7216a81..afb320a07d 100644 --- a/volatility3/framework/plugins/mac/kauth_scopes.py +++ b/volatility3/framework/plugins/mac/kauth_scopes.py @@ -65,7 +65,6 @@ def _generator(self): ) 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 74c5f60375..3b996bc0af 100644 --- a/volatility3/framework/plugins/mac/kevents.py +++ b/volatility3/framework/plugins/mac/kevents.py @@ -184,7 +184,6 @@ def _generator(self): 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 ede0fa32ba..c18b0b7a25 100644 --- a/volatility3/framework/plugins/mac/list_files.py +++ b/volatility3/framework/plugins/mac/list_files.py @@ -137,7 +137,6 @@ def _walk_vnodelist(cls, context, list_head, loop_vnodes): 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 @@ def _build_path(cls, vnodes, vnode_name, parent_offset): 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 @@ def list_files( 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 2cdd5e3de9..2979e374be 100644 --- a/volatility3/framework/plugins/mac/lsmod.py +++ b/volatility3/framework/plugins/mac/lsmod.py @@ -63,7 +63,6 @@ def list_modules( 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 @@ def list_modules( 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 581a9c67fa..76bba25f68 100644 --- a/volatility3/framework/plugins/mac/netstat.py +++ b/volatility3/framework/plugins/mac/netstat.py @@ -68,7 +68,6 @@ def list_sockets( # 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 @@ def _generator(self): 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 c2ae71e7eb..1d97216bfb 100644 --- a/volatility3/framework/plugins/mac/pslist.py +++ b/volatility3/framework/plugins/mac/pslist.py @@ -83,7 +83,6 @@ def get_list_tasks( @classmethod def create_pid_filter(cls, pid_list: List[int] = None) -> Callable[[int], bool]: - filter_func = lambda _: False # FIXME: mypy #4973 or #2608 pid_list = pid_list or [] diff --git a/volatility3/framework/plugins/timeliner.py b/volatility3/framework/plugins/timeliner.py index 0776b6cc82..d1c1c0f703 100644 --- a/volatility3/framework/plugins/timeliner.py +++ b/volatility3/framework/plugins/timeliner.py @@ -136,7 +136,7 @@ def _generator( ) 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 1a51a0b817..393c2a417f 100644 --- a/volatility3/framework/plugins/windows/bigpools.py +++ b/volatility3/framework/plugins/windows/bigpools.py @@ -141,7 +141,6 @@ def _generator(self) -> Iterator[Tuple[int, Tuple[int, str]]]: # , str, int]]]: 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 7d3093ed77..a9b669add4 100644 --- a/volatility3/framework/plugins/windows/cachedump.py +++ b/volatility3/framework/plugins/windows/cachedump.py @@ -173,7 +173,6 @@ def run(self): 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 56609a73a8..48b2e7c620 100644 --- a/volatility3/framework/plugins/windows/callbacks.py +++ b/volatility3/framework/plugins/windows/callbacks.py @@ -104,7 +104,6 @@ def list_notify_routines( ] for symbol_name, extended_list in symbol_names: - try: symbol_offset = ntkrnlmp.get_symbol(symbol_name).address except exceptions.SymbolError: @@ -354,7 +353,6 @@ def list_bugcheck_callbacks( ) for callback in callback_record.Entry: - if not context.layers[layer_name].is_valid(callback.CallbackRoutine, 64): continue @@ -372,7 +370,6 @@ def list_bugcheck_callbacks( 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 @@ def _generator(self): kernel.symbol_table_name, callback_table_name, ): - if callback_detail is None: detail = renderers.NotApplicableValue() else: @@ -451,7 +447,6 @@ def _generator(self): ) 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 2541629d59..6f39799c11 100644 --- a/volatility3/framework/plugins/windows/devicetree.py +++ b/volatility3/framework/plugins/windows/devicetree.py @@ -180,7 +180,7 @@ def _generator(self) -> Iterator[Tuple]: ), ) - 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 c1593b8369..d73cea652f 100644 --- a/volatility3/framework/plugins/windows/dlllist.py +++ b/volatility3/framework/plugins/windows/dlllist.py @@ -129,12 +129,10 @@ def _generator(self, procs): 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 4d2c24dea2..b5cd33db76 100644 --- a/volatility3/framework/plugins/windows/driverirp.py +++ b/volatility3/framework/plugins/windows/driverirp.py @@ -71,7 +71,6 @@ def _generator(self): 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 @@ def _generator(self): ) 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 cc735db306..de827602ee 100644 --- a/volatility3/framework/plugins/windows/drivermodule.py +++ b/volatility3/framework/plugins/windows/drivermodule.py @@ -73,7 +73,6 @@ def _generator(self) -> Iterator[Tuple]: ) 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 d8df80702c..24d81c3d59 100644 --- a/volatility3/framework/plugins/windows/driverscan.py +++ b/volatility3/framework/plugins/windows/driverscan.py @@ -54,7 +54,6 @@ def scan_drivers( 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 af95688971..38d55d15dc 100755 --- a/volatility3/framework/plugins/windows/dumpfiles.py +++ b/volatility3/framework/plugins/windows/dumpfiles.py @@ -229,7 +229,6 @@ def _generator(self, procs: List, offsets: List): ) 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 a1dbd76654..66db03c9c2 100644 --- a/volatility3/framework/plugins/windows/envars.py +++ b/volatility3/framework/plugins/windows/envars.py @@ -221,7 +221,6 @@ def _generator(self, data): ) 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 de3331e163..0f68f39d47 100644 --- a/volatility3/framework/plugins/windows/filescan.py +++ b/volatility3/framework/plugins/windows/filescan.py @@ -53,7 +53,6 @@ def scan_files( 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 @@ def _generator(self): 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 c4088426f3..9b20ed2d0f 100644 --- a/volatility3/framework/plugins/windows/getservicesids.py +++ b/volatility3/framework/plugins/windows/getservicesids.py @@ -73,7 +73,6 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] ] 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 2334a328da..3e332f85db 100644 --- a/volatility3/framework/plugins/windows/getsids.py +++ b/volatility3/framework/plugins/windows/getsids.py @@ -112,7 +112,6 @@ def lookup_user_sids(self) -> Dict[str, str]: 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 @@ def lookup_user_sids(self) -> Dict[str, str]: 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 @@ def _generator(self, procs): ) 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 2f25a05979..dd7c90860a 100644 --- a/volatility3/framework/plugins/windows/handles.py +++ b/volatility3/framework/plugins/windows/handles.py @@ -136,7 +136,6 @@ def find_sar_value(self): """ if self._sar_value is None: - if not has_capstone: return None kernel = self.context.modules[self.config["kernel"]] @@ -160,7 +159,7 @@ def find_sar_value(self): 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 @@ def _make_handle_array(self, offset, level, depth=0): 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 @@ def _make_handle_array(self, offset, level, depth=0): 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 @@ def _generator(self, procs): 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 @@ def _generator(self, procs): ) 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 72bea2c8bf..0c98ab8cac 100644 --- a/volatility3/framework/plugins/windows/hashdump.py +++ b/volatility3/framework/plugins/windows/hashdump.py @@ -602,7 +602,6 @@ def run(self): 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 aa7837029c..100a677c2e 100644 --- a/volatility3/framework/plugins/windows/info.py +++ b/volatility3/framework/plugins/windows/info.py @@ -187,7 +187,6 @@ def get_ntheader_structure( return nt_header def _generator(self): - kernel = self.context.modules[self.config["kernel"]] layer_name = kernel.layer_name @@ -215,7 +214,6 @@ def _generator(self): 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 @@ def _generator(self): ) 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 354ef31c95..d84c133c06 100644 --- a/volatility3/framework/plugins/windows/joblinks.py +++ b/volatility3/framework/plugins/windows/joblinks.py @@ -103,7 +103,7 @@ def _generator(self) -> Iterator[Tuple]: ), ) - 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 a9b229048c..9642810a58 100644 --- a/volatility3/framework/plugins/windows/ldrmodules.py +++ b/volatility3/framework/plugins/windows/ldrmodules.py @@ -33,7 +33,6 @@ def get_requirements(cls): ] 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 8cb2399056..12589b07e5 100644 --- a/volatility3/framework/plugins/windows/lsadump.py +++ b/volatility3/framework/plugins/windows/lsadump.py @@ -118,12 +118,10 @@ def get_secret_by_name( 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 @@ def decrypt_secret(cls, secret: bytes, key: bytes): 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 @@ def _generator( 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 @@ def _generator( 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 @@ def run(self): 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 1e7a009eb9..4249259555 100644 --- a/volatility3/framework/plugins/windows/malfind.py +++ b/volatility3/framework/plugins/windows/malfind.py @@ -151,7 +151,6 @@ def _generator(self, procs): 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 ccf6eccea2..e58ca8c242 100644 --- a/volatility3/framework/plugins/windows/mbrscan.py +++ b/volatility3/framework/plugins/windows/mbrscan.py @@ -99,7 +99,6 @@ def _generator(self) -> Iterator[Tuple]: all_zeros = bootcode.count(b"\x00") == len(bootcode) if not all_zeros: - partition_entries = [ partition_table.FirstEntry, partition_table.SecondEntry, @@ -155,7 +154,6 @@ def _generator(self) -> Iterator[Tuple]: 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 bbd9a7b4a1..99fadac07c 100644 --- a/volatility3/framework/plugins/windows/modscan.py +++ b/volatility3/framework/plugins/windows/modscan.py @@ -70,7 +70,6 @@ def scan_modules( 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 @@ def _generator(self): 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 @@ def _generator(self): 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 eba6d1ce79..ff61c215cd 100644 --- a/volatility3/framework/plugins/windows/modules.py +++ b/volatility3/framework/plugins/windows/modules.py @@ -53,7 +53,6 @@ def _generator(self): 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 ad6e024d13..64d3b5470a 100644 --- a/volatility3/framework/plugins/windows/mutantscan.py +++ b/volatility3/framework/plugins/windows/mutantscan.py @@ -53,7 +53,6 @@ def scan_mutants( 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 @@ def _generator(self): 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 5c866bfebc..d0bbd5cbdd 100644 --- a/volatility3/framework/plugins/windows/netscan.py +++ b/volatility3/framework/plugins/windows/netscan.py @@ -375,7 +375,6 @@ def scan( 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 @@ def _generator(self, show_corrupt_results: Optional[bool] = None): 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 1685f2a210..d3ce3fd2e2 100644 --- a/volatility3/framework/plugins/windows/netstat.py +++ b/volatility3/framework/plugins/windows/netstat.py @@ -329,7 +329,6 @@ def parse_partitions( alignment, net_symbol_table, ): - endpoint = context.object( obj_name, layer_name=layer_name, @@ -591,7 +590,6 @@ def _generator(self, show_corrupt_results: Optional[bool] = None): 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 13c611bf89..e131c5f785 100644 --- a/volatility3/framework/plugins/windows/poolscanner.py +++ b/volatility3/framework/plugins/windows/poolscanner.py @@ -144,7 +144,6 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] ] def _generator(self): - kernel = self.context.modules[self.config["kernel"]] symbol_table = kernel.symbol_table_name @@ -367,7 +366,6 @@ def generate_pool_scan( 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 7a7087c951..0370dfc921 100644 --- a/volatility3/framework/plugins/windows/privileges.py +++ b/volatility3/framework/plugins/windows/privileges.py @@ -66,7 +66,6 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] ] def _generator(self, procs): - for task in procs: try: process_token = task.Token.dereference().cast("_TOKEN") @@ -107,7 +106,6 @@ def _generator(self, procs): ) 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 7a06af36fa..88697e71ae 100644 --- a/volatility3/framework/plugins/windows/pslist.py +++ b/volatility3/framework/plugins/windows/pslist.py @@ -226,7 +226,6 @@ def _generator(self): 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 427814d22b..3d9ae5c1e2 100644 --- a/volatility3/framework/plugins/windows/psscan.py +++ b/volatility3/framework/plugins/windows/psscan.py @@ -87,7 +87,6 @@ def scan_processes( 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 @@ def _generator(self): 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 4abcd2f158..91798de405 100644 --- a/volatility3/framework/plugins/windows/registry/hivelist.py +++ b/volatility3/framework/plugins/windows/registry/hivelist.py @@ -88,7 +88,6 @@ def _generator(self) -> Iterator[Tuple[int, Tuple[int, str]]]: 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 c3a52e3033..7b3c0b6223 100644 --- a/volatility3/framework/plugins/windows/registry/hivescan.py +++ b/volatility3/framework/plugins/windows/registry/hivescan.py @@ -86,7 +86,6 @@ def _generator(self): 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 19527321ed..537bfc943a 100644 --- a/volatility3/framework/plugins/windows/registry/printkey.py +++ b/volatility3/framework/plugins/windows/registry/printkey.py @@ -241,7 +241,6 @@ def _registry_walker( key: str = None, recurse: bool = False, ): - for hive in hivelist.HiveList.list_hives( self.context, self.config_path, @@ -249,14 +248,13 @@ def _registry_walker( 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 f64a130fad..f90724f66c 100644 --- a/volatility3/framework/plugins/windows/registry/userassist.py +++ b/volatility3/framework/plugins/windows/registry/userassist.py @@ -248,7 +248,6 @@ def list_userassist( # 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 @@ def list_userassist( 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 3e15878bd2..d766b40ea4 100644 --- a/volatility3/framework/plugins/windows/sessions.py +++ b/volatility3/framework/plugins/windows/sessions.py @@ -51,7 +51,6 @@ def _generator(self): 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 @@ def generate_timeline(self): 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 e7a1820e4f..b697774cb7 100644 --- a/volatility3/framework/plugins/windows/skeleton_key_check.py +++ b/volatility3/framework/plugins/windows/skeleton_key_check.py @@ -187,7 +187,6 @@ def _find_array_with_pdb_symbols( 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 @@ def _find_csystems_with_scanning( 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 184d8388c3..6a47c36e9f 100644 --- a/volatility3/framework/plugins/windows/ssdt.py +++ b/volatility3/framework/plugins/windows/ssdt.py @@ -56,7 +56,6 @@ def build_module_collection( context_modules = [] for mod in mods: - try: module_name_with_ext = mod.BaseDllName.get_string() except exceptions.InvalidAddressException: @@ -83,7 +82,6 @@ def build_module_collection( 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 @@ def passthrough(func: int) -> int: ) 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 e6c1829e99..60562915ec 100644 --- a/volatility3/framework/plugins/windows/svcscan.py +++ b/volatility3/framework/plugins/windows/svcscan.py @@ -180,7 +180,6 @@ def _generator(self): symbol_table=kernel.symbol_table_name, filter_func=filter_func, ): - proc_id = "Unknown" try: proc_id = task.UniqueProcessId @@ -200,7 +199,6 @@ def _generator(self): 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 78c2c6931b..89fdf142e0 100644 --- a/volatility3/framework/plugins/windows/symlinkscan.py +++ b/volatility3/framework/plugins/windows/symlinkscan.py @@ -52,7 +52,6 @@ def scan_symlinks( 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 @@ def _generator(self): 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 3214c71341..812affe865 100644 --- a/volatility3/framework/plugins/windows/vadinfo.py +++ b/volatility3/framework/plugins/windows/vadinfo.py @@ -214,7 +214,6 @@ def filter_function(x: interfaces.objects.ObjectInterface) -> bool: 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 fea4a0f809..1c6615804a 100644 --- a/volatility3/framework/plugins/windows/verinfo.py +++ b/volatility3/framework/plugins/windows/verinfo.py @@ -222,7 +222,6 @@ def _generator( 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 6fbf139323..5190bec8d2 100644 --- a/volatility3/framework/plugins/windows/virtmap.py +++ b/volatility3/framework/plugins/windows/virtmap.py @@ -31,7 +31,7 @@ def get_requirements(cls) -> List[interfaces.configuration.RequirementInterface] 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 ee87b3b850..534686022c 100644 --- a/volatility3/framework/renderers/__init__.py +++ b/volatility3/framework/renderers/__init__.py @@ -181,7 +181,7 @@ def __init__( 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 @@ def function(_x: interfaces.renderers.TreeNode, _y: Any) -> Any: 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 239acbde3f..6ec9ebab97 100644 --- a/volatility3/framework/renderers/format_hints.py +++ b/volatility3/framework/renderers/format_hints.py @@ -36,7 +36,6 @@ def __new__( 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 d1af56a265..10cf39cf19 100644 --- a/volatility3/framework/symbols/__init__.py +++ b/volatility3/framework/symbols/__init__.py @@ -192,7 +192,7 @@ def _iterative_resolve(self, traverse_list): 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 0d7cbb7e47..ce07167e52 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 @@ def files_descriptors_for_process( symbol_table: str, task: interfaces.objects.ObjectInterface, ): - # task.files can be null if not task.files: return @@ -225,7 +223,7 @@ def files_descriptors_for_process( 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 55f139730d..5ab8f1aa0b 100644 --- a/volatility3/framework/symbols/linux/extensions/__init__.py +++ b/volatility3/framework/symbols/linux/extensions/__init__.py @@ -595,7 +595,6 @@ def to_list( 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 @@ def get_max_fds(self) -> interfaces.objects.ObjectInterface: class mount(objects.StructType): - MNT_NOSUID = 0x01 MNT_NODEV = 0x02 MNT_NOEXEC = 0x04 @@ -755,7 +753,6 @@ def is_path_reachable(self, current_dentry, root): 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 df6b23df88..416a7e4d28 100644 --- a/volatility3/framework/symbols/linux/extensions/elf.py +++ b/volatility3/framework/symbols/linux/extensions/elf.py @@ -22,7 +22,6 @@ def __init__( 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 3909817ea3..56ac96633f 100644 --- a/volatility3/framework/symbols/mac/__init__.py +++ b/volatility3/framework/symbols/mac/__init__.py @@ -70,7 +70,6 @@ def generate_kernel_handler_info( 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 @@ def walk_tailq( 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 @@ def walk_list_head( 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 @@ def walk_slist( 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 b678304b85..c89b527e62 100644 --- a/volatility3/framework/symbols/mac/extensions/__init__.py +++ b/volatility3/framework/symbols/mac/extensions/__init__.py @@ -206,7 +206,7 @@ def get_perms(self): 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 @@ def get_perms(self) -> str: 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 d34d6a22f7..ba00a40533 100755 --- a/volatility3/framework/symbols/windows/extensions/__init__.py +++ b/volatility3/framework/symbols/windows/extensions/__init__.py @@ -202,7 +202,6 @@ def get_parent(self): # 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 @@ def get_parent(self): # 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 @@ def get_start(self) -> int: """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 @@ def get_end(self) -> int: """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 @@ def get_is_wow64(self): 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 @@ def get_available_pages(self) -> List: 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 c0f2bd61a1..9b7573c2e8 100644 --- a/volatility3/framework/symbols/windows/extensions/network.py +++ b/volatility3/framework/symbols/windows/extensions/network.py @@ -64,7 +64,6 @@ def __init__( size: int, members: Dict[str, Tuple[int, interfaces.objects.Template]], ) -> None: - super().__init__( context=context, type_name=type_name, @@ -167,7 +166,6 @@ def dual_stack_sockets(self): 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 @@ def get_remote_address(self): 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 adee956f75..3f34fc3dd4 100644 --- a/volatility3/framework/symbols/windows/extensions/pe.py +++ b/volatility3/framework/symbols/windows/extensions/pe.py @@ -151,7 +151,6 @@ def reconstruct(self) -> Generator[Tuple[int, bytes], None, None]: 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 74fd0e4e86..a43933ccfb 100644 --- a/volatility3/framework/symbols/windows/pdbutil.py +++ b/volatility3/framework/symbols/windows/pdbutil.py @@ -249,7 +249,6 @@ def download_pdb_isf( # 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 @@ def pdbname_scan( 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 @@ def _modtable_from_pdb( 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 3212cb4650..5ef840f324 100644 --- a/volatility3/plugins/windows/registry/certificates.py +++ b/volatility3/plugins/windows/registry/certificates.py @@ -77,7 +77,6 @@ def _generator(self) -> Iterator[Tuple[int, Tuple[str, str, str, str]]]: 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 58/77] 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 dba5b5b802..5f45230726 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 59/77] 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 0000000000..f87b78eb1b --- /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 60/77] 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 f87b78eb1b..25e7206ec9 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -68,8 +68,11 @@ def _get_task_fields( 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 61/77] 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 25e7206ec9..ba233e53d4 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 @@ def scan_tasks( 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 @@ def scan_tasks( 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 62/77] 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 c4908caf33..39670a62dd 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 63/77] 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 63c6fc7fac..1ca5ba210e 100644 --- a/volatility3/framework/automagic/symbol_cache.py +++ b/volatility3/framework/automagic/symbol_cache.py @@ -332,7 +332,7 @@ def update(self, progress_callback=None): 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 64/77] 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 7c372da2ae..9db14d4411 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 1793012f13..99e0786cc9 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 66f3f0e4fc..3ce25ca6f7 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 @@ 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 @@ def _check_header(cls, layer: interfaces.layers.DataLayerInterface): 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 @@ def _read_snappy_frames( ] 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 @@ def _decode_data( ) -> 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 65/77] 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 3ce25ca6f7..83c10186fe 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -20,7 +20,23 @@ 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 @@ HAS_SNAPPY = False -class SnappyException(Exception): +class SnappyException(exceptions.VolatilityException): pass @@ -65,9 +81,12 @@ def _check_header(cls, layer: interfaces.layers.DataLayerInterface): 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 66/77] 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 83c10186fe..1ba564c614 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -17,25 +17,23 @@ 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 67/77] 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 1ca5ba210e..a58bf00912 100644 --- a/volatility3/framework/automagic/symbol_cache.py +++ b/volatility3/framework/automagic/symbol_cache.py @@ -371,7 +371,7 @@ def update(self, progress_callback=None): # 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 68/77] 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 88a3697dad..5c78d16828 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 @@ def __init__(self, *args, **kwargs) -> None: 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 @@ def get_requirements(cls): 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 @@ def find_level(self, pid: objects.Pointer) -> None: 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 @@ def _generator(self): # 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 @@ def yield_processes(pid): 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 @@ def run(self): ("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 69/77] 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 1ba564c614..c9c682ac4d 100644 --- a/volatility3/framework/layers/avml.py +++ b/volatility3/framework/layers/avml.py @@ -39,7 +39,7 @@ __snappy_uncompressed_length = lib_snappy.snappy_uncompressed_length HAS_SNAPPY = True -except OSError: +except (AttributeError, OSError): HAS_SNAPPY = False From a34fb8497633394062e10366553a2c69ba10c85a Mon Sep 17 00:00:00 2001 From: Eve Date: Wed, 8 Mar 2023 13:31:37 +0000 Subject: [PATCH 70/77] 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 ba233e53d4..60b96ba408 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -111,7 +111,7 @@ def scan_tasks( # 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 71/77] 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 60b96ba408..7cf0aa6317 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -112,9 +112,16 @@ def scan_tasks( 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 72/77] 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 7cf0aa6317..e8cc17a407 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -6,7 +6,7 @@ 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 @@ def scan_tasks( # 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 73/77] 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 e8cc17a407..f4bfd347ee 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -111,7 +111,9 @@ def scan_tasks( # 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 @@ def scan_tasks( # 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 85e86d45c547654afe7c2dca86f6c6d200cb05df Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 8 Mar 2023 20:42:24 +0000 Subject: [PATCH 74/77] 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 028eb2a572..5cbf0f5020 100644 --- a/volatility3/framework/plugins/linux/envars.py +++ b/volatility3/framework/plugins/linux/envars.py @@ -70,7 +70,6 @@ def _generator(self, tasks): # 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 fddea46684..8efbf3b578 100644 --- a/volatility3/framework/plugins/linux/iomem.py +++ b/volatility3/framework/plugins/linux/iomem.py @@ -128,7 +128,6 @@ def _generator(self): # 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 cfec8e4b1214329726855ffc89541f9eec29f3ec Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 15:18:58 +0000 Subject: [PATCH 75/77] 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 a04eedd873..3b1745718c 100644 --- a/volatility3/framework/objects/__init__.py +++ b/volatility3/framework/objects/__init__.py @@ -442,7 +442,7 @@ def dereference( 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 b5ef7248ab1ef5ab5cab9fc5b6d05a88ef26c7bf Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Sat, 11 Mar 2023 15:17:24 +0000 Subject: [PATCH 76/77] 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 f4bfd347ee..7b03d8c87d 100644 --- a/volatility3/framework/plugins/linux/psscan.py +++ b/volatility3/framework/plugins/linux/psscan.py @@ -135,6 +135,7 @@ def scan_tasks( 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 @@ -145,7 +146,8 @@ def scan_tasks( 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 1b5a6b626733a289ae088e92a292d259c3e1931f Mon Sep 17 00:00:00 2001 From: Mike Auty Date: Wed, 12 Apr 2023 19:57:15 +0100 Subject: [PATCH 77/77] 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 502e26f104..471735af8b 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ The latest generated copy of the documentation can be found at: