https://wintermade.it/blog/posts/how-to-write-generic-dissectors-in-wireshark.html
2022-02-16
OS
Ubuntu 20 using apt-get install wireshark with version 3.2
Windows 10 with Wireshark 3.6.2 64 bit
C:\Program Files\Wireshark\plugins\3.6\epan\
Or Linux
sudo cp generic.so /usr/lib/x86_64-linux-gnu/wireshark/plugins/3.2/epan
sudo cp custom.wsgd /usr/lib/x86_64-linux-gnu/wireshark/plugins/3.2/epan
sudo cp custom.fdesc /usr/lib/x86_64-linux-gnu/wireshark/plugins/3.2/epan
# udp_server.py
import socketserver
class CustomHandler(socketserver.DatagramRequestHandler):
def handle(self):
data = self.request[0].strip()
print(data)
if __name__ == "__main__":
print("server starting")
serv = socketserver.UDPServer(("127.0.0.1", 8756), CustomHandler)
serv.serve_forever()
# udp_client.py
import socket
import struct
import random
import string
import time
HOST, PORT = "localhost", 8756
# SOCK_DGRAM is the socket type to use for UDP sockets
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# refer to `pydoc struct`
HEADER_STRUCT = "".join([
">", # network byte order
"L", # counter
"B", # message size
"B", # message type (0: word, 1: number)
])
PAYLOAD_WORD_TYPE = HEADER_STRUCT + "".join([
"B", # word length
"100s", # string (at most 100 characters)
])
word_struct = struct.Struct(PAYLOAD_WORD_TYPE)
PAYLOAD_NUMBER_TYPE = HEADER_STRUCT + "".join([
"B", # number
"B", # 0: even, 1: odd
"B", # unsigned char
"H", # unsigned short
"I", # unsigned int
"Q", # unsigned long long
])
number_struct = struct.Struct(PAYLOAD_NUMBER_TYPE)
msg_counter = 0
for _ in range(3):
msg_counter += 1
# prepare data to send
if random.random() < 0.99:
num = random.choice(range(256))
is_even = num & 1
ui8 = pow(2,8)-1 # 255
ui16 = pow(2,16)-1 # 65535
ui32 = pow(2,32)-1 # 4294967295
i64 = pow(2,63)-1 # 9223372036854775807 ok
i64 = pow(2,63) # 9223372036854775808 become -9223372036854775808:int64
data = number_struct.pack(msg_counter, 2, 1, num, is_even, ui8, ui16, ui32, i64)
#data = number_struct.pack(msg_counter, 2, 1, num, is_even)
else:
string_len = random.choice(range(100))
the_string = bytes("".join(random.choice(string.ascii_letters+" ") for i in range(string_len)), "ascii")
data = word_struct.pack(msg_counter, 101, 0, string_len, the_string)
# send the message
sock.sendto(data, (HOST, PORT))
# wait 200ms
time.sleep(0.2)
# file custom.wsgd
# protocol metadata
PROTONAME Custom Protocol over UDP
PROTOSHORTNAME Custom
PROTOABBREV custom
# conditions on which the dissector is applied:
# the protocol will be applied on all UDP messages with port = 8756
PARENT_SUBFIELD udp.port
PARENT_SUBFIELD_VALUES 8756
# the name of the header structure
MSG_HEADER_TYPE T_custom_header
# field which permits to identify the message type.
MSG_ID_FIELD_NAME msg_id
# the main message type - usually it is a fake message, built of one
# of the possible messages
MSG_MAIN_TYPE T_custom_switch(msg_id)
# this token marks the end of the protocol description
PROTO_TYPE_DEFINITIONS
# refer to the description of the data format
include custom.fdesc;
# file custom.fdesc
# here, we define an enumerated type to list the type of messages
# defined in our protocol
enum8 T_custom_msg_type
{
word_message 0
number_message 1
}
# here, we define the structure of the header.
# The header (the same for each message type) must...
struct T_custom_header
{
# ... define the order of the data
byte_order big_endian;
uint32 counter;
uint8 size_after_header;
# ... contain the field defined as MSG_ID_FIELD_NAME
T_custom_msg_type msg_id;
}
struct T_word_message
{
T_custom_header header;
uint8 word_len;
# array of characters
char[word_len] word;
# "word" messages will always have some unused trailing bytes:
# they can be marked as raw(*) - the size is calculated at runtime
raw(*) spare;
}
struct T_number_message
{
T_custom_header header;
uint8 number;
bool8 is_even;
uint8 uint8;
uint16 uint16;
uint32 uint32;
int64{min=0:max=9223372036854775807} int64;
}
# T_custom_switch is the main message (as defined in the protocol description)
# according to the parameter msg_id (of type T_custom_msg_type), we define
# the main message to be defined by a single message: either T_word_message or T_number_message.
switch T_custom_switch T_custom_msg_type
{
case T_custom_msg_type::word_message : T_word_message "";
case T_custom_msg_type::number_message : T_number_message "";
}
Result
more
Multicast
Ubuntu
# route add -net 224.0.0.0 netmask 240.0.0.0 dev lo
# ifconfig lo multicast
# multicast_server.py
import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 8756
IS_ALL_GROUPS = True
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
# on this port, receives ALL multicast groups
sock.bind(('', MCAST_PORT))
else:
# on this port, listen ONLY to MCAST_GRP
sock.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
# For Python 3, change next line to "print(sock.recv(10240))"
print(sock.recv(10240))
# multicast_client.py
import socket
import struct
import random
import string
import time
# Ubuntu: add a new route
# route add -net 224.0.0.0 netmask 240.0.0.0 dev lo
# ifconfig lo multicast
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 8756
# regarding socket.IP_MULTICAST_TTL
# ---------------------------------
# for all packets sent, after two hops on the network the packet will not
# be re-sent/broadcast (see https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html)
MULTICAST_TTL = 2
# refer to `pydoc struct`
HEADER_STRUCT = "".join([
">", # network byte order
"L", # counter
"B", # message size
"B", # message type (0: word, 1: number)
])
PAYLOAD_WORD_TYPE = HEADER_STRUCT + "".join([
"B", # word length
"100s", # string (at most 100 characters)
])
word_struct = struct.Struct(PAYLOAD_WORD_TYPE)
PAYLOAD_NUMBER_TYPE = HEADER_STRUCT + "".join([
"B", # number
"B", # 0: even, 1: odd
"B", # unsigned char
"H", # unsigned short
"I", # unsigned int
"Q", # unsigned long long
])
number_struct = struct.Struct(PAYLOAD_NUMBER_TYPE)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
msg_counter = 0
for _ in range(3):
msg_counter += 1
# prepare data to send
if random.random() < 0.99:
num = random.choice(range(256))
is_even = num & 1
ui8 = pow(2,8)-1 # 255
ui16 = pow(2,16)-1 # 65535
ui32 = pow(2,32)-1 # 4294967295
i64 = pow(2,63)-1 # 9223372036854775807 ok
i64 = pow(2,63) # 9223372036854775808 become -9223372036854775808:int64
data = number_struct.pack(msg_counter, 2, 1, num, is_even, ui8, ui16, ui32, i64)
#data = number_struct.pack(msg_counter, 2, 1, num, is_even)
else:
string_len = random.choice(range(100))
the_string = bytes("".join(random.choice(string.ascii_letters+" ") for i in range(string_len)), "ascii")
data = word_struct.pack(msg_counter, 101, 0, string_len, the_string)
# send the message
sock.sendto(data, (MCAST_GRP, MCAST_PORT))
# wait 200ms
time.sleep(0.2)
Wireshark (multicast)
more