diff --git a/tools/freebsd/module.c b/tools/freebsd/module.c index 7b607755e..3a5cf2916 100644 --- a/tools/freebsd/module.c +++ b/tools/freebsd/module.c @@ -58,9 +58,13 @@ struct ps_strings vol_ps_strings; struct freebsd32_ps_strings vol_freebsd32_ps_strings; #endif +#if defined __freeBSD_kernel__ && __FreeBSD_version >= 1100040 struct fdescenttbl vol_fdescenttbl; +#endif struct filedesc vol_filedesc; +#if defined __freeBSD_kernel__ && __FreeBSD_version >= 1000028 struct filedescent vol_filedescent; +#endif struct file vol_file; diff --git a/volatility/plugins/freebsd/tcpconns.py b/volatility/plugins/freebsd/tcpconns.py new file mode 100644 index 000000000..881ba6b61 --- /dev/null +++ b/volatility/plugins/freebsd/tcpconns.py @@ -0,0 +1,68 @@ +# Volatility +# Copyright (C) 2019 Volatility Foundation +# +# This file is part of Volatility. +# +# Volatility is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# Volatility is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Volatility. If not, see . +# + +import volatility.obj as obj +import volatility.utils as utils +import volatility.plugins.freebsd.common as freebsd_common +from volatility.renderers import TreeGrid +from volatility.renderers.basic import Address +import socket + +class freebsd_tcpconns(freebsd_common.AbstractFreebsdCommand): + """List TCP connections""" + + def __init__(self, config, *args, **kwargs): + freebsd_common.AbstractFreebsdCommand.__init__(self, config, *args, **kwargs) + + def calculate(self): + freebsd_common.set_plugin_members(self) + + + tcbinfo_addr = self.addr_space.profile.get_symbol('tcbinfo') + if not tcbinfo_addr: + raise RuntimeError("Unsupported version: don't know where to find the list of connections") + + info = obj.Object('inpcbinfo', offset = tcbinfo_addr, vm = self.addr_space) + c = info.ipi_listhead.dereference().lh_first.dereference().cast("inpcb") + while c.v(): + endpoints = c.inp_inc.inc_ie + local_ip = endpoints.ie_dependladdr.ie46_local.ia46_addr4.s_addr + remote_ip = endpoints.ie_dependfaddr.ie46_foreign.ia46_addr4.s_addr + local_port = endpoints.ie_lport + remote_port = endpoints.ie_fport + + # TCP state should be accessible through c.inp_ppcb.cast("tcpcb").t_state + # but the module.c did not include its definition so we + # don't know how to access it + + c = c.inp_list.le_next + yield (local_ip, remote_ip, socket.htons(local_port), socket.htons(remote_port)) + + def unified_output(self, data): + return TreeGrid([ + ('Local IP', str), + ('Remote IP', str), + ('Local port', int), + ('Remote port', int), + + ], self.generator(data)) + + def generator(self, data): + for (lip, rip, lp, rp) in data: + yield (0, [str(lip), str(rip), int(lp), int(rp)]) diff --git a/volatility/plugins/overlays/freebsd/freebsd.py b/volatility/plugins/overlays/freebsd/freebsd.py index 812ad4f55..96bed8baf 100644 --- a/volatility/plugins/overlays/freebsd/freebsd.py +++ b/volatility/plugins/overlays/freebsd/freebsd.py @@ -26,6 +26,7 @@ import zipfile import volatility.debug as debug +import volatility.exceptions as exceptions import volatility.dwarf as dwarf import volatility.obj as obj import volatility.plugins as plugins @@ -43,9 +44,6 @@ 'IA32ValidAS' : [ 0x0, ['VolatilityFreebsdValidAS']], 'AMD64ValidAS' : [ 0x0, ['VolatilityFreebsdValidAS']], }], - 'cdev' : [ None, { - 'si_name' : [ None, ['String', dict(length = 256)]], - }], 'domain' : [ None, { 'dom_family' : [ None, ['Enumeration', dict(target = 'int', choices = { 1: 'AF_UNIX', @@ -317,11 +315,16 @@ def get_process_address_space(self): process_as = self.obj_vm.__class__(self.obj_vm.base, self.obj_vm.get_config(), dtb = self.obj_vm.vtop(self.p_vmspace.vm_pmap.pm_pdpt), skip_as_check = True) elif self.obj_vm.profile.metadata.get('arch', 'x86') == 'x86': process_as = self.obj_vm.__class__(self.obj_vm.base, self.obj_vm.get_config(), dtb = self.obj_vm.vtop(self.p_vmspace.vm_pmap.pm_pdir), skip_as_check = True) - elif self.obj_vm.profile.metadata.get('arch', 'x86') == 'x64': + # Around 9.3 precomputed cr3 became part of the structure + elif self.obj_vm.profile.metadata.get('arch', 'x86') == 'x64' and hasattr(self.p_vmspace.vm_pmap, "pm_cr3"): if self.p_vmspace.vm_pmap.pm_ucr3 != 0xffffffffffffffffL: process_as = self.obj_vm.__class__(self.obj_vm.base, self.obj_vm.get_config(), dtb = self.p_vmspace.vm_pmap.pm_ucr3, skip_as_check = True) else: process_as = self.obj_vm.__class__(self.obj_vm.base, self.obj_vm.get_config(), dtb = self.p_vmspace.vm_pmap.pm_cr3, skip_as_check = True) + elif self.obj_vm.profile.metadata.get('arch', 'x86') == 'x64' and hasattr(self.p_vmspace.vm_pmap, "pm_pml4"): + process_as = self.obj_vm.__class__(self.obj_vm.base, self.obj_vm.get_config(), dtb = self.obj_vm.vtop(self.p_vmspace.vm_pmap.pm_pml4), skip_as_check = True) + else: + raise RuntimeError('Unknown pmap structure') process_as.name = 'Process {0}'.format(self.p_pid) @@ -377,11 +380,23 @@ def get_commandline(self): def lsof(self): if self.p_fd.fd_lastfile != -1: - files = obj.Object('Array', offset = self.p_fd.fd_files.fdt_ofiles.obj_offset, vm = self.obj_vm, targetType = 'filedescent', count = self.p_fd.fd_lastfile + 1) - for n, f in enumerate(files): - if f.fde_file.v(): - yield f.fde_file, n + # This is the most recent version with a separate table struct for filedescent structs + if hasattr(self.p_fd, "fd_files"): + filedescents = obj.Object('Array', offset = self.p_fd.fd_files.fdt_ofiles.obj_offset, vm = self.obj_vm, targetType = 'filedescent', count = self.p_fd.fd_lastfile + 1) + files = (i.fde_file for i in filedescents) + # In 8.4.0, type of fd_ofiles is `struct file **` + elif hasattr(self.p_fd, "fd_ofiles") \ + and isinstance(self.p_fd.fd_ofiles, obj.Pointer) \ + and isinstance(self.p_fd.fd_ofiles.dereference(), obj.Pointer) \ + and self.p_fd.fd_ofiles.dereference().dereference().obj_type == "file": + fileptrs = obj.Object('Array', offset = self.p_fd.fd_ofiles, vm = self.obj_vm, targetType = 'Pointer', count = self.p_fd.fd_lastfile + 1) + files = (i.dereference_as("file") for i in fileptrs) + else: + raise RuntimeError("Unknown filedesc structure") + for n, f in enumerate(files): + if f: + yield f, n class vm_map_entry(obj.CType): def get_perms(self): @@ -530,6 +545,19 @@ class FreebsdOverlay(obj.ProfileModification): def modification(self, profile): profile.merge_overlay(freebsd_overlay) +class cdev(obj.CType): + + @property + def si_name(self): + orig = self.m("si_name") + # in versions before 9.1.0, this member is a char pointer + if isinstance(orig, obj.Pointer): + return obj.Object("String", offset=orig.v(), vm=self.obj_vm, length=64) + # after that the indirection was removed and the statically allocated + # array is used directly + else: + return obj.Object("String", offset=orig.obj_offset, vm=self.obj_vm, length=64) + class FreebsdObjectClasses(obj.ProfileModification): conditions = {'os': lambda x: x == 'freebsd'} @@ -544,4 +572,5 @@ def modification(self, profile): 'proc': proc, 'vm_map_entry': vm_map_entry, 'vnode': vnode, + 'cdev': cdev, })