mirror of
https://github.com/volatilityfoundation/volatility3.git
synced 2026-09-07 02:07:39 +02:00
add mac_netstat and supporting networking code
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user