24/02/2010 21:02:32
Wireshark is an open source network protocol analyzer and quite probably the best
of its kind. If you are a developer working with a lot of networking code, it’s a must have!
It can recognize many standard protocols such as POP3, NTP, Jabber, etc…
Most of the development I do is related to communicating with hardware devices
using TCP/IP. These devices implement their own communication protocol unknown to
Wireshark. While I can still use Wireshark to monitor the raw data, I thought it would
be much better if I could actually tell it what my data is so it can be displayed properly.
Protocol dissectors allow you to just that, they allow you to define your protocol in
Wireshark. When I first started looking for information on the subject all I found was
some vague guides on how to write a dissector using C++. C++ being bad enough as it is,
the efforts to set up a working build environment was even worse and given the limited
time resources I had I decided to put the idea on hold for a while… until I learned you can
also created protocol dissectors using the Lua language.
Out of the box Wireshark does not enable Lua support, so the first step is to enable it.
I do most of my development on a Windows machine so if you’re doing this on Linux or MacOS
there might be some small differences, but the basic idea behind it remains the same.
On Windows you’ll find a file called init.lua in the Wireshark installation directory,
I’m not sure where this file is located on Linux (perhaps in the /etc/ directory ?).
Open this file with your favorite text editor. I’m using LuaEdit, but any editor will do.
Somewhere at the top of the file you’ll find the following lines:
-- If set and we are running with special privileges this setting
-- tells whether scripts other than this one are to be run.
run_user_scripts_when_superuser = false
Since you’ll most likely be running Wireshark with administrator priviliges on Windows,
you have to set this value to true, otherwise Lua support will be disabled.
By default only the init.lua file will be parsed, you have to manually include the files you want to load. We’ll be making a single Lua file to represent our protocol, let’s call it protosample.lua.
At the very bottom of the init.lua file, you’ll find the following
dofile("console.lua")
You need to update this to include our own Lua script:
dofile("console.lua")
dofile("protosample.lua")
With the basic configuration set up, we’re good to go.
In order to keep things simple, we’ll work with a fictional protocol with only a few fields.
Usually you’ll send a series of bytes across the network which represent what we like to call protocol messages. In order to identify messages in the stream of bytes we need a start and stop byte. In between have the data that make up the various fields of the message.
(The start and stop bytes are not really required when using TCP, but we support RS232)
Consider the following message structure:
|START|SRC|DST|CMD|LENMSB|LENLSB|DATA 0…N|CRCMSB|CRCLSB|CRCMSB|END|
Each section in the line above (except for data) represents a single byte.
So every package will have a length of 10 + N bytes. (Keep in mind it’s purely fictional).
As you can see the LEN and CRC fields are represented by 2 bytes each, this is important
when we define our fields in the protocol dissector.
Create a new file (protosample.lua) in the Wireshark directory.
You start off by creating an instance of the Proto class:
sampleProtocol = Proto("sampleprotocol","Sample Protocol")
The first parameter of the initializer is the name of the protocol, the second a description.
Next you have to define what the fields are in a protocol message (basically the TCP package).
sampleProtocol = Proto("sampleprotocol","Sample Protocol")
local fields = ledProtocol.fields
fields.start = ProtoField.uint8("sampleProtocol.Start","Start Byte")
fields.source = ProtoField.uint8("sampleProtocol.Source","Source")
fields.destination = ProtoField.uint8("sampleProtocol.Destination","Destination")
fields.command = ProtoField.uint8("sampleProtocol.Command","Command")
fields.length = ProtoField.uint16("sampleProtocol.Length","Data Length")
fields.data = ProtoField.bytes("sampleProtocol.Data","Data")
fields.crc = ProtoField.uint16("sampleProtocol.Crc","CRC")
Every field you define needs to have a type. Single byte fields can be represented by the uint8 type, two byte fields (such as Length and Crc) can be represented by the uint16 type and finally the data bytes can be represented by the bytes type. You can find a full list of supported types in the init.lua file (Search for “Field Types”).
The hard work is done in the dissector function. It takes 3 parameters: buffer, pinfo and tree.
The buffer parameter contains the data from the captured package. The pinfo parameter can be used to provide more information about the protocol. (have not played with this much yet).
Finally the tree parameter gives you access to the visual treeview in Wireshark.
function sampleProtocol.dissector(buffer, pinfo, tree)
-- Set the text in the protocol column
pinfo.cols.protocol = sampleProtocol.name
-- Create a new subtree in the treeview and get a reference
local subtree = tree:add(sampleProtocol,buffer())
-- Create a variable to track the current offset in the buffer
local offset = 0
-- Next add all the fields to the subtree
subtree:add(fields.start,buffer(offset,1))
offset += 1
subtree:add(fields.source,buffer(offset,1))
offset += 1
subtree:add(fields.destination,buffer(offset,1))
offset += 1
subtree:add(fields.command,buffer(offset,1))
offset += 1
local dataLength = buffer(offset,2):uint()
subtree:add(fields.length,dataLength)
offset += 2
subtree:add(fields.data,buffer(offset,dataLength)
offset += dataLength
subtree:add(fields.crc,buffer(offset,2)
end
I kept the sample very basic so it would be clear what’s happening here.
Of course in reality you’d probably want to add additional logic to check the validity of the fields, display to the user which command the message is calling, etc…
There are two more steps involved in order to get this dissector working.
First you will need to add an init function (so you can set the packet_counter value):
local packet_counter
function sampleProtocol.init()
packet_counter = 0
end
Second you need to register the dissector in the Wireshark TCP table (at the end of the file):
tcp_table = DissectorTable.get("tcp.port")
tcp_table:add(33000,sampleProtocol)
Basically it means that if data is sent or received using/on TCP port 33000,
decode it using the sampleProtocol dissector.
Once you’ve saved the file in the Wireshark, restart the application and start
transmitting some protocol data on the port you specified.
You can even filter on “sampleprotocol” to hide all other packets that are captured.
Since this is not an example of a real protocol, I’ve taken a screenshot of a dissector for
one of our older communication protocols (which was also made with RS232 in mind):
As you can see, the packet gets decoded and all the fields are displayed properly.
The root item (marked yellow) not only displays the name of the protocol, but it also displays
the type of protocol message this packet represents.
This is done by adding text to the ‘subtree’ directly:
subtree:append_text(", " .. messageTypeName)
In this case the messageTypeName is a string which was created after evaluating the
Message Type field. The ‘..’ operator simply concatenates two strings in Lua
You can create much more powerful dissectors with Lua, but I’ve kept this example as simple
as possible. It should be enough to get you started, the rest is up to you.