week06
This commit is contained in:
0
env/lib/python3.12/site-packages/pyshark/packet/__init__.py
vendored
Normal file
0
env/lib/python3.12/site-packages/pyshark/packet/__init__.py
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/common.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/common.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/consts.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/consts.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/fields.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/fields.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/packet.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/packet.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/packet_summary.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/__pycache__/packet_summary.cpython-312.pyc
vendored
Normal file
Binary file not shown.
41
env/lib/python3.12/site-packages/pyshark/packet/common.py
vendored
Normal file
41
env/lib/python3.12/site-packages/pyshark/packet/common.py
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
import sys
|
||||
import functools
|
||||
|
||||
import termcolor
|
||||
|
||||
|
||||
class Pickleable(object):
|
||||
"""
|
||||
Base class that implements getstate/setstate, since most of the classes are overriding getattr.
|
||||
"""
|
||||
|
||||
def __getstate__(self):
|
||||
return self.__dict__
|
||||
|
||||
def __setstate__(self, data):
|
||||
self.__dict__.update(data)
|
||||
|
||||
|
||||
class SlotsPickleable(object):
|
||||
__slots__ = []
|
||||
|
||||
def __getstate__(self):
|
||||
ret = {}
|
||||
for slot in self.__slots__:
|
||||
ret[slot] = getattr(self, slot)
|
||||
return ret
|
||||
|
||||
def __setstate__(self, data):
|
||||
for key, val in data.items():
|
||||
setattr(self, key, val)
|
||||
|
||||
|
||||
@functools.wraps(termcolor.colored)
|
||||
def colored(text, *args, **kwargs):
|
||||
try:
|
||||
enable_color = sys.stdout.isatty()
|
||||
except (AttributeError, NotImplementedError, FileNotFoundError):
|
||||
enable_color = False
|
||||
if enable_color:
|
||||
return termcolor.colored(text, *args, **kwargs)
|
||||
return text
|
1
env/lib/python3.12/site-packages/pyshark/packet/consts.py
vendored
Normal file
1
env/lib/python3.12/site-packages/pyshark/packet/consts.py
vendored
Normal file
@ -0,0 +1 @@
|
||||
TRANSPORT_LAYERS = ['UDP', 'TCP']
|
115
env/lib/python3.12/site-packages/pyshark/packet/fields.py
vendored
Normal file
115
env/lib/python3.12/site-packages/pyshark/packet/fields.py
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
import binascii
|
||||
import typing
|
||||
|
||||
from pyshark.packet.common import Pickleable, SlotsPickleable
|
||||
|
||||
|
||||
class LayerField(SlotsPickleable):
|
||||
"""Holds all data about a field of a layer, both its actual value and its name and nice representation."""
|
||||
|
||||
# Note: We use this object with slots and not just a dict because
|
||||
# it's much more memory-efficient (cuts about a third of the memory).
|
||||
__slots__ = ['name', 'showname', 'raw_value', 'show', 'hide', 'pos', 'size', 'unmaskedvalue']
|
||||
|
||||
def __init__(self, name=None, showname=None, value=None, show=None, hide=None, pos=None, size=None, unmaskedvalue=None):
|
||||
self.name = name
|
||||
self.showname = showname
|
||||
self.raw_value = value
|
||||
self.show = show
|
||||
self.pos = pos
|
||||
self.size = size
|
||||
self.unmaskedvalue = unmaskedvalue
|
||||
|
||||
if hide and hide == 'yes':
|
||||
self.hide = True
|
||||
else:
|
||||
self.hide = False
|
||||
|
||||
def __repr__(self):
|
||||
return f'<LayerField {self.name}: {self.get_default_value()}>'
|
||||
|
||||
def get_default_value(self) -> str:
|
||||
"""Gets the best 'value' string this field has."""
|
||||
val = self.show
|
||||
if not val:
|
||||
val = self.raw_value
|
||||
if not val:
|
||||
val = self.showname
|
||||
return val
|
||||
|
||||
@property
|
||||
def showname_value(self) -> typing.Union[str, None]:
|
||||
"""The "pretty value" (as displayed by Wireshark) of the field."""
|
||||
if self.showname and ': ' in self.showname:
|
||||
return self.showname.split(': ', 1)[1]
|
||||
return None
|
||||
|
||||
@property
|
||||
def showname_key(self) -> typing.Union[str, None]:
|
||||
"""The "pretty name" (as displayed by Wireshark) of the field."""
|
||||
if self.showname and ': ' in self.showname:
|
||||
return self.showname.split(': ', 1)[0]
|
||||
return None
|
||||
|
||||
@property
|
||||
def binary_value(self) -> bytes:
|
||||
"""Converts this field to binary (assuming it's a binary string)"""
|
||||
str_raw_value = str(self.raw_value)
|
||||
if len(str_raw_value) % 2 == 1:
|
||||
str_raw_value = '0' + str_raw_value
|
||||
|
||||
return binascii.unhexlify(str_raw_value)
|
||||
|
||||
@property
|
||||
def int_value(self) -> int:
|
||||
"""Returns the int value of this field (assuming it's represented as a decimal integer)."""
|
||||
return int(self.raw_value)
|
||||
|
||||
@property
|
||||
def hex_value(self) -> int:
|
||||
"""Returns the int value of this field if it's in base 16
|
||||
|
||||
(either as a normal number or in a "0xFFFF"-style hex value)
|
||||
"""
|
||||
return int(self.raw_value, 16)
|
||||
|
||||
base16_value = hex_value
|
||||
|
||||
|
||||
class LayerFieldsContainer(str, Pickleable):
|
||||
"""An object which contains one or more fields (of the same name).
|
||||
|
||||
When accessing member, such as showname, raw_value, etc. the appropriate member of the main (first) field saved
|
||||
in this container will be shown.
|
||||
"""
|
||||
|
||||
def __new__(cls, main_field, *args, **kwargs):
|
||||
if hasattr(main_field, 'get_default_value'):
|
||||
obj = str.__new__(cls, main_field.get_default_value(), *args, **kwargs)
|
||||
else:
|
||||
obj = str.__new__(cls, main_field, *args, **kwargs)
|
||||
obj.fields = [main_field]
|
||||
return obj
|
||||
|
||||
def __dir__(self):
|
||||
return dir(type(self)) + list(self.__dict__.keys()) + dir(self.main_field)
|
||||
|
||||
def add_field(self, field):
|
||||
self.fields.append(field)
|
||||
|
||||
@property
|
||||
def all_fields(self):
|
||||
"""Returns all fields in a list, the main field followed by the alternate fields."""
|
||||
return self.fields
|
||||
|
||||
@property
|
||||
def main_field(self):
|
||||
return self.fields[0]
|
||||
|
||||
@property
|
||||
def alternate_fields(self):
|
||||
"""Return the alternate values of this field containers (non-main ones)."""
|
||||
return self.fields[1:]
|
||||
|
||||
def __getattr__(self, item):
|
||||
return getattr(self.main_field, item)
|
0
env/lib/python3.12/site-packages/pyshark/packet/layers/__init__.py
vendored
Normal file
0
env/lib/python3.12/site-packages/pyshark/packet/layers/__init__.py
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/layers/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/layers/__pycache__/__init__.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.12/site-packages/pyshark/packet/layers/__pycache__/base.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/layers/__pycache__/base.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.12/site-packages/pyshark/packet/layers/__pycache__/ek_layer.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/layers/__pycache__/ek_layer.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.12/site-packages/pyshark/packet/layers/__pycache__/json_layer.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/layers/__pycache__/json_layer.cpython-312.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.12/site-packages/pyshark/packet/layers/__pycache__/xml_layer.cpython-312.pyc
vendored
Normal file
BIN
env/lib/python3.12/site-packages/pyshark/packet/layers/__pycache__/xml_layer.cpython-312.pyc
vendored
Normal file
Binary file not shown.
71
env/lib/python3.12/site-packages/pyshark/packet/layers/base.py
vendored
Normal file
71
env/lib/python3.12/site-packages/pyshark/packet/layers/base.py
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
import os
|
||||
import typing
|
||||
import io
|
||||
import sys
|
||||
|
||||
from pyshark.packet import common
|
||||
|
||||
DATA_LAYER_NAME = "DATA"
|
||||
|
||||
|
||||
class BaseLayer(common.SlotsPickleable):
|
||||
"""An object representing a Packet layer."""
|
||||
__slots__ = ["_layer_name"]
|
||||
|
||||
def __init__(self, layer_name):
|
||||
self._layer_name = layer_name
|
||||
|
||||
def get_field(self, name):
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def field_names(self) -> typing.List[str]:
|
||||
"""Gets all XML field names of this layer."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def has_field(self, name):
|
||||
return name in self.field_names
|
||||
|
||||
@property
|
||||
def layer_name(self):
|
||||
return self._layer_name
|
||||
|
||||
def get(self, item, default=None):
|
||||
"""Gets a field in the layer, or the default if not found.
|
||||
|
||||
Works the same way as getattr, but returns the given default if not the field was not found"""
|
||||
try:
|
||||
return getattr(self, item)
|
||||
except AttributeError:
|
||||
return default
|
||||
|
||||
def __dir__(self):
|
||||
return dir(type(self)) + self.field_names
|
||||
|
||||
def __getattr__(self, item):
|
||||
val = self.get_field(item)
|
||||
if val is None:
|
||||
raise AttributeError(f"{item} does not exist in Layer")
|
||||
return val
|
||||
|
||||
def pretty_print(self, writer=None):
|
||||
if not writer:
|
||||
writer = sys.stdout
|
||||
if self.layer_name == DATA_LAYER_NAME:
|
||||
writer.write('DATA')
|
||||
return
|
||||
|
||||
text = f'Layer {self.layer_name.upper()}{os.linesep}:'
|
||||
writer.write(common.colored(text, color="yellow", attrs=["bold"]))
|
||||
self._pretty_print_layer_fields(writer)
|
||||
|
||||
def _pretty_print_layer_fields(self, terminal_writer: io.IOBase):
|
||||
raise NotImplementedError()
|
||||
|
||||
def __repr__(self):
|
||||
return f'<{self.layer_name.upper()} Layer>'
|
||||
|
||||
def __str__(self):
|
||||
writer = io.StringIO()
|
||||
self.pretty_print(writer=writer)
|
||||
return writer.getvalue()
|
185
env/lib/python3.12/site-packages/pyshark/packet/layers/ek_layer.py
vendored
Normal file
185
env/lib/python3.12/site-packages/pyshark/packet/layers/ek_layer.py
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
import abc
|
||||
import os
|
||||
import io
|
||||
|
||||
import typing
|
||||
|
||||
from pyshark.packet.common import colored
|
||||
from pyshark import ek_field_mapping
|
||||
from pyshark.packet.layers.base import BaseLayer
|
||||
|
||||
|
||||
class _EkLayerHelperFuncsMixin(abc.ABC):
|
||||
"""For methods shared between the EK layer and sublayers"""
|
||||
|
||||
def get_field_as_list(self, name) -> list:
|
||||
"""Helper function to get a certain field always as a list.
|
||||
|
||||
Some fields may appear once or more in the packet. The field will appear as a list if it appears more
|
||||
than once. In order to avoid checking certain fields if they're lists or not, this function will
|
||||
return the field inside a list at all times.
|
||||
|
||||
For example, in a DNS packet there may be one or more responses.
|
||||
A packet with with one response (www.google.com) will return:
|
||||
>>> print(pkt.dns.resp_name)
|
||||
"www.google.com"
|
||||
While a packet with two responses will return:
|
||||
>>> print(pkt.dns.resp_name)
|
||||
["www.google.com", "www.google2.com"]
|
||||
|
||||
To avoid this changing behaviour, use:
|
||||
>>> print(pkt.dns.get_field_as_list("resp_name"))
|
||||
["www.google.com"]
|
||||
"""
|
||||
field_value = self.get_field(name)
|
||||
if isinstance(field_value, list):
|
||||
return field_value
|
||||
return [field_value]
|
||||
|
||||
|
||||
class EkLayer(BaseLayer, _EkLayerHelperFuncsMixin):
|
||||
__slots__ = ["_layer_name", "_fields_dict"]
|
||||
|
||||
def __init__(self, layer_name, layer_dict):
|
||||
super().__init__(layer_name)
|
||||
self._fields_dict = layer_dict
|
||||
|
||||
def get_field(self, name) -> typing.Union["EkMultiField", None, str, int, bool, bytes, list]:
|
||||
name = name.replace(".", "_")
|
||||
if name in self._fields_dict:
|
||||
# For cases like "text"
|
||||
return self._get_field_value(name)
|
||||
|
||||
for prefix in self._get_possible_layer_prefixes():
|
||||
nested_field = self._get_nested_field(prefix, name)
|
||||
if nested_field is not None:
|
||||
return nested_field
|
||||
|
||||
return None
|
||||
|
||||
def has_field(self, name) -> bool:
|
||||
"""Checks if the field exists, either a nested field or a regular field"""
|
||||
return name in self.field_names or name in self.all_field_names
|
||||
|
||||
@property
|
||||
def field_names(self):
|
||||
return list({field_name.split("_", 1)[0] for field_name in self.all_field_names})
|
||||
|
||||
@property
|
||||
def all_field_names(self):
|
||||
"""Gets all field names, including subfields"""
|
||||
names = set()
|
||||
for field_name in self._fields_dict:
|
||||
for prefix in self._get_possible_layer_prefixes():
|
||||
if field_name.startswith(prefix):
|
||||
names.add(_remove_ek_prefix(prefix, field_name))
|
||||
break
|
||||
return list(names)
|
||||
|
||||
def _get_field_value(self, full_field_name):
|
||||
"""Gets the field value, optionally casting it using the cached field mapping"""
|
||||
field_value = self._fields_dict[full_field_name]
|
||||
return ek_field_mapping.MAPPING.cast_field_value(self._layer_name, full_field_name, field_value)
|
||||
|
||||
def _get_nested_field(self, prefix, name):
|
||||
"""Gets a field that is directly on the layer
|
||||
|
||||
Returns either a multifield or a raw value.
|
||||
"""
|
||||
# TODO: Optimize
|
||||
field_ek_name = f"{prefix}_{name}"
|
||||
if field_ek_name in self._fields_dict:
|
||||
if self._field_has_subfields(field_ek_name):
|
||||
return EkMultiField(self, self._fields_dict, name,
|
||||
value=self._get_field_value(field_ek_name))
|
||||
return self._get_field_value(field_ek_name)
|
||||
|
||||
for possible_nested_name in self._fields_dict:
|
||||
if possible_nested_name.startswith(f"{field_ek_name}_"):
|
||||
return EkMultiField(self, self._fields_dict, name, value=None)
|
||||
|
||||
return None
|
||||
|
||||
def _field_has_subfields(self, field_ek_name):
|
||||
field_ek_name_with_ext = f"{field_ek_name}_"
|
||||
for field_name in self._fields_dict:
|
||||
if field_name.startswith(field_ek_name_with_ext):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _pretty_print_layer_fields(self, file: io.IOBase):
|
||||
for field_name in self.field_names:
|
||||
field = self.get_field(field_name)
|
||||
self._pretty_print_field(field_name, field, file, indent=1)
|
||||
|
||||
def _pretty_print_field(self, field_name, field, file, indent=0):
|
||||
prefix = "\t" * indent
|
||||
if isinstance(field, EkMultiField):
|
||||
file.write(colored(f"{prefix}{field_name}: ", "green", attrs=["bold"]))
|
||||
if field.value is not None:
|
||||
file.write(str(field.value))
|
||||
file.write(os.linesep)
|
||||
for subfield in field.subfields:
|
||||
self._pretty_print_field(subfield, field.get_field(subfield), file,
|
||||
indent=indent + 1)
|
||||
else:
|
||||
file.write(colored(f"{prefix}{field_name}: ", "green", attrs=["bold"]))
|
||||
file.write(f"{field}{os.linesep}")
|
||||
|
||||
def _get_possible_layer_prefixes(self):
|
||||
"""Gets the possible prefixes for a field under this layer.
|
||||
|
||||
The order matters, longest must be first
|
||||
"""
|
||||
return [f"{self._layer_name}_{self._layer_name}", self._layer_name]
|
||||
|
||||
|
||||
class EkMultiField(_EkLayerHelperFuncsMixin):
|
||||
__slots__ = ["_containing_layer", "_full_name", "_all_fields", "value"]
|
||||
|
||||
def __init__(self, containing_layer: EkLayer, all_fields, full_name, value=None):
|
||||
self._containing_layer = containing_layer
|
||||
self._full_name = full_name
|
||||
self._all_fields = all_fields
|
||||
self.value = value
|
||||
|
||||
def get_field(self, field_name):
|
||||
return self._containing_layer.get_field(f"{self._full_name}_{field_name}")
|
||||
|
||||
@property
|
||||
def subfields(self):
|
||||
names = set()
|
||||
for field_name in self._containing_layer.all_field_names:
|
||||
if field_name != self._full_name and field_name.startswith(f"{self._full_name}_"):
|
||||
names.add(field_name[len(self._full_name):].split("_")[1])
|
||||
return list(names)
|
||||
|
||||
@property
|
||||
def field_name(self):
|
||||
return self._full_name.split("_")[-1]
|
||||
|
||||
def __getattr__(self, item):
|
||||
value = self.get_field(item)
|
||||
if value is None:
|
||||
raise AttributeError(f"Subfield {item} not found")
|
||||
return value
|
||||
|
||||
def __repr__(self):
|
||||
value = f": {self.value}" if self.value else ""
|
||||
return f"<EkMultiField {self.field_name}{value}>"
|
||||
|
||||
def __dir__(self) -> typing.Iterable[str]:
|
||||
return dir(type(self)) + self.subfields
|
||||
|
||||
|
||||
def _remove_ek_prefix(prefix, value):
|
||||
"""Removes prefix given and the underscore after it"""
|
||||
return value[len(prefix) + 1:]
|
||||
|
||||
|
||||
def _get_subfields(all_fields, field_ek_name):
|
||||
subfield_names = []
|
||||
for field in all_fields:
|
||||
if field != field_ek_name and field.startswith(field_ek_name):
|
||||
subfield_names.append(_remove_ek_prefix(field_ek_name, field))
|
||||
return subfield_names
|
200
env/lib/python3.12/site-packages/pyshark/packet/layers/json_layer.py
vendored
Normal file
200
env/lib/python3.12/site-packages/pyshark/packet/layers/json_layer.py
vendored
Normal file
@ -0,0 +1,200 @@
|
||||
import os
|
||||
import io
|
||||
|
||||
from pyshark.packet.common import colored
|
||||
from pyshark.packet.fields import LayerField
|
||||
from pyshark.packet.fields import LayerFieldsContainer
|
||||
from pyshark.packet.layers.base import BaseLayer
|
||||
|
||||
|
||||
class JsonLayer(BaseLayer):
|
||||
__slots__ = [
|
||||
"duplicate_layers",
|
||||
"_showname_fields_converted_to_regular",
|
||||
"_full_name",
|
||||
"_is_intermediate",
|
||||
"_wrapped_fields",
|
||||
"value",
|
||||
"_all_fields"
|
||||
] + BaseLayer.__slots__
|
||||
|
||||
def __init__(self, layer_name, layer_dict, full_name=None, is_intermediate=False):
|
||||
"""Creates a JsonLayer. All sublayers and fields are created lazily later."""
|
||||
super().__init__(layer_name)
|
||||
self.duplicate_layers = []
|
||||
self._showname_fields_converted_to_regular = False
|
||||
if not full_name:
|
||||
self._full_name = self._layer_name
|
||||
else:
|
||||
self._full_name = full_name
|
||||
self._is_intermediate = is_intermediate
|
||||
self._wrapped_fields = {}
|
||||
if isinstance(layer_dict, list):
|
||||
self.duplicate_layers = [JsonLayer(layer_name, duplicate_dict,
|
||||
full_name=full_name, is_intermediate=is_intermediate)
|
||||
for duplicate_dict in layer_dict[1:]]
|
||||
layer_dict = layer_dict[0]
|
||||
if not isinstance(layer_dict, dict):
|
||||
self.value = layer_dict
|
||||
self._all_fields = {}
|
||||
return
|
||||
|
||||
self._all_fields = layer_dict
|
||||
|
||||
def get_field(self, name):
|
||||
"""Gets a field by its full or partial name."""
|
||||
# We only make the wrappers here (lazily) to avoid creating a ton of objects needlessly.
|
||||
self._convert_showname_field_names_to_field_names()
|
||||
field = self._wrapped_fields.get(name)
|
||||
if field is None:
|
||||
is_fake = False
|
||||
field = self._get_internal_field_by_name(name)
|
||||
if field is None:
|
||||
# Might be a "fake" field in JSON
|
||||
is_fake = self._is_fake_field(name)
|
||||
if not is_fake:
|
||||
raise AttributeError(f"No such field {name}")
|
||||
field = self._make_wrapped_field(name, field, is_fake=is_fake)
|
||||
self._wrapped_fields[name] = field
|
||||
return field
|
||||
|
||||
@property
|
||||
def field_names(self):
|
||||
self._convert_showname_field_names_to_field_names()
|
||||
return list(set([self._sanitize_field_name(name) for name in self._all_fields
|
||||
if name.startswith(self._full_name)] +
|
||||
[name.rsplit('.', 1)[1] for name in self._all_fields if '.' in name]))
|
||||
|
||||
def has_field(self, dotted_name) -> bool:
|
||||
"""Checks whether the layer has the given field name.
|
||||
|
||||
Can get a dotted name, i.e. layer.sublayer.subsublayer.field
|
||||
"""
|
||||
parts = dotted_name.split('.')
|
||||
cur_layer = self
|
||||
for part in parts:
|
||||
if part in cur_layer.field_names:
|
||||
cur_layer = cur_layer.get_field(part)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _pretty_print_layer_fields(self, file: io.IOBase):
|
||||
for field_line in self._get_all_field_lines():
|
||||
if ':' in field_line:
|
||||
field_name, field_line = field_line.split(':', 1)
|
||||
file.write(colored(field_name + ':', "green", ["bold"]))
|
||||
file.write(colored(field_line, attrs=["bold"]))
|
||||
|
||||
def _get_all_field_lines(self):
|
||||
"""Returns all lines that represent the fields of the layer (both their names and values)."""
|
||||
for field in self._get_all_fields_with_alternates():
|
||||
yield from self._get_field_or_layer_repr(field)
|
||||
|
||||
def _get_field_or_layer_repr(self, field):
|
||||
if isinstance(field, JsonLayer):
|
||||
yield "\t" + field.layer_name + ":" + os.linesep
|
||||
for line in field._get_all_field_lines():
|
||||
yield "\t" + line
|
||||
elif isinstance(field, list):
|
||||
for subfield_or_layer in field:
|
||||
yield from self._get_field_or_layer_repr(subfield_or_layer)
|
||||
else:
|
||||
yield f"\t{self._sanitize_field_name(field.name)}: {field.raw_value}{os.linesep}"
|
||||
|
||||
def _sanitize_field_name(self, field_name):
|
||||
return field_name.replace(self._full_name + '.', '')
|
||||
|
||||
def _field_name_from_showname(self, field_name):
|
||||
"""Converts a 'showname'-like field key to a regular field name
|
||||
|
||||
Sometimes in the JSON, there are "text" type fields which might look like this:
|
||||
"my_layer":
|
||||
{
|
||||
"my_layer.some_field": 1,
|
||||
"Something Special: it's special": {
|
||||
"my_layer.special_field": "it's special"
|
||||
}
|
||||
}
|
||||
|
||||
We convert the showname key into the field name. The internals will turn into a fake layer.
|
||||
In this case the field will be accessible by pkt.my_layer.something_special.special_field
|
||||
"""
|
||||
showname_key = field_name.split(":", 1)[0]
|
||||
return self._full_name + "." + showname_key.lower().replace(" ", "_")
|
||||
|
||||
def _get_all_fields_with_alternates(self):
|
||||
return [self.get_field(name) for name in self.field_names]
|
||||
|
||||
def _convert_showname_field_names_to_field_names(self):
|
||||
"""Converts all fields that don't have a proper name (they have a showname name) to a regular name
|
||||
|
||||
See self._field_name_from_showname docs for more.
|
||||
"""
|
||||
if self._showname_fields_converted_to_regular:
|
||||
return
|
||||
for field_name in list(self._all_fields):
|
||||
if ":" in field_name:
|
||||
field_value = self._all_fields.pop(field_name)
|
||||
if isinstance(field_value, dict):
|
||||
# Save the showname
|
||||
field_value["showname"] = field_name
|
||||
# Convert the old name to the new name.
|
||||
self._all_fields[
|
||||
self._field_name_from_showname(field_name)] = field_value
|
||||
|
||||
self._showname_fields_converted_to_regular = True
|
||||
|
||||
def _get_internal_field_by_name(self, name):
|
||||
"""Gets the field by name, or None if not found."""
|
||||
field = self._all_fields.get(name, self._all_fields.get(f"{self._full_name}.{name}"))
|
||||
if field is not None:
|
||||
return field
|
||||
for field_name in self._all_fields:
|
||||
# Specific name
|
||||
if field_name.endswith(f'.{name}'):
|
||||
return self._all_fields[field_name]
|
||||
|
||||
def _is_fake_field(self, name):
|
||||
# Some fields include parts that are not reflected in the JSON dictionary
|
||||
# i.e. a possible json is:
|
||||
# {
|
||||
# foo: {
|
||||
# foo.bar.baz: {
|
||||
# foo.baz: 3
|
||||
# }
|
||||
# }
|
||||
# So in this case we must create a fake layer for "bar".
|
||||
field_full_name = f"{self._full_name}.{name}."
|
||||
for name, field in self._all_fields.items():
|
||||
if name.startswith(field_full_name):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _make_wrapped_field(self, name, field, is_fake=False, full_name=None):
|
||||
"""Creates the field lazily.
|
||||
|
||||
If it's a simple field, wraps it in a container that adds extra features.
|
||||
If it's a nested layer, creates a layer for it.
|
||||
If it's an intermediate layer, copies over the relevant fields and creates a new layer for
|
||||
it.
|
||||
"""
|
||||
if not full_name:
|
||||
full_name = f"{self._full_name}.{name}"
|
||||
|
||||
if is_fake:
|
||||
# Populate with all fields that are supposed to be inside of it
|
||||
field = {key: value for key, value in self._all_fields.items()
|
||||
if key.startswith(full_name)}
|
||||
if isinstance(field, dict):
|
||||
if name.endswith('_tree'):
|
||||
name = name.replace('_tree', '')
|
||||
full_name = f'{self._full_name}.{name}'
|
||||
return JsonLayer(name, field, full_name=full_name, is_intermediate=is_fake)
|
||||
elif isinstance(field, list):
|
||||
# For whatever reason in list-type object it goes back to using the original parent name
|
||||
return [self._make_wrapped_field(name, field_part,
|
||||
full_name=self._full_name.split('.')[0])
|
||||
for field_part in field]
|
||||
|
||||
return LayerFieldsContainer(LayerField(name=name, value=field))
|
142
env/lib/python3.12/site-packages/pyshark/packet/layers/xml_layer.py
vendored
Normal file
142
env/lib/python3.12/site-packages/pyshark/packet/layers/xml_layer.py
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
import os
|
||||
import typing
|
||||
import io
|
||||
|
||||
from pyshark.packet.common import colored
|
||||
from pyshark.packet.fields import LayerField, LayerFieldsContainer
|
||||
from pyshark.packet.layers import base
|
||||
|
||||
|
||||
class XmlLayer(base.BaseLayer):
|
||||
__slots__ = [
|
||||
"raw_mode",
|
||||
"_all_fields"
|
||||
] + base.BaseLayer.__slots__
|
||||
|
||||
def __init__(self, xml_obj=None, raw_mode=False):
|
||||
super().__init__(xml_obj.attrib['name'])
|
||||
self.raw_mode = raw_mode
|
||||
|
||||
self._all_fields = {}
|
||||
|
||||
# We copy over all the fields from the XML object
|
||||
# Note: we don't read lazily from the XML because the lxml objects are very memory-inefficient
|
||||
# so we'd rather not save them.
|
||||
for field in xml_obj.findall('.//field'):
|
||||
attributes = dict(field.attrib)
|
||||
field_obj = LayerField(**attributes)
|
||||
if attributes['name'] in self._all_fields:
|
||||
# Field name already exists, add this field to the container.
|
||||
self._all_fields[attributes['name']].add_field(field_obj)
|
||||
else:
|
||||
self._all_fields[attributes['name']] = LayerFieldsContainer(field_obj)
|
||||
|
||||
def get_field(self, name) -> typing.Union[LayerFieldsContainer, None]:
|
||||
"""Gets the XML field object of the given name."""
|
||||
# Quicker in case the exact name was used.
|
||||
field = self._all_fields.get(name)
|
||||
if field is not None:
|
||||
return field
|
||||
|
||||
for field_name, field in self._all_fields.items():
|
||||
if self._sanitize_field_name(name) == self._sanitize_field_name(field_name):
|
||||
return field
|
||||
return None
|
||||
|
||||
def get_field_value(self, name, raw=False) -> typing.Union[LayerFieldsContainer, None]:
|
||||
"""Tries getting the value of the given field.
|
||||
|
||||
Tries it in the following order: show (standard nice display), value (raw value),
|
||||
showname (extended nice display).
|
||||
|
||||
:param name: The name of the field
|
||||
:param raw: Only return raw value
|
||||
:return: str of value
|
||||
"""
|
||||
field = self.get_field(name)
|
||||
if field is None:
|
||||
return None
|
||||
|
||||
if raw:
|
||||
return field.raw_value
|
||||
|
||||
return field
|
||||
|
||||
@property
|
||||
def field_names(self) -> typing.List[str]:
|
||||
"""Gets all XML field names of this layer."""
|
||||
return [self._sanitize_field_name(field_name) for field_name in self._all_fields]
|
||||
|
||||
@property
|
||||
def layer_name(self):
|
||||
if self._layer_name == 'fake-field-wrapper':
|
||||
return base.DATA_LAYER_NAME
|
||||
return super().layer_name
|
||||
|
||||
def __getattr__(self, item):
|
||||
val = self.get_field(item)
|
||||
if val is None:
|
||||
raise AttributeError()
|
||||
if self.raw_mode:
|
||||
return val.raw_value
|
||||
return val
|
||||
|
||||
@property
|
||||
def _field_prefix(self) -> str:
|
||||
"""Prefix to field names in the XML."""
|
||||
if self.layer_name == 'geninfo':
|
||||
return ''
|
||||
return self.layer_name + '.'
|
||||
|
||||
def _sanitize_field_name(self, field_name):
|
||||
"""Sanitizes an XML field name
|
||||
|
||||
An xml field might have characters which would make it inaccessible as a python attribute).
|
||||
"""
|
||||
field_name = field_name.replace(self._field_prefix, '')
|
||||
return field_name.replace('.', '_').replace('-', '_').lower()
|
||||
|
||||
def _pretty_print_layer_fields(self, file: io.IOBase):
|
||||
for field_line in self._get_all_field_lines():
|
||||
if ':' in field_line:
|
||||
field_name, field_line = field_line.split(':', 1)
|
||||
file.write(colored(field_name + ':', "green", attrs=["bold"]))
|
||||
file.write(colored(field_line, attrs=["bold"]))
|
||||
|
||||
def _get_all_fields_with_alternates(self):
|
||||
all_fields = list(self._all_fields.values())
|
||||
all_fields += sum([field.alternate_fields for field in all_fields
|
||||
if isinstance(field, LayerFieldsContainer)], [])
|
||||
return all_fields
|
||||
|
||||
def _get_all_field_lines(self):
|
||||
"""Returns all lines that represent the fields of the layer (both their names and values)."""
|
||||
for field in self._get_all_fields_with_alternates():
|
||||
yield from self._get_field_or_layer_repr(field)
|
||||
|
||||
def _get_field_or_layer_repr(self, field):
|
||||
field_repr = self._get_field_repr(field)
|
||||
if field_repr:
|
||||
yield f"\t{field_repr}{os.linesep}"
|
||||
|
||||
def _get_field_repr(self, field):
|
||||
if field.hide:
|
||||
return
|
||||
if field.showname:
|
||||
return field.showname
|
||||
elif field.show:
|
||||
return field.show
|
||||
elif field.raw_value:
|
||||
return f"{self._sanitize_field_name(field.name)}: {field.raw_value}"
|
||||
|
||||
def get_field_by_showname(self, showname) -> typing.Union[LayerFieldsContainer, None]:
|
||||
"""Gets a field by its "showname"
|
||||
This is the name that appears in Wireshark's detailed display i.e. in 'User-Agent: Mozilla...',
|
||||
'User-Agent' is the .showname
|
||||
Returns None if not found.
|
||||
"""
|
||||
for field in self._get_all_fields_with_alternates():
|
||||
if field.showname_key == showname:
|
||||
# Return it if "XXX: whatever == XXX"
|
||||
return field
|
||||
return None
|
143
env/lib/python3.12/site-packages/pyshark/packet/packet.py
vendored
Normal file
143
env/lib/python3.12/site-packages/pyshark/packet/packet.py
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
import datetime
|
||||
import os
|
||||
import binascii
|
||||
import typing
|
||||
|
||||
from pyshark.packet import consts
|
||||
from pyshark.packet.common import Pickleable
|
||||
from pyshark.packet.layers.base import BaseLayer
|
||||
|
||||
|
||||
class Packet(Pickleable):
|
||||
"""A packet object which contains layers.
|
||||
|
||||
Layers can be accessed via index or name.
|
||||
"""
|
||||
|
||||
def __init__(self, layers=None, frame_info=None, number=None,
|
||||
length=None, captured_length=None, sniff_time=None, interface_captured=None):
|
||||
"""
|
||||
Creates a Packet object with the given layers and info.
|
||||
|
||||
:param layers: A list of BaseLayer objects.
|
||||
:param frame_info: Layer object for the entire packet frame (information like frame length, packet number, etc.
|
||||
:param length: Length of the actual packet.
|
||||
:param captured_length: The length of the packet that was actually captured (could be less then length)
|
||||
:param sniff_time: The time the packet was captured (timestamp)
|
||||
:param interface_captured: The interface the packet was captured in.
|
||||
"""
|
||||
if layers is None:
|
||||
self.layers = []
|
||||
else:
|
||||
self.layers = layers
|
||||
self.frame_info = frame_info
|
||||
self.number = number
|
||||
self.interface_captured = interface_captured
|
||||
self.captured_length = captured_length
|
||||
self.length = length
|
||||
self.sniff_timestamp = sniff_time
|
||||
|
||||
def __getitem__(self, item):
|
||||
"""
|
||||
Gets a layer according to its index or its name
|
||||
|
||||
:param item: layer index or name
|
||||
:return: BaseLayer object.
|
||||
"""
|
||||
if isinstance(item, int):
|
||||
return self.layers[item]
|
||||
for layer in self.layers:
|
||||
if layer.layer_name.lower() == item.lower():
|
||||
return layer
|
||||
raise KeyError('Layer does not exist in packet')
|
||||
|
||||
def __contains__(self, item):
|
||||
"""Checks if the layer is inside the packet.
|
||||
|
||||
:param item: name of the layer
|
||||
"""
|
||||
try:
|
||||
self[item]
|
||||
return True
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
def __dir__(self):
|
||||
return dir(type(self)) + list(self.__dict__.keys()) + [l.layer_name for l in self.layers]
|
||||
|
||||
def get_raw_packet(self) -> bytes:
|
||||
assert "FRAME_RAW" in self, "Packet contains no raw data. In order to contains it, " \
|
||||
"make sure that use_json and include_raw are set to True " \
|
||||
"in the Capture object"
|
||||
raw_packet = b''
|
||||
byte_values = [''.join(x) for x in zip(self.frame_raw.value[0::2], self.frame_raw.value[1::2])]
|
||||
for value in byte_values:
|
||||
raw_packet += binascii.unhexlify(value)
|
||||
return raw_packet
|
||||
|
||||
def __len__(self):
|
||||
return int(self.length)
|
||||
|
||||
def __bool__(self):
|
||||
return True
|
||||
|
||||
@property
|
||||
def sniff_time(self) -> datetime.datetime:
|
||||
try:
|
||||
timestamp = float(self.sniff_timestamp)
|
||||
except ValueError:
|
||||
# If the value after the decimal point is negative, discard it
|
||||
# Google: wireshark fractional second
|
||||
timestamp = float(self.sniff_timestamp.split(".")[0])
|
||||
return datetime.datetime.fromtimestamp(timestamp)
|
||||
|
||||
def __repr__(self):
|
||||
transport_protocol = ''
|
||||
if self.transport_layer != self.highest_layer and self.transport_layer is not None:
|
||||
transport_protocol = self.transport_layer + '/'
|
||||
|
||||
return f'<{transport_protocol}{self.highest_layer} Packet>'
|
||||
|
||||
def __str__(self):
|
||||
s = self._packet_string
|
||||
for layer in self.layers:
|
||||
s += str(layer)
|
||||
return s
|
||||
|
||||
@property
|
||||
def _packet_string(self):
|
||||
"""A simple pretty string that represents the packet."""
|
||||
return f'Packet (Length: {self.length}){os.linesep}'
|
||||
|
||||
def pretty_print(self):
|
||||
for layer in self.layers:
|
||||
layer.pretty_print()
|
||||
# Alias
|
||||
show = pretty_print
|
||||
|
||||
def __getattr__(self, item):
|
||||
"""
|
||||
Allows layers to be retrieved via get attr. For instance: pkt.ip
|
||||
"""
|
||||
for layer in self.layers:
|
||||
if layer.layer_name.lower() == item.lower():
|
||||
return layer
|
||||
raise AttributeError(f"No attribute named {item}")
|
||||
|
||||
@property
|
||||
def highest_layer(self) -> BaseLayer:
|
||||
return self.layers[-1].layer_name.upper()
|
||||
|
||||
@property
|
||||
def transport_layer(self) -> BaseLayer:
|
||||
for layer in consts.TRANSPORT_LAYERS:
|
||||
if layer in self:
|
||||
return layer
|
||||
|
||||
def get_multiple_layers(self, layer_name) -> typing.List[BaseLayer]:
|
||||
"""Returns a list of all the layers in the packet that are of the layer type (an incase-sensitive string).
|
||||
|
||||
This is in order to retrieve layers which appear multiple times in the same packet (i.e. double VLAN)
|
||||
which cannot be retrieved by easier means.
|
||||
"""
|
||||
return [layer for layer in self.layers if layer.layer_name.lower() == layer_name.lower()]
|
27
env/lib/python3.12/site-packages/pyshark/packet/packet_summary.py
vendored
Normal file
27
env/lib/python3.12/site-packages/pyshark/packet/packet_summary.py
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
class PacketSummary(object):
|
||||
"""A simple object containing a psml summary.
|
||||
|
||||
Can contain various summary information about a packet.
|
||||
"""
|
||||
|
||||
def __init__(self, structure, values):
|
||||
self._fields = {}
|
||||
self._field_order = []
|
||||
|
||||
for key, val in zip(structure, values):
|
||||
key, val = str(key), str(val)
|
||||
self._fields[key] = val
|
||||
self._field_order.append(key)
|
||||
setattr(self, key.lower().replace('.', '').replace(',', ''), val)
|
||||
|
||||
def __repr__(self):
|
||||
protocol, src, dst = self._fields.get('Protocol', '?'), self._fields.get('Source', '?'),\
|
||||
self._fields.get('Destination', '?')
|
||||
return f'<{self.__class__.__name__} {protocol}: {src} to {dst}>'
|
||||
|
||||
def __str__(self):
|
||||
return self.summary_line
|
||||
|
||||
@property
|
||||
def summary_line(self) -> str:
|
||||
return ' '.join([self._fields[key] for key in self._field_order])
|
Reference in New Issue
Block a user