diff --git a/FreeBSD-Support-README.md b/FreeBSD-Support-README.md new file mode 100644 index 00000000..6fd76a79 --- /dev/null +++ b/FreeBSD-Support-README.md @@ -0,0 +1,383 @@ +# FreeBSD Support + +Author: Antoine Brodin from [SEKOIA](https://www.sekoia.fr/) + +## Previous work + +[Creating Volatility Support for FreeBSD](https://scholarworks.uno.edu/td/2033/) was published in 2015, however the code was never released. + +## Acquiring a memory image + +If the target system is a virtual machine, it's possible to take a snapshot and acquire the VM memory. + +If the target system is bare metal and crashed, a legacy memory dump can be done (sysctl debug.minidump=0 ; call doadump in ddb). + +If the target system is bare metal and running, a [tool](tools/freebsd/dumpmem) was developped to acquire a memory image in the lime format. + +```console +# ./dumpmem FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime +Dumping 0x0 -> 0x9fbff +Dumping 0x100000 -> 0x3ffeffff +``` + +## Creating a volatility profile + +### Prerequisites + +On a system with the same kernel as the target system, kernel sources, dwarfdump and zip must be installed. + +### Profile creation + +```console +% cd tools/freebsd +% make +``` + +This will create a zip file that can be put in the `volatility/plugins/overlays/freebsd` directory. + +## Plugins + +### freebsd\_version + +This is the first plugin I wrote, just to verify I could retrieve a string from memory. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_version +Volatility Foundation Volatility Framework 2.6.1 +Version +FreeBSD 12.1-PRERELEASE r352266 GENERIC +``` + +### freebsd\_ifconfig + +This plugin retrieves network interfaces, along with IP and IPv6 addresses. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_ifconfig +Volatility Foundation Volatility Framework 2.6.1 +Driver name Interface name Addresses +em em0 10.0.2.15 +lo lo0 ::1 fe80:2::1 127.0.0.1 +``` + +### freebsd\_lskld + +This plugin displays status of dynamic kernel linker. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_lskld +Volatility Foundation Volatility Framework 2.6.1 +Id Refs Address Size Name + 1 17 0xffffffff80200000 38046600 /boot/kernel/kernel + 2 1 0xffffffff82649000 3840616 /boot/kernel/zfs.ko + 3 2 0xffffffff829f3000 42424 /boot/kernel/opensolaris.ko + 4 1 0xffffffff82c19000 9832 /boot/kernel/intpm.ko + 5 1 0xffffffff82c1c000 2896 /boot/kernel/smbus.ko + 6 1 0xffffffff82c1d000 2767 /boot/kernel/mac_ntpd.ko +``` + +### freebsd\_lsmod + +This plugin displays kernel modules. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_lsmod +Volatility Foundation Volatility Framework 2.6.1 +Id Name Kld + 1 opensolaris /boot/kernel/opensolaris.ko + 2 zfsctrl /boot/kernel/zfs.ko + 3 zfs /boot/kernel/zfs.ko + 4 zfs_zvol /boot/kernel/zfs.ko + 5 zfs_vdev /boot/kernel/zfs.ko + 6 xpt /boot/kernel/kernel + 7 cam /boot/kernel/kernel + 8 aprobe /boot/kernel/kernel + 9 pmp /boot/kernel/kernel + 10 nda /boot/kernel/kernel +... +``` + +### freebsd\_lsof + +This plugin lists processes and open files. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_lsof +Volatility Foundation Volatility Framework 2.6.1 +Pid Name File number File type Vnode type Socket type Address family Protocol Path Device +779 dumpmem 0 DTYPE_VNODE VCHR ttyv0 +779 dumpmem 1 DTYPE_VNODE VCHR ttyv0 +779 dumpmem 2 DTYPE_VNODE VCHR ttyv0 +779 dumpmem 3 DTYPE_VNODE VREG +779 dumpmem 4 DTYPE_VNODE VCHR mem +772 systat 0 DTYPE_VNODE VCHR ttyv2 +772 systat 1 DTYPE_VNODE VCHR ttyv2 +772 systat 2 DTYPE_VNODE VCHR ttyv2 +772 systat 3 DTYPE_VNODE VCHR null +772 systat 4 DTYPE_VNODE VCHR null +768 tcsh 15 DTYPE_VNODE VCHR ttyv2 +768 tcsh 16 DTYPE_VNODE VCHR ttyv2 +768 tcsh 17 DTYPE_VNODE VCHR ttyv2 +768 tcsh 18 DTYPE_VNODE VCHR ttyv2 +768 tcsh 19 DTYPE_VNODE VCHR ttyv2 +767 top 0 DTYPE_VNODE VCHR ttyv1 +767 top 1 DTYPE_VNODE VCHR ttyv1 +767 top 2 DTYPE_VNODE VCHR ttyv1 +767 top 3 DTYPE_VNODE VCHR null +767 top 4 DTYPE_VNODE VCHR null +763 tcsh 15 DTYPE_VNODE VCHR ttyv1 +763 tcsh 16 DTYPE_VNODE VCHR ttyv1 +763 tcsh 17 DTYPE_VNODE VCHR ttyv1 +763 tcsh 18 DTYPE_VNODE VCHR ttyv1 +763 tcsh 19 DTYPE_VNODE VCHR ttyv1 +760 csh 15 DTYPE_VNODE VCHR ttyv0 +760 csh 16 DTYPE_VNODE VCHR ttyv0 +760 csh 17 DTYPE_VNODE VCHR ttyv0 +760 csh 18 DTYPE_VNODE VCHR ttyv0 +760 csh 19 DTYPE_VNODE VCHR ttyv0 +759 getty 0 DTYPE_VNODE VCHR ttyv7 +759 getty 1 DTYPE_VNODE VCHR ttyv7 +759 getty 2 DTYPE_VNODE VCHR ttyv7 +758 getty 0 DTYPE_VNODE VCHR ttyv6 +758 getty 1 DTYPE_VNODE VCHR ttyv6 +758 getty 2 DTYPE_VNODE VCHR ttyv6 +757 getty 0 DTYPE_VNODE VCHR ttyv5 +757 getty 1 DTYPE_VNODE VCHR ttyv5 +757 getty 2 DTYPE_VNODE VCHR ttyv5 +756 getty 0 DTYPE_VNODE VCHR ttyv4 +756 getty 1 DTYPE_VNODE VCHR ttyv4 +756 getty 2 DTYPE_VNODE VCHR ttyv4 +755 getty 0 DTYPE_VNODE VCHR ttyv3 +755 getty 1 DTYPE_VNODE VCHR ttyv3 +755 getty 2 DTYPE_VNODE VCHR ttyv3 +754 login 0 DTYPE_VNODE VCHR ttyv2 +754 login 1 DTYPE_VNODE VCHR ttyv2 +754 login 2 DTYPE_VNODE VCHR ttyv2 +754 login 3 DTYPE_SOCKET SOCK_DGRAM AF_UNIX +753 login 0 DTYPE_VNODE VCHR ttyv1 +753 login 1 DTYPE_VNODE VCHR ttyv1 +753 login 2 DTYPE_VNODE VCHR ttyv1 +753 login 3 DTYPE_SOCKET SOCK_DGRAM AF_UNIX +752 login 0 DTYPE_VNODE VCHR ttyv0 +752 login 1 DTYPE_VNODE VCHR ttyv0 +752 login 2 DTYPE_VNODE VCHR ttyv0 +752 login 3 DTYPE_SOCKET SOCK_DGRAM AF_UNIX +702 cron 0 DTYPE_VNODE VCHR null +702 cron 1 DTYPE_VNODE VCHR null +702 cron 2 DTYPE_VNODE VCHR null +702 cron 3 DTYPE_VNODE VDIR /var/run +702 cron 4 DTYPE_VNODE VREG /var/run/cron.pid +698 sendmail 0 DTYPE_VNODE VCHR null +698 sendmail 1 DTYPE_VNODE VCHR null +698 sendmail 2 DTYPE_VNODE VCHR null +698 sendmail 3 DTYPE_SOCKET SOCK_DGRAM AF_UNIX +698 sendmail 4 DTYPE_VNODE VREG +695 sendmail 0 DTYPE_VNODE VCHR null +695 sendmail 1 DTYPE_VNODE VCHR null +695 sendmail 2 DTYPE_VNODE VCHR null +695 sendmail 3 DTYPE_SOCKET SOCK_STREAM AF_INET TCP +... +``` + +### freebsd\_mount + +This plugin lists currently mounted file systems. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_mount +Volatility Foundation Volatility Framework 2.6.1 +Special device Mount point Type +zroot/ROOT/default / zfs +devfs /dev devfs +zroot/tmp /tmp zfs +zroot /zroot zfs +zroot/usr/home /usr/home zfs +zroot/usr/ports /usr/ports zfs +zroot/usr/src /usr/src zfs +zroot/var/audit /var/audit zfs +zroot/var/log /var/log zfs +zroot/var/crash /var/crash zfs +zroot/var/mail /var/mail zfs +zroot/var/tmp /var/tmp zfs +``` + +### freebsd\_proc\_maps + +This plugin displays processes and virtual memory mappings. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_proc_maps +Volatility Foundation Volatility Framework 2.6.1 +Pid Name Start End Perms Type Path +779 dumpmem 0x200000 0x21d000 r-- OBJT_VNODE /usr/home/freebsd/freebsd/dumpmem/dumpmem +779 dumpmem 0x21d000 0x28e000 r-x OBJT_VNODE /usr/home/freebsd/freebsd/dumpmem/dumpmem +779 dumpmem 0x28e000 0x293000 rw- OBJT_VNODE /usr/home/freebsd/freebsd/dumpmem/dumpmem +779 dumpmem 0x293000 0x4ae000 rw- OBJT_DEFAULT +779 dumpmem 0x80028e000 0x8003b0000 rw- OBJT_DEFAULT +779 dumpmem 0x800400000 0x800a00000 rw- OBJT_DEFAULT +779 dumpmem 0x7fffdffff000 0x7ffffffdf000 --- NONE +779 dumpmem 0x7ffffffdf000 0x7ffffffff000 rw- OBJT_DEFAULT +779 dumpmem 0x7ffffffff000 0x800000000000 r-x OBJT_PHYS +772 systat 0x200000 0x207000 r-- OBJT_VNODE /usr/bin/systat +772 systat 0x207000 0x218000 r-x OBJT_VNODE /usr/bin/systat +772 systat 0x218000 0x219000 rw- OBJT_VNODE /usr/bin/systat +772 systat 0x219000 0x21a000 r-- OBJT_DEFAULT +772 systat 0x21a000 0x235000 rw- OBJT_DEFAULT +772 systat 0x800218000 0x800221000 r-- OBJT_VNODE /libexec/ld-elf.so.1 +772 systat 0x800221000 0x80023b000 r-x OBJT_VNODE /libexec/ld-elf.so.1 +772 systat 0x80023b000 0x80023c000 rw- OBJT_VNODE /libexec/ld-elf.so.1 +772 systat 0x80023c000 0x80025f000 rw- OBJT_DEFAULT +772 systat 0x80025f000 0x800288000 r-- OBJT_VNODE /lib/libncursesw.so.8 +772 systat 0x800288000 0x8002ba000 r-x OBJT_VNODE /lib/libncursesw.so.8 +772 systat 0x8002ba000 0x8002bb000 rw- OBJT_VNODE /lib/libncursesw.so.8 +... +``` + +### freebsd\_psaux + +This plugin lists processes, executable path and arguments. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_psaux +Volatility Foundation Volatility Framework 2.6.1 +Pid Name Pathname Arguments +779 dumpmem /usr/home/freebsd/freebsd/dumpmem/dumpmem ./dumpmem FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime +772 systat /usr/bin/systat systat -if +768 tcsh /bin/tcsh -tcsh +767 top /usr/bin/top top +763 tcsh /bin/tcsh -tcsh +760 csh /bin/tcsh -csh +759 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv7 +758 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv6 +757 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv5 +756 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv4 +755 getty /usr/libexec/getty /usr/libexec/getty Pc ttyv3 +754 login /usr/bin/login login [pam] +753 login /usr/bin/login login [pam] +752 login /usr/bin/login login [pam] +702 cron /usr/sbin/cron /usr/sbin/cron -s +698 sendmail /usr/libexec/sendmail/sendmail sendmail: Queue runner@00:30:00 for /var/spool/clientmqueue +695 sendmail /usr/libexec/sendmail/sendmail sendmail: accepting connections +692 sshd /usr/sbin/sshd /usr/sbin/sshd +662 ntpd /usr/sbin/ntpd /usr/sbin/ntpd -p /var/db/ntp/ntpd.pid -c /etc/ntp.conf -f /var/db/ntp/ntpd.drift +489 syslogd /usr/sbin/syslogd /usr/sbin/syslogd -ss +418 devd /sbin/devd /sbin/devd +417 dhclient /sbin/dhclient dhclient: em0 +372 dhclient /sbin/dhclient dhclient: em0 [priv] +369 dhclient /sbin/dhclient dhclient: system.syslog +... +``` + +### freebsd\_pscred + +This plugin lists processes and credentials (user identification, group identification...). + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_pscred +Volatility Foundation Volatility Framework 2.6.1 +Pid Name Euid Ruid Svuid Egid Rgid Svgid Umask Flags Groups +779 dumpmem 0 0 0 0 0 0 022 - 0,0,5 +772 systat 1001 1001 1001 1001 1001 1001 022 - 1001,0 +768 tcsh 1001 1001 1001 1001 1001 1001 022 - 1001,0 +767 top 1001 1001 1001 1001 1001 1001 022 - 1001,0 +763 tcsh 1001 1001 1001 1001 1001 1001 022 - 1001,0 +760 csh 0 0 0 0 0 0 022 - 0,0,5 +759 getty 0 0 0 0 0 0 022 - 0 +758 getty 0 0 0 0 0 0 022 - 0 +757 getty 0 0 0 0 0 0 022 - 0 +756 getty 0 0 0 0 0 0 022 - 0 +755 getty 0 0 0 0 0 0 022 - 0 +754 login 0 0 0 1001 1001 1001 022 - 1001,0 +753 login 0 0 0 1001 1001 1001 022 - 1001,0 +752 login 0 0 0 0 0 0 022 - 0,0,5 +702 cron 0 0 0 0 0 0 022 - 0 +698 sendmail 25 25 25 25 25 25 022 - 25 +695 sendmail 0 0 0 25 0 25 022 - 25 +692 sshd 0 0 0 0 0 0 022 - 0 +662 ntpd 123 123 123 123 123 123 022 - 123 +489 syslogd 0 0 0 0 0 0 022 - 0 +418 devd 0 0 0 0 0 0 022 - 0 +417 dhclient 65 65 65 65 65 65 022 C 65 +372 dhclient 0 0 0 0 0 0 022 - 0 +369 dhclient 0 0 0 0 0 0 022 - 0 +... +``` + +### freebsd\_psenv + +This plugin lists processes and environment variables. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_psenv +Volatility Foundation Volatility Framework 2.6.1 +Pid Name Environment +779 dumpmem USER=root LOGNAME=root HOME=/root SHELL=/bin/csh BLOCKSIZE=K MAIL=/var/mail/root PATH=/s... SHLVL=1 PWD=/home/freebsd/freebsd/dumpmem GROUP=wheel HOST=freebsd EDITOR=vi PAGER=less +772 systat USER=freebsd LOGNAME=freebsd HOME=/home/freebsd SHELL=/bin/tcsh BLOCKSIZE=K MAIL=/var/ma...ACHTYPE=x86_64 SHLVL=1 PWD=/home/freebsd GROUP=freebsd HOST=freebsd EDITOR=vi PAGER=less +768 tcsh USER=freebsd LOGNAME=freebsd HOME=/home/freebsd SHELL=/bin/tcsh BLOCKSIZE=K MAIL=/var/ma...sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/freebsd/bin TERM=xterm +767 top USER=freebsd LOGNAME=freebsd HOME=/home/freebsd SHELL=/bin/tcsh BLOCKSIZE=K MAIL=/var/ma...ACHTYPE=x86_64 SHLVL=1 PWD=/home/freebsd GROUP=freebsd HOST=freebsd EDITOR=vi PAGER=less +763 tcsh USER=freebsd LOGNAME=freebsd HOME=/home/freebsd SHELL=/bin/tcsh BLOCKSIZE=K MAIL=/var/ma...sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/freebsd/bin TERM=xterm +760 csh USER=root LOGNAME=root HOME=/root SHELL=/bin/csh BLOCKSIZE=K MAIL=/var/mail/root PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/root/bin TERM=xterm +759 getty TERM=xterm +758 getty TERM=xterm +757 getty TERM=xterm +756 getty TERM=xterm +755 getty TERM=xterm +... +``` + +### freebsd\_pslist + +This plugin lists processes. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_pslist +Volatility Foundation Volatility Framework 2.6.1 +Offset (V) Pid Name +0xfffff800035e9530 779 dumpmem +0xfffff80025e13000 772 systat +0xfffff80025e13530 768 tcsh +0xfffff80025e13a60 767 top +0xfffff8002545e000 763 tcsh +0xfffff8002545ea60 760 csh +0xfffff80003fb7a60 759 getty +0xfffff800035e9a60 758 getty +0xfffff800035e9000 757 getty +0xfffff80003fb7530 756 getty +0xfffff80003fb7000 755 getty +... +``` + +### freebsd\_yarascan + +This plugin is modeled after the `linux_yarascan` and `mac_yarascan` plugins. It scans memory for yara signatures. + +```console +% python2.7 vol.py -f ../FreeBSD-12.1-PRERELEASE-GENERIC-amd64.lime --profile FreeBSD-12_1-PRERELEASE-GENERIC-amd64 freebsd_yarascan -A -Y "usr/src/sys/kern/subr_capability" -s 32 +Volatility Foundation Volatility Framework 2.6.1 +[kernel] rule r1 addr 0xf8001cc8eb6e +0x0000f8001cc8eb6e 75 73 72 2f 73 72 63 2f 73 79 73 2f 6b 65 72 6e usr/src/sys/kern +0x0000f8001cc8eb7e 2f 73 75 62 72 5f 63 61 70 61 62 69 6c 69 74 79 /subr_capability +[kernel] rule r1 addr 0xf8001da7ea33 +0x0000f8001da7ea33 75 73 72 2f 73 72 63 2f 73 79 73 2f 6b 65 72 6e usr/src/sys/kern +0x0000f8001da7ea43 2f 73 75 62 72 5f 63 61 70 61 62 69 6c 69 74 79 /subr_capability +Proc: systat pid 772 rule r1 addr 0x800368b6e +0x0000000800368b6e 75 73 72 2f 73 72 63 2f 73 79 73 2f 6b 65 72 6e usr/src/sys/kern +0x0000000800368b7e 2f 73 75 62 72 5f 63 61 70 61 62 69 6c 69 74 79 /subr_capability +Proc: tcsh pid 768 rule r1 addr 0x800372b6e +0x0000000800372b6e 75 73 72 2f 73 72 63 2f 73 79 73 2f 6b 65 72 6e usr/src/sys/kern +0x0000000800372b7e 2f 73 75 62 72 5f 63 61 70 61 62 69 6c 69 74 79 /subr_capability +Proc: top pid 767 rule r1 addr 0x800368b6e +0x0000000800368b6e 75 73 72 2f 73 72 63 2f 73 79 73 2f 6b 65 72 6e usr/src/sys/kern +0x0000000800368b7e 2f 73 75 62 72 5f 63 61 70 61 62 69 6c 69 74 79 /subr_capability +Proc: tcsh pid 763 rule r1 addr 0x800372b6e +0x0000000800372b6e 75 73 72 2f 73 72 63 2f 73 79 73 2f 6b 65 72 6e usr/src/sys/kern +0x0000000800372b7e 2f 73 75 62 72 5f 63 61 70 61 62 69 6c 69 74 79 /subr_capability +Proc: csh pid 760 rule r1 addr 0x800372b6e +0x0000000800372b6e 75 73 72 2f 73 72 63 2f 73 79 73 2f 6b 65 72 6e usr/src/sys/kern +0x0000000800372b7e 2f 73 75 62 72 5f 63 61 70 61 62 69 6c 69 74 79 /subr_capability +Proc: getty pid 759 rule r1 addr 0x8002a9b6e +0x00000008002a9b6e 75 73 72 2f 73 72 63 2f 73 79 73 2f 6b 65 72 6e usr/src/sys/kern +0x00000008002a9b7e 2f 73 75 62 72 5f 63 61 70 61 62 69 6c 69 74 79 /subr_capability +... +``` + diff --git a/tools/freebsd/Makefile b/tools/freebsd/Makefile new file mode 100644 index 00000000..b8e314f6 --- /dev/null +++ b/tools/freebsd/Makefile @@ -0,0 +1,42 @@ + +UNAME_I!= /usr/bin/uname -i +UNAME_M!= /usr/bin/uname -m +UNAME_R!= /usr/bin/uname -r +UNAME_S!= /usr/bin/uname -s +BOOTFILE!= /sbin/sysctl -n kern.bootfile +CLEANFILES= freebsd-kernel.map ${KMOD}.dwarf ${UNAME_S}-${UNAME_R}-${UNAME_I}-${UNAME_M}.zip + +SRCS= module.c vnode_if.h +KMOD= module +DEBUG_FLAGS= -g +SYSDIR?= /usr/src/sys +PAE!= /sbin/sysctl -i -n kern.features.pae +PAEMODE!= /sbin/sysctl -n vm.pmap.pae_mode 2>/dev/null || echo nope +VIMAGE!= /sbin/sysctl -i -n kern.features.vimage + +.if ${PAE} == 1 +CFLAGS+= -DPAE=1 +.endif + +.if ${PAEMODE} == 0 +CFLAGS+= -DPAEMODE=0 +.elif ${PAEMODE} == 1 +CFLAGS+= -DPAEMODE=1 +.endif + +.if ${VIMAGE} == 1 +CFLAGS+= -DVIMAGE=1 +.endif + +${KMOD}.dwarf: ${KMOD}.ko.debug + /usr/local/bin/dwarfdump -di ${KMOD}.ko.debug > ${.TARGET} + +freebsd-kernel.map: ${BOOTFILE} + /usr/bin/nm ${BOOTFILE} > ${.TARGET} + +${UNAME_S}-${UNAME_R}-${UNAME_I}-${UNAME_M}.zip: freebsd-kernel.map ${KMOD}.dwarf + /usr/local/bin/zip -r ${.TARGET} freebsd-kernel.map ${KMOD}.dwarf + +all: ${UNAME_S}-${UNAME_R}-${UNAME_I}-${UNAME_M}.zip + +.include diff --git a/tools/freebsd/dumpmem/Makefile b/tools/freebsd/dumpmem/Makefile new file mode 100644 index 00000000..56496595 --- /dev/null +++ b/tools/freebsd/dumpmem/Makefile @@ -0,0 +1,6 @@ + +PROG= dumpmem +MAN= +LDADD= -static -ldevinfo + +.include diff --git a/tools/freebsd/dumpmem/dumpmem.c b/tools/freebsd/dumpmem/dumpmem.c new file mode 100644 index 00000000..e93bf0ee --- /dev/null +++ b/tools/freebsd/dumpmem/dumpmem.c @@ -0,0 +1,157 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Antoine Brodin + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Dump memory, this was successfully tested on: + * - amd64 + * - i386 with less than 4GB of ram + * - i386 with more than 4GB of ram after https://svnweb.freebsd.org/changeset/base/343667 or https://svnweb.freebsd.org/changeset/base/350856 + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +static void usage(void); +static int find_io_memory_rman(struct devinfo_rman *, void *); +static int find_ram0_rman_resource(struct devinfo_res *, void *); +static int write_lime_header(int, uint64_t, uint64_t); +static int write_memory_segment(int, off_t, size_t); + +struct lime_header { + uint32_t magic; + uint32_t version; + uint64_t start; + uint64_t end; + uint64_t padding; +} __packed; + +#define BUFSIZE 1024 * 1024 + +int +main(int argc, char *argv[]) +{ + int fd, rv; + + if (argc != 2) + usage(); + if (strcmp(argv[1], "-") == 0) + fd = STDOUT_FILENO; + else if ((fd = open(argv[1], O_CREAT | O_WRONLY | O_TRUNC, 0600)) == -1) + err(EX_CANTCREAT, "%s", argv[1]); + if ((rv = devinfo_init()) != 0) { + errno = rv; + err(EX_SOFTWARE, "devinfo_init"); + } + devinfo_foreach_rman(find_io_memory_rman, &fd); + devinfo_free(); + if (fd != STDOUT_FILENO) + close(fd); + exit(EX_OK); +} + +static void +usage(void) +{ + + fprintf(stderr, "usage: %s file.lime|-\n", getprogname()); + exit(EX_USAGE); +} + +static int +find_io_memory_rman(struct devinfo_rman *rman, void *arg) +{ + + if (strcmp(rman->dm_desc, "I/O memory addresses") == 0) + devinfo_foreach_rman_resource(rman, find_ram0_rman_resource, arg); + return (0); +} + +static int +find_ram0_rman_resource(struct devinfo_res *res, void *arg) +{ + struct devinfo_dev *dev; + + dev = devinfo_handle_to_device(res->dr_device); + if (dev != NULL && strcmp(dev->dd_name, "ram0") == 0) { + fprintf(stderr, "Dumping 0x%jx -> 0x%jx\n", res->dr_start, res->dr_start + res->dr_size - 1); + write_lime_header(*(int *)arg, res->dr_start, res->dr_start + res->dr_size - 1); + write_memory_segment(*(int *)arg, res->dr_start, res->dr_size); + } + return (0); +} + +static int +write_lime_header(int fd, uint64_t start, uint64_t end) +{ + static struct lime_header h; + + h.magic = 0x4c694d45; + h.version = 1; + h.start = start; + h.end = end; + if (write(fd, &h, sizeof(h)) != sizeof(h)) + err(EX_IOERR, "write_lime_header write"); + return (0); +} + +static int +write_memory_segment(int outfd, off_t offset, size_t size) +{ + static void *buf; + static int memfd; + + if (buf == NULL) + if ((buf = malloc(BUFSIZE)) == NULL) + err(EX_SOFTWARE, "write_memory_segment malloc"); + if (memfd == 0) + if ((memfd = open("/dev/mem", O_RDONLY, 0)) == -1) + err(EX_NOINPUT, "write_memory_segment open"); + if (lseek(memfd, offset, SEEK_SET) != offset) + err(EX_SOFTWARE, "write_memory_segment lseek"); + while (size > 0) { + ssize_t nbytes; + + nbytes = MIN(BUFSIZE, size); + if (read(memfd, buf, nbytes) != nbytes) + err(EX_IOERR, "write_memory_segment read"); + if (write(outfd, buf, nbytes) != nbytes) + err(EX_IOERR, "write_memory_segment write"); + size -= nbytes; + } + return (0); +} + diff --git a/tools/freebsd/module.c b/tools/freebsd/module.c new file mode 100644 index 00000000..7b607755 --- /dev/null +++ b/tools/freebsd/module.c @@ -0,0 +1,143 @@ +/*- + * This file is in the public domain. + */ + +#include + +#include +#include /* cdev */ +#include /* ps_strings */ +#include /* filedesc, fdescenttbl, filedescent */ +#include /* file */ +#include /* linker_file */ +#include /* modeventhand_t, modspecific_t */ +#include /* mntlist, mount */ +#include /* pgrp, proc, proclist, session, thread */ +#include +#include /* sockaddr */ +#include /* socket */ +#include /* domain */ +#include /* protosw */ +#include /* sockaddr_un */ +#include /* unpcb */ +#include /* sysentvec */ +#include /* tty */ +#include /* ucred */ +#include /* vnode */ + +#include +#include /* ifnethead, ifnet, ifaddrhead, ifaddr */ +#include /* vnet */ + +#include /* sockaddr_in, sockaddr_in6 */ +#include /* inpcb */ + +#include +#include +#if defined(__i386__) +#if defined(PAEMODE) && PAEMODE == 0 +#define PMTYPE pmap_nopae_ +#include +#elif defined(PAEMODE) && PAEMODE == 1 +#define PMTYPE pmap_pae_ +#include +#endif +#endif +#include /* pmap */ +#include /* vm_map_entry, vmspace */ +#include /* vm_object */ + +#if defined(__amd64__) +#include /* freebsd32_ps_strings */ +#endif + +struct cdev vol_cdev; + +struct ps_strings vol_ps_strings; +#if defined(__amd64__) +struct freebsd32_ps_strings vol_freebsd32_ps_strings; +#endif + +struct fdescenttbl vol_fdescenttbl; +struct filedesc vol_filedesc; +struct filedescent vol_filedescent; + +struct file vol_file; + +struct linker_file vol_linker_file; +TAILQ_HEAD(linker_file_head, linker_file); +struct linker_file_head vol_linker_file_head; + +struct module { + TAILQ_ENTRY(module) link; + TAILQ_ENTRY(module) flink; + struct linker_file *file; + int refs; + int id; + char *name; + modeventhand_t handler; + void *arg; + modspecific_t data; +}; +struct module vol_module; +TAILQ_HEAD(modulelist, module); +struct modulelist vol_modulelist; + +struct mntlist vol_mntlist; +struct mount vol_mount; + +struct pgrp vol_pgrp; +struct proc vol_proc; +struct proclist vol_proclist; +struct session vol_session; +struct thread vol_thread; + +struct sockaddr vol_sockaddr; +struct socket vol_socket; +struct domain vol_domain; +struct protosw vol_protosw; +struct sockaddr_un vol_sockaddr_un; +struct unpcb vol_unpcb; + +struct sysentvec vol_sysentvec; + +struct tty vol_tty; + +struct ucred vol_ucred; + +struct vnode vol_vnode; + +struct namecache { + LIST_ENTRY(namecache) nc_hash; + LIST_ENTRY(namecache) nc_src; + TAILQ_ENTRY(namecache) nc_dst; + struct vnode *nc_dvp; + union { + struct vnode *nu_vp; + u_int nu_neghits; + } n_un; + u_char nc_flag; + u_char nc_nlen; + char nc_name[0]; +}; +struct namecache vol_namecache; + +struct ifnethead vol_ifnethead; +struct ifnet vol_ifnet; +struct ifaddrhead vol_ifaddrhead; +struct ifaddr vol_ifaddr; + +struct vnet vol_vnet; +#if defined(VIMAGE) +struct vnet_list_head vol_vnet_list_head; +#endif + +struct sockaddr_in vol_sockaddr_in; +struct sockaddr_in6 vol_sockaddr_in6; +struct inpcb vol_inpcb; + +struct pmap vol_pmap; +struct vm_map_entry vol_vm_map_entry; +struct vmspace vol_vmspace; +struct vm_object vol_vm_object; + diff --git a/volatility/dwarf.py b/volatility/dwarf.py index 89feb713..55c38ce4 100644 --- a/volatility/dwarf.py +++ b/volatility/dwarf.py @@ -43,13 +43,16 @@ class DWARFParser(object): 'long long int': 'long long', 'long long unsigned int': 'unsigned long long', 'long unsigned int': 'unsigned long', + 'short': 'short', 'short int': 'short', 'short unsigned int': 'unsigned short', - 'signed char': 'signed char', + 'signed char': 'char', 'unsigned char': 'unsigned char', 'unsigned int': 'unsigned int', + 'unsigned short': 'unsigned short', 'sizetype' : 'unsigned long', 'ssizetype' : 'long', + '__ARRAY_SIZE_TYPE__': 'unsigned long', } diff --git a/volatility/plugins/freebsd/__init__.py b/volatility/plugins/freebsd/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/volatility/plugins/freebsd/common.py b/volatility/plugins/freebsd/common.py new file mode 100644 index 00000000..e5e6cc99 --- /dev/null +++ b/volatility/plugins/freebsd/common.py @@ -0,0 +1,43 @@ +# 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.commands as commands +import volatility.utils as utils + +def set_plugin_members(obj_ref): + obj_ref.addr_space = utils.load_as(obj_ref._config) + +class AbstractFreebsdCommand(commands.Command): + def __init__(self, *args, **kwargs): + self.addr_space = None + commands.Command.__init__(self, *args, **kwargs) + + @property + def profile(self): + if self.addr_space: + return self.addr_space.profile + return None + + def execute(self, *args, **kwargs): + commands.Command.execute(self, *args, **kwargs) + + @staticmethod + def is_valid_profile(profile): + return profile.metadata.get('os', 'Unknown').lower() == 'freebsd' + diff --git a/volatility/plugins/freebsd/ifconfig.py b/volatility/plugins/freebsd/ifconfig.py new file mode 100644 index 00000000..daa27bcd --- /dev/null +++ b/volatility/plugins/freebsd/ifconfig.py @@ -0,0 +1,84 @@ +# 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.plugins.freebsd.common as freebsd_common +from volatility.renderers import TreeGrid + +class freebsd_ifconfig(freebsd_common.AbstractFreebsdCommand): + """Display network interfaces""" + + def __init__(self, config, *args, **kwargs): + freebsd_common.AbstractFreebsdCommand.__init__(self, config, *args, **kwargs) + + def walk_ifnethead(self, ifnethead): + if hasattr(ifnethead, 'tqh_first'): + ifnet = ifnethead.tqh_first + else: + ifnet = ifnethead.cstqh_first + while ifnet.v(): + yield ifnet + if hasattr(ifnet.if_link, 'tqe_next'): + ifnet = ifnet.if_link.tqe_next + else: + ifnet = ifnet.if_link.cstqe_next + + def calculate(self): + freebsd_common.set_plugin_members(self) + if self.addr_space.profile.get_symbol('ifnet'): + ifnet_addr = self.addr_space.profile.get_symbol('ifnet') + ifnethead = obj.Object('ifnethead', offset = ifnet_addr, vm = self.addr_space) + for ifnet in self.walk_ifnethead(ifnethead): + yield ifnet + else: + vnet_head_addr = self.addr_space.profile.get_symbol('vnet_head') + vnet_head = obj.Object('vnet_list_head', offset = vnet_head_addr, vm = self.addr_space) + vnet = vnet_head.lh_first + while vnet.v(): + ifnet_addr = self.addr_space.profile.get_symbol('vnet_entry_ifnet') + vnet.vnet_data_base + ifnethead = obj.Object('ifnethead', offset = ifnet_addr, vm = self.addr_space) + for ifnet in self.walk_ifnethead(ifnethead): + yield ifnet + vnet = vnet.vnet_le.le_next + + def unified_output(self, data): + return TreeGrid([('Driver name', str), + ('Interface name', str), + ('Addresses', str)], + self.generator(data)) + + def generator(self, data): + for ifnet in data: + if hasattr(ifnet.if_addrhead, 'tqh_first'): + ifaddr = ifnet.if_addrhead.tqh_first + else: + ifaddr = ifnet.if_addrhead.cstqh_first + addresses = list() + while ifaddr.v(): + if ifaddr.ifa_addr.sa_family == 2: + addresses.append(str(ifaddr.ifa_addr.dereference_as('sockaddr_in').sin_addr.s_addr)) + elif ifaddr.ifa_addr.sa_family == 28: + addresses.append(str(ifaddr.ifa_addr.dereference_as('sockaddr_in6').sin6_addr.__u6_addr)) + if hasattr(ifaddr.ifa_link, 'tqe_next'): + ifaddr = ifaddr.ifa_link.tqe_next + else: + ifaddr = ifaddr.ifa_link.cstqe_next + yield (0, [str(ifnet.if_dname.dereference()), + str(ifnet.if_xname), + ' '.join(addresses)]) diff --git a/volatility/plugins/freebsd/lskld.py b/volatility/plugins/freebsd/lskld.py new file mode 100644 index 00000000..a5f40d88 --- /dev/null +++ b/volatility/plugins/freebsd/lskld.py @@ -0,0 +1,54 @@ +# 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.plugins.freebsd.common as freebsd_common +from volatility.renderers import TreeGrid +from volatility.renderers.basic import Address + +class freebsd_lskld(freebsd_common.AbstractFreebsdCommand): + """Dump kernel linker status""" + + def __init__(self, config, *args, **kwargs): + freebsd_common.AbstractFreebsdCommand.__init__(self, config, *args, **kwargs) + + def calculate(self): + freebsd_common.set_plugin_members(self) + linker_files_addr = self.addr_space.profile.get_symbol('linker_files') + linker_files = obj.Object('linker_file_head', offset = linker_files_addr, vm = self.addr_space) + linker_file = linker_files.tqh_first + while linker_file.v(): + yield linker_file + linker_file = linker_file.link.tqe_next + + def unified_output(self, data): + return TreeGrid([('Id', int), + ('Refs', int), + ('Address', Address), + ('Size', int), + ('Name', str)], + self.generator(data)) + + def generator(self, data): + for linker_file in data: + yield (0, [int(linker_file.id), + int(linker_file.refs), + Address(linker_file.address), + int(linker_file.m('size')), + str(linker_file.pathname.dereference())]) diff --git a/volatility/plugins/freebsd/lsmod.py b/volatility/plugins/freebsd/lsmod.py new file mode 100644 index 00000000..dcc354db --- /dev/null +++ b/volatility/plugins/freebsd/lsmod.py @@ -0,0 +1,50 @@ +# 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.plugins.freebsd.common as freebsd_common +from volatility.renderers import TreeGrid +from volatility.renderers.basic import Address + +class freebsd_lsmod(freebsd_common.AbstractFreebsdCommand): + """Dump kernel modules""" + + def __init__(self, config, *args, **kwargs): + freebsd_common.AbstractFreebsdCommand.__init__(self, config, *args, **kwargs) + + def calculate(self): + freebsd_common.set_plugin_members(self) + modules_addr = self.addr_space.profile.get_symbol('modules') + modules = obj.Object('modulelist', offset = modules_addr, vm = self.addr_space) + module = modules.tqh_first + while module.v(): + yield module + module = module.link.tqe_next + + def unified_output(self, data): + return TreeGrid([('Id', int), + ('Name', str), + ('Kld', str)], + self.generator(data)) + + def generator(self, data): + for module in data: + yield (0, [int(module.id), + str(module.name.dereference()), + str(module.file.pathname.dereference())]) diff --git a/volatility/plugins/freebsd/lsof.py b/volatility/plugins/freebsd/lsof.py new file mode 100644 index 00000000..105b6eb5 --- /dev/null +++ b/volatility/plugins/freebsd/lsof.py @@ -0,0 +1,63 @@ +# 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.plugins.freebsd.common as freebsd_common +import volatility.plugins.freebsd.pslist as freebsd_pslist +import volatility.protos as protos +from volatility.renderers import TreeGrid +from volatility.renderers.basic import Address + +class freebsd_lsof(freebsd_pslist.freebsd_pslist): + """List processes and open files""" + + def calculate(self): + freebsd_common.set_plugin_members(self) + + procs = freebsd_pslist.freebsd_pslist.calculate(self) + + for proc in procs: + for f, n in proc.lsof(): + yield proc, f, n + + def unified_output(self, data): + return TreeGrid([('Pid', int), + ('Name', str), + ('File number', int), + ('File type', str), + ('Vnode type', str), + ('Socket type', str), + ('Address family', str), + ('Protocol', str), + ('Path', str), + ('Device', str)], + self.generator(data)) + + def generator(self, data): + for proc, f, n in data: + + yield (0, [int(proc.p_pid), + str(proc.p_comm), + n, + str(f.f_type), + str(f.f_vnode.v_type) if str(f.f_type) == 'DTYPE_VNODE' else '', + str(f.f_data.dereference_as('socket').so_type) if str(f.f_type) == 'DTYPE_SOCKET' else '', + str(f.f_data.dereference_as('socket').so_proto.pr_domain.dom_family) if str(f.f_type) == 'DTYPE_SOCKET' else '', + protos.protos.get(f.f_data.dereference_as('socket').so_proto.pr_protocol.v(), 'UNKNOWN') if str(f.f_type) == 'DTYPE_SOCKET' and str(f.f_data.dereference_as('socket').so_proto.pr_domain.dom_family).startswith('AF_INET') else '', + f.f_vnode.get_vpath() if str(f.f_type) == 'DTYPE_VNODE' and (str(f.f_vnode.v_type) == 'VDIR' or str(f.f_vnode.v_type) == 'VREG') else '', + str(f.f_vnode.v_un.vu_cdev.si_name) if str(f.f_type) == 'DTYPE_VNODE' and (str(f.f_vnode.v_type) == 'VBLK' or str(f.f_vnode.v_type) == 'VCHR') and hasattr(f.f_vnode, 'v_un') else str(f.f_vnode.v_rdev.si_name) if str(f.f_type) == 'DTYPE_VNODE' and (str(f.f_vnode.v_type) == 'VBLK' or str(f.f_vnode.v_type) == 'VCHR') else '']) diff --git a/volatility/plugins/freebsd/mount.py b/volatility/plugins/freebsd/mount.py new file mode 100644 index 00000000..18d2abda --- /dev/null +++ b/volatility/plugins/freebsd/mount.py @@ -0,0 +1,49 @@ +# 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.plugins.freebsd.common as freebsd_common +from volatility.renderers import TreeGrid + +class freebsd_mount(freebsd_common.AbstractFreebsdCommand): + """Dump list of mounted file systems""" + + def __init__(self, config, *args, **kwargs): + freebsd_common.AbstractFreebsdCommand.__init__(self, config, *args, **kwargs) + + def calculate(self): + freebsd_common.set_plugin_members(self) + mountlist_addr = self.addr_space.profile.get_symbol('mountlist') + mountlist = obj.Object('mntlist', offset = mountlist_addr, vm = self.addr_space) + mount = mountlist.tqh_first + while mount.v(): + yield mount + mount = mount.mnt_list.tqe_next + + def unified_output(self, data): + return TreeGrid([('Special device', str), + ('Mount point', str), + ('Type', str)], + self.generator(data)) + + def generator(self, data): + for mount in data: + yield (0, [str(mount.mnt_stat.f_mntfromname), + str(mount.mnt_stat.f_mntonname), + str(mount.mnt_stat.f_fstypename)]) diff --git a/volatility/plugins/freebsd/proc_maps.py b/volatility/plugins/freebsd/proc_maps.py new file mode 100644 index 00000000..64e37437 --- /dev/null +++ b/volatility/plugins/freebsd/proc_maps.py @@ -0,0 +1,56 @@ +# 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.plugins.freebsd.common as freebsd_common +import volatility.plugins.freebsd.pslist as freebsd_pslist +from volatility.renderers import TreeGrid +from volatility.renderers.basic import Address + +class freebsd_proc_maps(freebsd_pslist.freebsd_pslist): + """List processes and virtual memory mappings""" + + def calculate(self): + freebsd_common.set_plugin_members(self) + + procs = freebsd_pslist.freebsd_pslist.calculate(self) + + for proc in procs: + for entry in proc.get_proc_maps(): + yield proc, entry + + def unified_output(self, data): + return TreeGrid([('Pid', int), + ('Name', str), + ('Start', Address), + ('End', Address), + ('Perms', str), + ('Type', str), + ('Path', str)], + self.generator(data)) + + def generator(self, data): + for proc, entry in data: + + yield (0, [int(proc.p_pid), + str(proc.p_comm), + Address(entry.start), + Address(entry.end), + entry.get_perms(), + entry.get_type(), + entry.get_path()]) diff --git a/volatility/plugins/freebsd/psaux.py b/volatility/plugins/freebsd/psaux.py new file mode 100644 index 00000000..fedf5466 --- /dev/null +++ b/volatility/plugins/freebsd/psaux.py @@ -0,0 +1,38 @@ +# 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.plugins.freebsd.pslist as freebsd_pslist +from volatility.renderers import TreeGrid + +class freebsd_psaux(freebsd_pslist.freebsd_pslist): + """List processes and command line arguments""" + + def unified_output(self, data): + return TreeGrid([('Pid', int), + ('Name', str), + ('Pathname', str), + ('Arguments', str)], + self.generator(data)) + + def generator(self, data): + for proc in data: + yield (0, [int(proc.p_pid), + str(proc.p_comm), + proc.p_textvp.get_vpath() if proc.p_textvp.v() else '', + proc.get_commandline()]) diff --git a/volatility/plugins/freebsd/pscred.py b/volatility/plugins/freebsd/pscred.py new file mode 100644 index 00000000..119948cd --- /dev/null +++ b/volatility/plugins/freebsd/pscred.py @@ -0,0 +1,54 @@ +# 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.plugins.freebsd.pslist as freebsd_pslist +from volatility.renderers import TreeGrid + +class freebsd_pscred(freebsd_pslist.freebsd_pslist): + """List processes and security credentials""" + + def unified_output(self, data): + return TreeGrid([('Pid', int), + ('Name', str), + ('Euid', int), + ('Ruid', int), + ('Svuid', int), + ('Egid', int), + ('Rgid', int), + ('Svgid', int), + ('Umask', str), + ('Flags', str), + ('Groups', str)], + self.generator(data)) + + def generator(self, data): + for proc in data: + groups = proc.p_ucred.cr_groups.dereference_as('Array', targetType = 'int', count = proc.p_ucred.cr_ngroups) + yield (0, [int(proc.p_pid), + str(proc.p_comm), + int(proc.p_ucred.cr_uid), + int(proc.p_ucred.cr_ruid), + int(proc.p_ucred.cr_svuid), + int(groups[0]), + int(proc.p_ucred.cr_rgid), + int(proc.p_ucred.cr_svgid), + '{0:03o}'.format(proc.p_fd.fd_cmask), + 'C' if (proc.p_ucred.cr_flags & 0x1) else '-', + ','.join([str(group) for group in groups])]) diff --git a/volatility/plugins/freebsd/psenv.py b/volatility/plugins/freebsd/psenv.py new file mode 100644 index 00000000..40ff8853 --- /dev/null +++ b/volatility/plugins/freebsd/psenv.py @@ -0,0 +1,36 @@ +# 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.plugins.freebsd.pslist as freebsd_pslist +from volatility.renderers import TreeGrid + +class freebsd_psenv(freebsd_pslist.freebsd_pslist): + """List processes and environment variables""" + + def unified_output(self, data): + return TreeGrid([('Pid', int), + ('Name', str), + ('Environment', str)], + self.generator(data)) + + def generator(self, data): + for proc in data: + yield (0, [int(proc.p_pid), + str(proc.p_comm), + proc.get_environment()]) diff --git a/volatility/plugins/freebsd/pslist.py b/volatility/plugins/freebsd/pslist.py new file mode 100644 index 00000000..fb386915 --- /dev/null +++ b/volatility/plugins/freebsd/pslist.py @@ -0,0 +1,76 @@ +# 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 + +class freebsd_pslist(freebsd_common.AbstractFreebsdCommand): + """List processes""" + + def __init__(self, config, *args, **kwargs): + freebsd_common.AbstractFreebsdCommand.__init__(self, config, *args, **kwargs) + config.add_option('PID', short_option = 'p', default = None, + help = 'Operate on these Process IDs (comma-separated)', + action = 'store', type = 'str') + + @staticmethod + def virtual_process_from_physical_offset(addr_space, offset): + pspace = utils.load_as(addr_space.get_config(), astype = 'physical') + proc = obj.Object('proc', vm = pspace, offset = offset) + thread = obj.Object('thread', vm = addr_space, offset = proc.p_threads.tqh_first) + + return thread.td_proc + + def calculate(self): + freebsd_common.set_plugin_members(self) + + pidlist = self._config.PID + if pidlist: + pidlist = [int(p) for p in self._config.PID.split(',')] + + addr = self.addr_space.profile.get_symbol('allproc') + allproc = obj.Object('proclist', offset = addr, vm = self.addr_space) + proc = allproc.lh_first + while proc.v(): + if not pidlist or proc.p_pid in pidlist: + yield proc + proc = proc.p_list.le_next + + addr = self.addr_space.profile.get_symbol('zombproc') + zombproc = obj.Object('proclist', offset = addr, vm = self.addr_space) + proc = zombproc.lh_first + while proc.v(): + if not pidlist or proc.p_pid in pidlist: + yield proc + proc = proc.p_list.le_next + + def unified_output(self, data): + return TreeGrid([('Offset (V)', Address), + ('Pid', int), + ('Name', str)], + self.generator(data)) + + def generator(self, data): + for proc in data: + yield (0, [Address(proc.v()), + int(proc.p_pid), + str(proc.p_comm)]) diff --git a/volatility/plugins/freebsd/version.py b/volatility/plugins/freebsd/version.py new file mode 100644 index 00000000..9bf6bda0 --- /dev/null +++ b/volatility/plugins/freebsd/version.py @@ -0,0 +1,42 @@ +# 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.plugins.freebsd.common as freebsd_common +from volatility.renderers import TreeGrid + +class freebsd_version(freebsd_common.AbstractFreebsdCommand): + """Dump FreeBSD version""" + + def __init__(self, config, *args, **kwargs): + freebsd_common.AbstractFreebsdCommand.__init__(self, config, *args, **kwargs) + + def calculate(self): + freebsd_common.set_plugin_members(self) + version_addr = self.addr_space.profile.get_symbol('version') + version = obj.Object('String', offset = version_addr, vm = self.addr_space, length = 256) + yield version + + def unified_output(self, data): + return TreeGrid([('Version', str)], + self.generator(data)) + + def generator(self, data): + for version in data: + yield (0, [str(version)]) diff --git a/volatility/plugins/freebsd/yarascan.py b/volatility/plugins/freebsd/yarascan.py new file mode 100644 index 00000000..7440904e --- /dev/null +++ b/volatility/plugins/freebsd/yarascan.py @@ -0,0 +1,121 @@ +# 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.debug as debug +import volatility.utils as utils +import volatility.plugins.freebsd.common as freebsd_common +import volatility.plugins.freebsd.pslist as freebsd_pslist +import volatility.plugins.malware.malfind as malfind +import re + +try: + import yara + has_yara = True +except ImportError: + has_yara = False + +class MapYaraScanner(malfind.BaseYaraScanner): + """A scanner over all memory regions of a process.""" + + def __init__(self, proc = None, **kwargs): + """Scan the process address space through the address map entries. + + Args: + proc: The proc object for this process. + """ + self.proc = proc + malfind.BaseYaraScanner.__init__(self, address_space = proc.get_process_address_space(), **kwargs) + + def scan(self, offset = 0, maxlen = None): + for entry in self.proc.get_proc_maps(): + for match in malfind.BaseYaraScanner.scan(self, entry.start, entry.end - entry.start): + yield match + +class freebsd_yarascan(malfind.YaraScan): + """Scan memory for yara signatures""" + + @staticmethod + def is_valid_profile(profile): + return profile.metadata.get('os', 'Unknown').lower() == 'freebsd' + + def filter_tasks(self): + procs = freebsd_pslist.freebsd_pslist(self._config).calculate() + + if self._config.PID is not None: + try: + pidlist = [int(p) for p in self._config.PID.split(',')] + except ValueError: + debug.error('Invalid PID {0}'.format(self._config.PID)) + + pids = [t for t in procs if t.p_pid in pidlist] + if len(pids) == 0: + debug.error('Cannot find PID {0}.'.format(self._config.PID)) + return pids + + if self._config.NAME is not None: + try: + name_re = re.compile(self._config.NAME, re.I) + except re.error: + debug.error('Invalid name {0}'.format(self._config.NAME)) + + names = [t for t in procs if name_re.search(str(t.p_comm))] + if len(names) == 0: + debug.error('Cannot find name {0}.'.format(self._config.NAME)) + return names + + return procs + + def calculate(self): + + ## we need this module imported + if not has_yara: + debug.error('Please install Yara from https://plusvic.github.io/yara/') + + ## leveraged from the windows yarascan plugin + rules = self._compile_rules() + + ## set the freebsd plugin address spaces + freebsd_common.set_plugin_members(self) + + if self._config.KERNEL or self._config.ALL: + scanner = malfind.DiscontigYaraScanner(rules = rules, + address_space = self.addr_space) + + for hit, address in scanner.scan(): + yield (None, address - self._config.REVERSE, hit, + scanner.address_space.zread(address - self._config.REVERSE, self._config.SIZE)) + if not self._config.KERNEL or self._config.ALL: + # Scan each process memory block + procs = self.filter_tasks() + for proc in procs: + scanner = MapYaraScanner(proc = proc, rules = rules) + for hit, address in scanner.scan(): + yield (proc, address - self._config.REVERSE, hit, + scanner.address_space.zread(address - self._config.REVERSE, self._config.SIZE)) + + def render_text(self, outfd, data): + for proc, address, hit, buf in data: + if proc: + outfd.write('Proc: {0} pid {1} rule {2} addr {3:#x}\n'.format( + proc.p_comm, proc.p_pid, hit.rule, address)) + else: + outfd.write('[kernel] rule {0} addr {1:#x}\n'.format(hit.rule, address)) + + outfd.write(''.join(['{0:#018x} {1:<48} {2}\n'.format( + address + o, h, ''.join(c)) for o, h, c in utils.Hexdump(buf)])) diff --git a/volatility/plugins/overlays/freebsd/__init__.py b/volatility/plugins/overlays/freebsd/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/volatility/plugins/overlays/freebsd/freebsd.py b/volatility/plugins/overlays/freebsd/freebsd.py new file mode 100644 index 00000000..812ad4f5 --- /dev/null +++ b/volatility/plugins/overlays/freebsd/freebsd.py @@ -0,0 +1,547 @@ +# Volatility +# Copyright (C) 2010 Brendan Dolan-Gavitt +# Copyright (c) 2011 Michael Cohen +# 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 Version 2 as +# published by the Free Software Foundation. You may not use, modify or +# distribute this program under any other version of the GNU General +# Public License. +# +# 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 copy +import os +import struct +import zipfile + +import volatility.debug as debug +import volatility.dwarf as dwarf +import volatility.obj as obj +import volatility.plugins as plugins +import volatility.plugins.overlays.basic as basic +import volatility.plugins.overlays.native_types as native_types + +x64_native_types = copy.deepcopy(native_types.x64_native_types) + +x64_native_types['long'] = [8, ' name of the symbol + nm_tyes -> types as defined by 'nm' (man nm for examples) + module -> which module to get the symbol from, default is kernel, otherwise can be any name seen in 'lsmod' + + This fixes a few issues from the old static hash table method: + 1) Conflicting symbols can be handled, if a symbol is found to conflict on any profile, + then the plugin will need to provide the nm_type to differentiate, otherwise the plugin will be errored out + 2) Can handle symbols gathered from modules on disk as well from the static kernel + + symtable is stored as a hash table of: + + symtable[module][sym_name] = [(symbol address, symbol type), (symbol addres, symbol type), ...] + + The function has overly verbose error checking on purpose... + """ + + symtable = self.sys_map + + ret = None + + # check if the module is there... + if module in symtable: + + mod = symtable[module] + + # check if the requested symbol is in the module + if sym_name in mod: + + sym_list = mod[sym_name] + + # if a symbol has multiple definitions, then the plugin needs to specify the type + if len(sym_list) > 1: + if nm_type == '': + debug.debug('Requested symbol {0:s} in module {1:s} has multiple definitions and no type given\n'.format(sym_name, module)) + return None + else: + for (addr, stype) in sym_list: + + if stype == nm_type: + ret = addr + break + + if ret == None: + debug.error('Requested symbol {0:s} in module {1:s} could not be found\n'.format(sym_name, module)) + else: + # get the address of the symbol + ret = sym_list[0][0] + else: + debug.debug('Requested symbol {0:s} not found in module {1:s}\n'.format(sym_name, module)) + else: + debug.info('Requested module {0:s} not found in symbol table\n'.format(module)) + + return ret + + + cls = AbstractFreebsdProfile + cls.__name__ = profilename.replace('.', '_') + + return cls + +################################ +# Track down the zip files +# Push them through the factory +# Check whether ProfileModifications will work + +new_classes = [] + +for path in set(plugins.__path__): + for path, _, files in os.walk(path): + for fn in files: + if zipfile.is_zipfile(os.path.join(path, fn)): + new_classes.append(FreebsdProfileFactory(zipfile.ZipFile(os.path.join(path, fn)))) + +################################ + +class proc(obj.CType): + def get_process_address_space(self): + if self.obj_vm.profile.metadata.get('arch', 'x86') == 'x86' and self.obj_vm.pae: + 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': + 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) + + process_as.name = 'Process {0}'.format(self.p_pid) + + return process_as + + def get_proc_maps(self): + entry = self.p_vmspace.vm_map.header.next + + while entry.v() != self.p_vmspace.vm_map.header.v(): + if entry.eflags & 0x2 == 0: + # Skip MAP_ENTRY_IS_SUB_MAP + yield entry + entry = entry.next + + def psenv(self): + process_as = self.get_process_address_space() + + if self.p_sysent.sv_flags & 0x100 == 0 or self.obj_vm.profile.metadata.get('memory_model', '32bit') == '32bit': + # SV_ILP32 not set or 32bit + ps_strings = obj.Object('ps_strings', offset = self.p_sysent.sv_psstrings, vm = process_as) + ps_envstr = ps_strings.ps_envstr.dereference_as('Array', targetType = 'Pointer', count = ps_strings.ps_nenvstr) + else: + # SV_ILP32 on 64bit + ps_strings = obj.Object('freebsd32_ps_strings', offset = self.p_sysent.sv_psstrings, vm = process_as) + ps_envstr = ps_strings.ps_envstr.dereference_as('Array', targetType = 'Pointer32', count = ps_strings.ps_nenvstr) + + for s in ps_envstr: + vals = s.dereference_as('String', length = 1024) + ents = vals.split('=', 1) + if len(ents) == 2: + yield ents[0], ents[1] + + def get_environment(self): + env = ' '.join(['{0}={1}'.format(key, value) for key, value in self.psenv()]) + + return env + + def get_commandline(self): + process_as = self.get_process_address_space() + + if self.p_sysent.sv_flags & 0x100 == 0 or self.obj_vm.profile.metadata.get('memory_model', '32bit') == '32bit': + # SV_ILP32 not set or 32bit + ps_strings = obj.Object('ps_strings', offset = self.p_sysent.sv_psstrings, vm = process_as) + ps_argvstr = ps_strings.ps_argvstr.dereference_as('Array', targetType = 'Pointer', count = ps_strings.ps_nargvstr) + else: + # SV_ILP32 on 64bit + ps_strings = obj.Object('freebsd32_ps_strings', offset = self.p_sysent.sv_psstrings, vm = process_as) + ps_argvstr = ps_strings.ps_argvstr.dereference_as('Array', targetType = 'Pointer32', count = ps_strings.ps_nargvstr) + + name = ' '.join([str(s.dereference_as('String', length = 1024)) for s in ps_argvstr]) + + return name + + 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 + + +class vm_map_entry(obj.CType): + def get_perms(self): + permask = 'rwx' + perms = '' + + for (ctr, i) in enumerate([1, 3, 5]): + if (self.protection & i) == i: + perms = perms + permask[ctr] + else: + perms = perms + '-' + + return perms + + def get_type(self): + vm_object = self.object.vm_object + + if vm_object.v() == 0: + return 'NONE' + + while vm_object.backing_object.v(): + vm_object = vm_object.backing_object + + return str(vm_object.type) + + def get_path(self): + vm_object = self.object.vm_object + + if vm_object.v() == 0: + return '' + + while vm_object.backing_object.v(): + vm_object = vm_object.backing_object + + if vm_object.type != 2: + return '' + + vnode = vm_object.handle.dereference_as('vnode') + return vnode.get_vpath() + + +class vnode(obj.CType): + def get_vpath(self): + """Lookup pathname of a vnode in the namecache""" + rootvnode_addr = self.obj_vm.profile.get_symbol('rootvnode') + rootvnode = obj.Object('Pointer', offset = rootvnode_addr, vm = self.obj_vm) + vp = self + components = list() + + while vp.v(): + if vp.v() == rootvnode.v(): + if len(components) == 0: + components.insert(0, '/') + else: + components.insert(0, '') + break + + if vp.v_vflag & 0x1 != 0: + # VV_ROOT set + vp = vp.v_mount.mnt_vnodecovered + else: + ncp = vp.v_cache_dst.tqh_first + if ncp.v(): + ncn = obj.Object('String', offset = ncp.nc_name.obj_offset, vm = self.obj_vm, length = ncp.nc_nlen) + components.insert(0, str(ncn)) + vp = ncp.nc_dvp + else: + break + + if components: + return '/'.join(components) + else: + return '' + + +class VolatilityDTB(obj.VolatilityMagic): + """A scanner for DTB values.""" + + def generate_suggestions(self): + """Tries to locate the DTB.""" + profile = self.obj_vm.profile + kernbase = profile.get_symbol('kernbase') + + if profile.get_symbol('IdlePDPT') and profile.get_symbol('tramp_idleptd'): + # PAE after 4/4G split + ret = profile.get_symbol('IdlePDPT') + ret = self.obj_vm.read(ret, 4) + ret = struct.unpack("