add mac_netstat and supporting networking code

This commit is contained in:
Andrew Case
2019-02-26 22:20:33 +00:00
committed by ikelos
parent d8413efc1d
commit 6d2a5fbfaa
4 changed files with 245 additions and 5 deletions
+38
View File
@@ -222,3 +222,41 @@ class MacUtilities(object):
def virtual_to_physical_address(cls, addr: int) -> int:
"""Converts a virtual mac address to a physical one (does not account of ASLR)"""
return addr - 0xffffff8000000000
@classmethod
def files_descriptors_for_process(cls, config: interfaces.configuration.HierarchicalDict,
context: interfaces.context.ContextInterface,
task: interfaces.objects.ObjectInterface):
num_fds = task.p_fd.fd_lastfile
nfiles = task.p_fd.fd_nfiles
if nfiles > num_fds:
num_fds = nfiles
if num_fds > 4096:
num_fds = 1024
file_type = config["darwin"] + constants.BANG + 'fileproc'
try:
table_addr = task.p_fd.fd_ofiles.dereference()
except exceptions.PagedInvalidAddressException:
return
fds = objects.utility.array_of_pointers(table_addr, count = num_fds, subtype = file_type, context = context)
for fd_num, f in enumerate(fds):
if f != 0:
try:
ftype = f.f_fglob.get_fg_type()
except exceptions.PagedInvalidAddressException:
continue
if ftype == 'DTYPE_VNODE':
vnode = f.f_fglob.fg_data.dereference().cast("vnode")
path = vnode.full_path()
else:
path = "<%s>" % ftype.replace("DTYPE_", "").lower()
yield f, path, fd_num
+91 -1
View File
@@ -18,7 +18,7 @@
# specific language governing rights and limitations under the License.
#
import datetime
import datetime, socket, struct
from typing import Union
from volatility.framework import interfaces, renderers
@@ -65,3 +65,93 @@ def round(addr: int, align: int, up: bool = False) -> int:
if up:
return (addr + (align - (addr % align)))
return (addr - (addr % align))
"""
For vol3 devs:
convert_ipv4 && convert_ipv6 are slightly modified versions of their
counterparts from vol2:
https://github.com/volatilityfoundation/volatility/blob/master/volatility/utils.py#L84
Furthermore, vol2 used as overlay for ip addresses that made the conversion string based:
https://github.com/volatilityfoundation/volatility/blob/master/volatility/plugins/overlays/basic.py#L156
by using struct.pack with the given format string on data that was then gathered through .v():
https://github.com/volatilityfoundation/volatility/blob/aa6b960c1077e447bda9d64df507ec02f8fcc958/volatility/obj.py#L439
.v() for IP addresses would do obj_vm.read(), which returned a string, and struct.pack was called on it.
This doesn't translate very well to vol3, since vol3 does have overlays so the plugins instead are retreiving the raw integers
from memory. That is why convert_ip4 takes a 32 bit integer as its input and convert_ipv6 takes an array of shorts.
This code has only been tested on Mac so far, but since the modified functions cleanly replace evaluation of data that used to
be done by the overlays that plugins for every OS used, then I don't expect issues when vol3 linux and windows plugins use them
The other thing to note about these functions, is that when you struct.pack("I",...) as convert_ip4 does, and then you try to enumerate it,
Python3 will treat each element as an 'int' and *not* a string. That means any code that calls ord() will fail, so those calls were removed
when porting over the vol2 and python2 version.
"""
def convert_ipv4(ip_as_integer):
ip_str = struct.pack("<I", ip_as_integer)
return "{0}.{1}.{2}.{3}".format(*[x for x in ip_str])
def convert_ipv6(packed_ip):
# Replace a run of 0x00s with None
numlen = [(k, len(list(g))) for k, g in itertools.groupby(packed_ip)]
max_zero_run = sorted(sorted(numlen, key = lambda x: x[1], reverse = True), key = lambda x: x[0])[0]
words = []
for k, l in numlen:
if (k == 0) and (l == max_zero_run[1]) and not (None in words):
words.append(None)
else:
for i in range(l):
words.append(k)
# Handle encapsulated IPv4 addresses
encapsulated = ""
if (words[0] is None) and (len(words) == 3 or (len(words) == 4 and words[1] == 0xffff)):
words = words[:-2]
encapsulated = inet_ntop4(packed_ip[-4:])
# If we start or end with None, then add an additional :
if words[0] is None:
words = [None] + words
if words[-1] is None:
words += [None]
# Join up everything we've got using :s
return ":".join(["{0:x}".format(w) if w is not None else "" for w in words]) + encapsulated
def convert_port(port_as_integer):
return (port_as_integer >> 8) | ((port_as_integer & 0xff) << 8)
def convert_network_four_tuple(family, four_tuple):
"""
Converts the connection four_tuple:
(source ip,
source port,
dest ip,
dest port)
into their string equivalents.
IP addresses are expected as a tuple of unsigned shorts
Ports are converted to proper endianess as well
"""
if family == socket.AF_INET:
ret = (convert_ipv4(four_tuple[0]),
convert_port(four_tuple[1]),
convert_ipv4(four_tuple[2]),
convert_port(four_tuple[3]))
elif family == socket.AF_INET6:
ret = (convert_ipv6(four_tuple[0]),
convert_port(four_tuple[1]),
convert_ipv6(four_tuple[2]),
convert_port(four_tuple[3]))
else:
ret = None
return ret
@@ -34,3 +34,7 @@ class MacKernelIntermedSymbols(intermed.IntermediateSymbolTable):
self.set_type_class('vnode', extensions.vnode)
self.set_type_class('vm_map_entry', extensions.vm_map_entry)
self.set_type_class('vm_map_object', extensions.vm_map_object)
self.set_type_class('socket', extensions.socket)
self.set_type_class('inpcb', extensions.inpcb)
@@ -24,6 +24,7 @@ from volatility.framework import constants
from volatility.framework import exceptions, interfaces
from volatility.framework.symbols import generic
from volatility.framework.objects import utility
from volatility.framework.renderers import conversion
class proc(generic.GenericIntelProcess):
@@ -126,7 +127,7 @@ class vnode(generic.GenericIntelProcess):
return
if vname:
ret.append(utility.pointer_to_string(vname))
ret.append(utility.pointer_to_string(vname, 255))
if int(vnodeobj.v_flag) & 0x000001 != 0 and int(vnodeobj.v_mount) != 0:
if int(vnodeobj.v_mount.mnt_vnodecovered) != 0:
@@ -145,11 +146,11 @@ class vnode(generic.GenericIntelProcess):
elements.reverse()
for e in elements:
files.append(e.decode("utf-8"))
files.append(e.encode("utf-8"))
ret = "/".join(files)
ret = b"/".join(files)
if ret:
ret = "/" + ret
ret = b"/" + ret
return ret
@@ -277,3 +278,110 @@ class vm_map_entry(generic.GenericIntelProcess):
ret = None
return ret
class socket(generic.GenericIntelProcess):
def get_inpcb(self):
try:
ret = self.so_pcb.dereference().cast("inpcb")
except exceptions.PagedInvalidAddressException:
ret = None
return ret
def get_family(self):
return self.so_proto.pr_domain.dom_family
def get_protocol_as_string(self):
proto = self.so_proto.pr_protocol
if proto == 6:
ret = "TCP"
elif proto == 17:
ret = "UDP"
else:
ret = ""
return ret
def get_state(self):
ret = ""
if self.so_proto.pr_protocol == 6:
inpcb = self.get_inpcb()
if inpcb is not None:
ret = inpcb.get_tcp_state()
return ret
def get_connection_info(self):
inpcb = self.get_inpcb()
if inpcb is None:
ret = None
elif self.get_family() == 2:
ret = inpcb.get_ipv4_info()
else:
ret = inpcb.get_ipv6_info()
return ret
def get_converted_connection_info(self):
vals = self.get_connection_info()
if vals:
ret = conversion.convert_network_four_tuple(self.get_family(), vals)
else:
ret = None
return ret
class inpcb(generic.GenericIntelProcess):
def get_tcp_state(self):
tcp_states = (
"CLOSED",
"LISTEN",
"SYN_SENT",
"SYN_RECV",
"ESTABLISHED",
"CLOSE_WAIT",
"FIN_WAIT1",
"CLOSING",
"LAST_ACK",
"FIN_WAIT2",
"TIME_WAIT")
try:
tcpcb = self.inp_ppcb.dereference().cast("tcpcb")
except exceptions.PagedInvalidAddressException:
return ""
state_type = tcpcb.t_state
if state_type and state_type < len(tcp_states):
state = tcp_states[state_type]
else:
state = ""
return state
def get_ipv4_info(self):
lip = self.inp_dependladdr.inp46_local.ia46_addr4.s_addr
lport = self.inp_lport
rip = self.inp_dependfaddr.inp46_foreign.ia46_addr4.s_addr
rport = self.inp_fport
return [lip, lport, rip, rport]
def get_ipv6_info(self):
lip = self.inp_dependladdr.inp6_local.member(attr = '__u6_addr').member(attr = '__u6_addr16')
lport = self.inp_lport
rip = self.inp_dependfaddr.inp6_foreign.member(attr = '__u6_addr').member(attr = '__u6_addr16')
rport = self.inp_fport
return [lip, lport, rip, rport]