diff --git a/volatility/plugins/mac/dump_files.py b/volatility/plugins/mac/dump_files.py
new file mode 100644
index 00000000..16975eb4
--- /dev/null
+++ b/volatility/plugins/mac/dump_files.py
@@ -0,0 +1,85 @@
+# Volatility
+# Copyright (C) 2007-2013 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 .
+#
+
+"""
+@author: Andrew Case
+@license: GNU General Public License 2.0
+@contact: atcuno@gmail.com
+@organization:
+"""
+
+import volatility.obj as obj
+import volatility.debug as debug
+import volatility.plugins.mac.common as common
+import volatility.plugins.mac.list_files as mac_list_files
+
+class mac_dump_file(common.AbstractMacCommand):
+ """ Dumps a specified file """
+
+ def __init__(self, config, *args, **kwargs):
+ common.AbstractMacCommand.__init__(self, config, *args, **kwargs)
+ self._config.add_option('FILE-OFFSET', short_option = 'q', default = None, help = 'Virtual address of vnode structure from mac_list_files', action = 'store', type = 'int')
+ self._config.add_option('OUTFILE', short_option = 'O', default = None, help = 'output file path', action = 'store', type = 'str')
+
+ def calculate(self):
+ common.set_plugin_members(self)
+
+ outfile = self._config.outfile
+ vnode_off = self._config.FILE_OFFSET
+
+ if not outfile:
+ debug.error("You must specify an output file (-O/--outfile)")
+
+ if not vnode_off:
+ debug.error("You must specificy a vnode address (-q/--file-offset) from mac_list_files")
+
+ fd = open(outfile, "wb")
+
+ vnode = obj.Object("vnode", offset = vnode_off, vm = self.addr_space)
+
+ moc = vnode.v_un.vu_ubcinfo.ui_control.moc_object
+ memq = moc.memq
+
+ cur = memq.next.dereference_as("vm_page")
+
+ wrote = 0
+
+ while cur:
+ # I am not 100% sure why pages of other objects end up in the queue, but they do...
+ if cur.object != moc:
+ cur = cur.next
+ continue
+
+ # FIXME -- use the proper load_as call
+ buf = self.addr_space.base.zread(cur.phys_page * 4096, 4096)
+
+ fd.write(buf)
+
+ wrote = wrote + 4096
+
+ cur = cur.next
+
+ fd.close()
+
+ yield vnode_off, outfile, wrote
+
+ def render_text(self, outfd, data):
+ for (vnode_off, outfile, wrote) in data:
+ outfd.write("Wrote {0} bytes to {1} from vnode at address {2:x}\n".format(wrote, outfile, vnode_off))
+
diff --git a/volatility/plugins/mac/list_files.py b/volatility/plugins/mac/list_files.py
new file mode 100644
index 00000000..51dae4c1
--- /dev/null
+++ b/volatility/plugins/mac/list_files.py
@@ -0,0 +1,53 @@
+# Volatility
+# Copyright (C) 2007-2013 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 .
+#
+
+"""
+@author: Andrew Case
+@license: GNU General Public License 2.0
+@contact: atcuno@gmail.com
+@organization:
+"""
+
+import volatility.obj as obj
+import volatility.plugins.mac.common as common
+import volatility.plugins.mac.mount as mac_mount
+
+class mac_list_files(common.AbstractMacCommand):
+ """ Prints mounted device information """
+
+ def calculate(self):
+ common.set_plugin_members(self)
+
+ mounts = mac_mount.mac_mount(self._config).calculate()
+
+ for mount in mounts:
+ vnode = mount.mnt_vnodelist.tqh_first
+
+ while vnode:
+ path = vnode.full_path()
+
+ yield vnode, path
+
+ vnode = vnode.v_mntvnodes.tqe_next
+
+ def render_text(self, outfd, data):
+ self.table_header(outfd, [("Offset (V)", "[addrpad]"), ("File Path", "")])
+ for vnode, path in data:
+ self.table_row(outfd, vnode.v(), path)
+
diff --git a/volatility/plugins/mac/lsmod_iokit.py b/volatility/plugins/mac/lsmod_iokit.py
new file mode 100644
index 00000000..096d53f5
--- /dev/null
+++ b/volatility/plugins/mac/lsmod_iokit.py
@@ -0,0 +1,71 @@
+# Volatility
+# Copyright (C) 2007-2013 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 .
+#
+
+"""
+@author: Andrew Case
+@license: GNU General Public License 2.0
+@contact: atcuno@gmail.com
+@organization:
+"""
+
+import volatility.obj as obj
+import volatility.plugins.mac.common as common
+
+class mac_lsmod_iokit(common.AbstractMacCommand):
+ """ Lists loaded kernel modules through IOkit """
+
+ def calculate(self):
+ common.set_plugin_members(self)
+
+ saddr = common.get_cpp_sym("sLoadedKexts", self.addr_space.profile)
+
+ p = obj.Object("Pointer", offset = saddr, vm = self.addr_space)
+
+ kOSArr = obj.Object("OSArray_class", offset = p, vm = self.addr_space)
+
+ kext_arr = obj.Object(theType = "Array", targetType = "Pointer", offset = kOSArr.array, count = kOSArr.capacity, vm = self.addr_space)
+
+ for (i, kext) in enumerate(kext_arr):
+ kext = kext.dereference_as("OSKext_class")
+ if kext and kext.is_valid():
+ yield kext
+
+ def render_text(self, outfd, data):
+ self.table_header(outfd, [("Address", "[addrpad]"),
+ ("Size", "8"),
+ ("Refs", "^8"),
+ ("Version", "12"),
+ ("Name", "48"),
+ ("Path", "")])
+ for kext in data:
+ path = kext.path
+
+ if path:
+ path = str(path.dereference())
+
+ self.table_row(outfd,
+ kext.kmod_info.address,
+ kext.kmod_info.m("size"),
+ kext.kmod_info.reference_count,
+ kext.version,
+ kext.kmod_info.name,
+ str(path))
+
+
+
diff --git a/volatility/plugins/mac/netconns.py b/volatility/plugins/mac/netconns.py
new file mode 100644
index 00000000..c468a3d7
--- /dev/null
+++ b/volatility/plugins/mac/netconns.py
@@ -0,0 +1,98 @@
+# Volatility
+# Copyright (C) 2007-2013 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 .
+#
+
+"""
+@author: Andrew Case
+@license: GNU General Public License 2.0
+@contact: atcuno@gmail.com
+@organization:
+"""
+
+import volatility.obj as obj
+import volatility.plugins.mac.common as common
+
+class mac_network_conns(common.AbstractMacCommand):
+ """ Lists network connections from kernel network structures """
+
+ # in_pcblookup_hash - bsd/netinet/in_pcb.c
+ def _walk_pcb_hash(self, proto_pcbinfo):
+ pcb_hash = obj.Object("Array", offset = proto_pcbinfo.hashbase, vm = self.addr_space, targetType = "Pointer", count = proto_pcbinfo.hashmask + 1)
+
+ for pcb_ent in pcb_hash:
+ head = pcb_ent.dereference_as("inpcbhead")
+
+ if not head:
+ continue
+
+ inpcb = head.lh_first.dereference_as("inpcb")
+
+ while inpcb:
+ yield inpcb
+ inpcb = inpcb.inp_hash.le_next
+
+ # in_pcblookup_hash - bsd/netinet/in_pcb.c
+ def _walk_pcb_list(self, proto_pcbinfo):
+ inpcb = proto_pcbinfo.listhead.lh_first.dereference_as("inpcb")
+
+ while inpcb:
+ yield inpcb
+ inpcb = inpcb.inp_list.le_next
+
+ def _walk_pcb_entries(self, inpcbinfo_addr):
+ pcbs = {}
+
+ inpcbinfo = obj.Object("inpcbinfo", offset = inpcbinfo_addr, vm = self.addr_space)
+
+ for pcbinfo in self._walk_pcb_list(inpcbinfo):
+ pcbs[pcbinfo.obj_offset] = pcbinfo
+
+ for pcbinfo in self._walk_pcb_hash(inpcbinfo):
+ pcbs[pcbinfo.obj_offset] = pcbinfo
+
+ for pcbinfo in pcbs.values():
+ (lip, lport, rip, rport) = pcbinfo.ipv4_info()
+ yield (pcbinfo, lip, lport, rip, rport)
+
+ def calculate(self):
+ common.set_plugin_members(self)
+
+ entries = []
+
+ tcbinfo_addr = self.addr_space.profile.get_symbol("_tcbinfo")
+ udbinfo_addr = self.addr_space.profile.get_symbol("_udbinfo")
+ ripdbinfo_addr = self.addr_space.profile.get_symbol("_ripcbinfo")
+
+ info_addrs = [("TCP", tcbinfo_addr), ("UDP", udbinfo_addr), ("RAW", ripdbinfo_addr)]
+
+ for (proto_str, info_addr) in info_addrs:
+ for (pcbinfo, lip, lport, rip, rport) in self._walk_pcb_entries(info_addr):
+ yield (proto_str, pcbinfo, lip, lport, rip, rport)
+
+ def render_text(self, outfd, data):
+ self.table_header(outfd, [("Offset (V)", "[addrpad]"),
+ ("Protocol", "4"),
+ ("Local IP", "20"),
+ ("Local Port", "6"),
+ ("Remote IP", "20"),
+ ("Remote Port", "6"),
+ ])
+
+ for (proto, pcb, lip, lport, rip, rport) in data:
+ self.table_row(outfd, pcb.obj_offset, proto, lip, lport, rip, rport)
+
diff --git a/volatility/plugins/mac/socket_filters.py b/volatility/plugins/mac/socket_filters.py
deleted file mode 100644
index f3348a3b..00000000
--- a/volatility/plugins/mac/socket_filters.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# Volatility
-# Copyright (C) 2007-2013 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 .
-#
-
-"""
-@author: Andrew Case
-@license: GNU General Public License 2.0
-@contact: atcuno@gmail.com
-@organization:
-"""
-
-import volatility.obj as obj
-import volatility.plugins.mac.common as common
-import volatility.plugins.mac.lsmod as lsmod
-
-class mac_socket_filters(lsmod.mac_lsmod):
- """ Reports socket filters """
-
- def check_filter(self, context, fname, ptr, kernel_symbol_addresses, kmods):
- if ptr == None:
- return
-
- # change the last paramter to 1 to get messages about which good modules hooks were found in
- good = common.is_known_address(ptr, kernel_symbol_addresses, kmods)
-
- return (good, context, fname, ptr)
-
- def calculate(self):
- common.set_plugin_members(self)
-
- # get the symbols need to check for if rootkit or not
- (kernel_symbol_addresses, kmods) = common.get_kernel_addrs(self)
-
- list_addrs = [self.addr_space.profile.get_symbol("_ipv4_filters"), self.addr_space.profile.get_symbol("_ipv6_filters")]
-
- for list_addr in list_addrs:
- plist = obj.Object("ipfilter_list", offset = list_addr, vm = self.addr_space)
-
- # type 'ipfilter'
- cur = plist.tqh_first
-
- while cur:
- filter = cur.ipf_filter
- name = filter.name.dereference()
-
- yield self.check_filter("INPUT", name, filter.ipf_input, kernel_symbol_addresses, kmods)
- yield self.check_filter("OUTPUT", name, filter.ipf_output, kernel_symbol_addresses, kmods)
- yield self.check_filter("DETACH", name, filter.ipf_detach, kernel_symbol_addresses, kmods)
-
- cur = cur.ipf_link.tqe_next
-
- def render_text(self, outfd, data):
- self.table_header(outfd, [("Context", "10"),
- ("Filter", "16"),
- ("Pointer", "[addrpad]"),
- ("Status", "")])
-
- for (good, context, fname, ptr) in data:
- if good == 0:
- status = "UNKNOWN"
- else:
- status = "OK"
- self.table_row(outfd, context, fname, ptr, status)