-
Notifications
You must be signed in to change notification settings - Fork 481
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Linux ip.Addr and ip.Link plugins #1079
base: develop
Are you sure you want to change the base?
Changes from 1 commit
82668af
6b3bbad
6e0ffc9
97007e8
620b9b4
cd867bf
5a8a0de
c72fa75
1b153cc
6f1e7c2
260fbd8
7b1c75c
8b6bd0f
5219e8a
828b688
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -291,5 +291,37 @@ | |
IFA_LINK = IPV6_ADDR_LINKLOCAL | ||
IFA_SITE = IPV6_ADDR_SITELOCAL | ||
|
||
# Promiscous mode | ||
IFF_PROMISC = 0x100 | ||
# Only for kernels < 3.15 when the net_device_flags enum didn't exist | ||
# ref include/uapi/linux/if.h | ||
NET_DEVICE_FLAGS = { | ||
"IFF_UP": 0x1, | ||
"IFF_BROADCAST": 0x2, | ||
"IFF_DEBUG": 0x4, | ||
"IFF_LOOPBACK": 0x8, | ||
"IFF_POINTOPOINT": 0x10, | ||
"IFF_NOTRAILERS": 0x20, | ||
"IFF_RUNNING": 0x40, | ||
"IFF_NOARP": 0x80, | ||
"IFF_PROMISC": 0x100, | ||
"IFF_ALLMULTI": 0x200, | ||
"IFF_MASTER": 0x400, | ||
"IFF_SLAVE": 0x800, | ||
"IFF_MULTICAST": 0x1000, | ||
"IFF_PORTSEL": 0x2000, | ||
"IFF_AUTOMEDIA": 0x4000, | ||
"IFF_DYNAMIC": 0x8000, | ||
"IFF_LOWER_UP": 0x10000, | ||
"IFF_DORMANT": 0x20000, | ||
"IFF_ECHO": 0x40000, | ||
} | ||
|
||
# RFC 2863 operational status. Kernels >= 2.6.17. See IF_OPER_* in include/uapi/linux/if.h | ||
IF_OPER_STATES = ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should these be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. This is used by value, so I don't think at the moment being an Enum offers any usability advantage. Although I reckon it may look better to Pythonic eyes ;) |
||
"UNKNOWN", | ||
"NOTPRESENT", | ||
"DOWN", | ||
"LOWERLAYERDOWN", | ||
"TESTING", | ||
"DORMANT", | ||
"UP", | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,18 +4,19 @@ | |
|
||
import collections.abc | ||
import logging | ||
import struct | ||
import socket as socket_module | ||
from typing import Generator, Iterable, Iterator, Optional, Tuple, List | ||
from typing import Generator, Iterable, Iterator, Optional, Tuple, List, Dict | ||
|
||
from volatility3.framework import constants | ||
from volatility3.framework.constants.linux import SOCK_TYPES, SOCK_FAMILY | ||
from volatility3.framework.constants.linux import IP_PROTOCOLS, IPV6_PROTOCOLS | ||
from volatility3.framework.constants.linux import TCP_STATES, NETLINK_PROTOCOLS | ||
from volatility3.framework.constants.linux import ETH_PROTOCOLS, BLUETOOTH_STATES | ||
from volatility3.framework.constants.linux import BLUETOOTH_PROTOCOLS, SOCKET_STATES | ||
from volatility3.framework.constants.linux import CAPABILITIES, IFF_PROMISC | ||
from volatility3.framework.constants.linux import CAPABILITIES, NET_DEVICE_FLAGS | ||
from volatility3.framework.constants.linux import IFA_HOST, IFA_LINK, IFA_SITE | ||
from volatility3.framework.constants.linux import IF_OPER_STATES | ||
from volatility3.framework.renderers import conversion | ||
from volatility3.framework import exceptions, objects, interfaces, symbols | ||
from volatility3.framework.layers import linear | ||
from volatility3.framework.objects import utility | ||
|
@@ -1236,24 +1237,25 @@ | |
|
||
|
||
class net_device(objects.StructType): | ||
@staticmethod | ||
def _format_as_mac_address(hwaddr): | ||
return ":".join([f"{x:02x}" for x in hwaddr[:6]]) | ||
def _format_as_mac_address(self, hwaddr): | ||
return ":".join([f"{x:02x}" for x in hwaddr[: self.addr_len]]) | ||
|
||
def get_mac_address(self): | ||
def get_mac_address(self) -> str: | ||
|
||
"""Get the MAC address of this network interface. | ||
|
||
Returns: | ||
str: the MAC address of this network interface. | ||
""" | ||
if self.has_member("perm_addr"): | ||
null_mac_addr_bytes = b"\x00" * self.addr_len | ||
null_mac_addr = self._format_as_mac_address(null_mac_addr_bytes) | ||
mac_addr = self._format_as_mac_address(self.perm_addr) | ||
if mac_addr != "00:00:00:00:00:00": | ||
if mac_addr != null_mac_addr: | ||
return mac_addr | ||
|
||
parent_layer = self._context.layers[self.vol.layer_name] | ||
try: | ||
hwaddr = parent_layer.read(self.dev_addr, 6) | ||
hwaddr = parent_layer.read(self.dev_addr, self.addr_len, pad=True) | ||
except exceptions.InvalidAddressException: | ||
vollog.debug( | ||
f"Unable to read network inteface mac address from {self.dev_addr:#x}" | ||
|
@@ -1262,16 +1264,41 @@ | |
|
||
return self._format_as_mac_address(hwaddr) | ||
|
||
def _get_flag_choices(self) -> Dict: | ||
"""Return the net_deivce flags as a list of strings""" | ||
vmlinux = linux.LinuxUtilities.get_module_from_volobj_type(self._context, self) | ||
try: | ||
# kernels >= 3.15 | ||
net_device_flags_enum = vmlinux.get_enumeration("net_device_flags") | ||
choices = net_device_flags_enum.choices | ||
except exceptions.SymbolError: | ||
# kernels < 3.15 | ||
choices = NET_DEVICE_FLAGS | ||
|
||
return choices | ||
|
||
def _get_net_device_flag_value(self, name): | ||
"""Return the net_deivce flag value based on the flag name""" | ||
return self._get_flag_choices()[name] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be happier if this were a get with a default value? Having said that, it's such a short function, perhaps it's not worth having at all (given it's private so can't be used by anyone else?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Default value and error managed. |
||
|
||
def get_flag_names(self) -> List[str]: | ||
"""Return the net_deivce flags as a list of strings | ||
|
||
Returns: | ||
List[str]: A list of flag names | ||
""" | ||
return list(self._get_flag_choices()) | ||
|
||
@property | ||
def promisc(self): | ||
"""Return if this network interface is in promiscuous mode. | ||
|
||
Returns: | ||
bool: True if this network interface is in promiscuous mode. Otherwise, False | ||
""" | ||
return self.flags & IFF_PROMISC == IFF_PROMISC | ||
return self.flags & self._get_net_device_flag_value("IFF_PROMISC") != 0 | ||
|
||
def get_net_namespace_id(self): | ||
def get_net_namespace_id(self) -> int: | ||
"""Return the network namespace id for this network interface. | ||
|
||
Returns: | ||
|
@@ -1288,6 +1315,17 @@ | |
|
||
return net_ns_id | ||
|
||
def get_operational_state(self) -> str: | ||
"""Return the netwok device oprational state (RFC 2863) string | ||
|
||
Returns: | ||
str: A string with the operational state | ||
""" | ||
if self.operstate >= len(IF_OPER_STATES): | ||
vollog.warning(f"Invalid net_device operational state '{self.operstate}'") | ||
return "INVALID" | ||
gcmoreira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return IF_OPER_STATES[self.operstate] | ||
|
||
class in_device(objects.StructType): | ||
def get_addresses(self): | ||
|
@@ -1320,7 +1358,7 @@ | |
return | ||
|
||
symbol_space = self._context.symbol_space | ||
table_name = self.vol.type_name.split(constants.BANG)[0] | ||
table_name = self.get_symbol_table_name() | ||
inet6_ifaddr_symname = table_name + constants.BANG + "inet6_ifaddr" | ||
if not symbol_space.has_type(inet6_ifaddr_symname) or not symbol_space.get_type( | ||
inet6_ifaddr_symname | ||
|
@@ -1351,7 +1389,7 @@ | |
Returns: | ||
str: the IPv4 scope type. | ||
""" | ||
table_name = self.vol.type_name.split(constants.BANG)[0] | ||
table_name = self.get_symbol_table_name() | ||
rt_scope_enum = self._context.symbol_space.get_enumeration( | ||
table_name + constants.BANG + "rt_scope_t" | ||
) | ||
|
@@ -1368,8 +1406,7 @@ | |
Returns: | ||
str: the IPv4 address | ||
""" | ||
ipv4_bytes = struct.pack("<I", self.ifa_address) | ||
return socket_module.inet_ntop(socket_module.AF_INET, ipv4_bytes) | ||
return conversion.convert_ipv4(self.ifa_address) | ||
|
||
def get_prefix_len(self): | ||
"""Get the IPv4 address prefix len | ||
|
@@ -1402,21 +1439,7 @@ | |
Returns: | ||
str: the IPv6 address | ||
""" | ||
symbol_space = self._context.symbol_space | ||
table_name = self.vol.type_name.split(constants.BANG)[0] | ||
in6_addr_symname = table_name + constants.BANG + "in6_addr" | ||
in6_addr_size = symbol_space.get_type(in6_addr_symname).size | ||
|
||
parent_layer = self._context.layers[self.vol.layer_name] | ||
try: | ||
ip6_addr_bytes = parent_layer.read(self.addr.vol.offset, in6_addr_size) | ||
except exceptions.InvalidAddressException: | ||
vollog.debug( | ||
f"Unable to read network IPv6 address from {self.addr.vol.offset:#x}" | ||
) | ||
return | ||
|
||
return socket_module.inet_ntop(socket_module.AF_INET6, ip6_addr_bytes) | ||
return conversion.convert_ipv6(self.addr.in6_u.u6_addr32) | ||
|
||
def get_prefix_len(self): | ||
"""Get the IPv6 address prefix len | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also looks like it could be a python Enum?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm here changing this to an Enum makes the code a bit complicated... have a look at how it's used in
_get_flag_choices()
and_get_net_device_flag_value()
to support both kernels <3.15 and >=3.15.