Getting started with Wireshark Dissector
A Wireshark dissector is simply a decoder, which is interesting in a specific type of traffic. Once it finds the traffic, it interprets the payload. In short, it is a protocol parser.
Wireshark dissectors can be useful when you are working with a custom protocol that Wireshark doesn’t already have a dissector for. I will create a dissector for the following payload (the data).
The first 3 packets belong to the TCP 3-way handshaking. After the TCP connection has established, the client sends some data that Wireshark’s dissectors do not understand in packet number 4.
We will create a simple protocol and write a dissector for it. The protocol is based on a request and response. With this protocol a client can ask the server if the service on the server is up or not. The server responses back, informing the service is up or down.
For sake of simplicity, we will create 2 bytes protocol, which will be working in client and server architecture. The client will be able to query the service status on the server. The server will response the client with a relevant answer. The first byte is used to distinguish whether the message type is a "question" or an "answer". The second byte will be used for the content of the "answer" or the "question".
For example:
- When the first byte is
0x01
, it means the client is questioning the server.
-
- When the second byte is 0x01, it means the client is asking if the service up or not.
- When the second byte is other than 0x01, it means the client is asking some other question.
- When the first byte is
0x02
, it means the server is answering the client.
-
- When the second byte is 0x01, it means "Yes, the service is up"
- When the second byte is 0x02, it means "No, the service is down"
- When the second byte is other than 0x01 and 0x02, it means the answer is something else.
- When the second byte is 0x01, it means "Yes, the service is up"
Below I created 4 message types.
MESSAGE1 = "0101" → "Is the service up?"
MESSAGE2 = "0102" → "Other question?"
MESSAGE3 = "0201" → "Yes, the service is up"
MESSAGE4 = "0202" → "No, the service is down"
Different types of Dissectors
There are also different types of dissectors that can be useful for different tasks.
There are dissectors that run after all the other dissectors have run, giving the programmer access to fields defined in other dissectors. These are referred to as post-dissectors
A chained dissector is similar to the post-dissector in that it runs after other dissectors so that you can access the fields for other dissectors.
The difference is that a chained dissector doesn't run against every packet, only those packets that are handled by the dissector off of which you are chaining. Chained dissectors are handy for extending an existing dissector without having to rewrite it completely, whereas post-dissectors are useful for adding a new dissector that provides additional context based on what other fields are set.
Different ways to write Wireshark Dissectors
There are 3 ways commonly used ways to write your own dissector:
C/C++ based dissectors
Most of Wireshark dissectors are written in C/C++ programing language. It is fast and efficient but It requires a full fledged development environment.
Wireshark Generic Dissector
In this method, you describe your data using a specific format and save it in text file(s). Wireshark reads the protocol definitions from the file(s). It is similar to a programming language. Interpreted text files are simple to use but slow for dissecting packets. Wireshark Generic Dissector does not require a development environment. The only thing you need is a text editor.
Scripting language based dissectors
This is a very common method used. Wireshark has a Lua implementation that makes it easy for people who are unfamiliar with C to write dissectors. Lua is a scripting language in that Lua code is read from a plain text script/source file and then executed by the Lua interpreter—a compiled executable itself—dynamically at runtime. Another word for scripting language is interpreted or managed language. Because the code is interpreted at runtime, and generally all memory access is managed by the runtime, Lua, in this case, is the interpreter.
Creating Dissectors for Wireshark
Although I am not familiar with Lua, I will use Lua for this article. It takes a little time to look at its syntax.
Step-1: Declare the protocol. My protocol short name (display filter name) will be "ulive.lua". The long name in the protocol tree will be "Service State Protocol".
ulive_protocol = Proto("ulive", "Service State Protocol")
Step-2: Declare the fields with their types. We will have 3 fields which are:
- MessageType: It show if the packet is a request/question or response/answer.
- Question: It shows the question type.
- Answer: It shows the answer type.
msg_type =ProtoField.uint8("ulive.MessageType","MessageType",base.DEC)
question_type =ProtoField.uint8("ulive.Question","Question",base.DEC)
answer_type =ProtoField.uint8("ulive.Answer","Answer",base.DEC)
Step-3: Register your fields.
ulive_protocol.fields = {msg_type,question_type,answer_type}
The dissector must register its data fields with Wireshark so that Wireshark knows how to display them. If you do not register the fields, you will get the error above.
Step-4: Create a dissection function which takes 3 parameters:
- buffer: It is the data on the top of TCP. The dissector will walk through the buffer of bytes.
- pinfo: It contains the data about the packet.
- tree: The tree on which we append our subtree.
function ulive_protocol.dissector(buffer, pinfo, tree)
-- set the protocol column
pinfo.cols.protocol = ulive_protocol.name;
-- create the protocol item tree
subtree = tree:add(ulive_protocol,buffer())
-- get the first byte for distinguishing the message type
mtype = buffer(0,1):le_uint()
-- now, do the comparisons
if mtype == 1 then -- if the packet is a question
mtype_str = "Question"
subtree:add_le(msg_type,buffer(0,1)):append_text(" (" .. mtype_str .. ")")
qtype = buffer(1,1):le_uint()
-- find the question type
if qtype == 1 then
mtype_str = "Is the service up?"
subtree:add_le(question_type,buffer(1,1)):append_text(" (" .. mtype_str .. ")")
else
mtype_str = "Other question?"
subtree:add_le(question_type,buffer(1,1)):append_text(" (" .. mtype_str .. ")")
end
end
if mtype == 2 then -- if the packet is an aswer
mtype_str = "Answer"
subtree:add_le(msg_type,buffer(0,1)):append_text(" (" .. mtype_str .. ")")
atype = buffer(1,1):le_uint()
-- find the answer type
if atype == 1 then
mtype_str = "Yes, the service is up"
subtree:add_le(answer_type,buffer(1,1)):append_text(" (" .. mtype_str .. ")")
else
mtype_str = "No, the service is down"
subtree:add_le(answer_type,buffer(1,1)):append_text(" (" .. mtype_str .. ")")
end
end
end
Step-5: Specify which port and protocol will ve used. I will use TCP and and port 12345. When Wireshark come across a packet with these parameters, it will use my dissector.
local tcp_port = DissectorTable.get("tcp.port")
tcp_port:add(12345,ulive_protocol)
TCP client code
We need to create some data to test our dissector. For this purpose, I coded a pretty simple client and server in Python.
import socket
import sys
# define the test message types
MESSAGE1 = "0101" # "Is the service up?"
MESSAGE2 = "0102" # "Other question?"
MESSAGE3 = "0201" # "Yes, the service is up"
MESSAGE4 = "0202" # "No, the service is down"
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Socket has successfully created")
except socket.error as err:
print("socket creation has failed with error %s" % (err))
port = 12345
try:
host_ip = socket.gethostbyname('192.168.1.5') # server ip address or host name
except socket.gaierror:
# it fails to resolve the host name
print("there has been an error resolving the host")
sys.exit()
s.connect((host_ip, port))
s.send(bytes.fromhex(MESSAGE1)) # it will test MESSAGE1
s.close()
TCP server code
import socket
# define the test message types
MESSAGE1 = "0101" # "Is the service up?"
MESSAGE2 = "0102" # "Other question?"
MESSAGE3 = "0201" # "Yes, the service is up"
MESSAGE4 = "0202" # "No, the service is down"
s = socket.socket()
print("Socket has successfully created")
port = 12345
s.bind(('', port))
print("socket has binded to %s" % (port))
s.listen(5)
print("socket is listening")
while True:
c, addr = s.accept()
print('Got connection from', addr)
#data = c.recv(1024)
c.send(bytes.fromhex(MESSAGE4))
c.close()
Adding the dissector to Wireshark
Step-1: Navigate to "Help → About Wireshark" menu.
Step-2: A window appears, then click "Folder" tab and you will see a list of paths. Click on "Personal Lua Plugins". If you have not created the directory before, click "Yes" button to create the directory.
Paste your Lua file in this directory.
Step-3: Reload your Wireshark to activate the dissector, then open packets you captured or my packets below. You should see something like below.
Final thoughts
Wireshark may not dissect a proprietary protocol if you use one of them in your enterprise. With help of Lua, you can create your own dissector and easily integrate it with Wireshark.
Further Reading
This was a really helpful example! I now understand the logic behind dissectors. However, I’m having trouble with the configuration. What interface did you use for the capture?