Wireshark Dissector with Lua

Posted: November 23, 2013 in Wireshark
Tags: , ,

At work I was told to write a Wireshark dissector (dissector.. yeah I thought he was joking).

What is a dissector and why would you need one?

A Wireshark dissector is essentially a decoder for protocols to a format that is nicely displayed on the Wireshark console. Wireshark automatically calls dissectors for the data link layer protocol, the IP protocol, and the TCP protocol, hence the nicely formatted output on those protocol layers.

Obviously Wireshark doesn’t have a dissector for the TCP payload, however you can write a plugin that tells Wireshark how to interpret the payload. In other words, we want to get from this horrible looking yoke:

first

To here:
second

Unfortunately, I didn’t find writing a dissector to be all that straightforward. There were examples by developers who assumed you knew what you were doing. I will try to explain what I learnt when writing a dissector plugin and save yourself a lot of hassle in the process.

Ways to write a dissector plugin:

C Dissector Plugin
Wireshark is written in C and thus fast and efficient… but when you combine a lack of well documented dissector C API and C pointers it becomes very difficult to get anything working; and if you are a C# developer like myself you may sense a cloud of depression.
Another reason why I wouldn’t recommend this route is the amount of time it takes to actually write a plugin.
You have to download and install Visual Studio C++ 2010 SP1 in a certain order and hope you didn’t break anything. Then you have to install Cygwin with tools and dependencies (seriously, it’s annoying when you run a tool only to get a dependency error).
You also have to download SVN, checkout the Wireshark source into the trunk directory. To build the source project and your plugin you must configure NMake.

If you’re lucky to get everything working after a couple of hours, with a test plugin ready, you have to wait give or take 15 minutes for the build to finish.
If you are a masochist then by all means follow this hellish path (ok, maybe I exaggerate). However, if you are sane… run away!

Wireshark Generic Dissector
Offers a thin abstraction layer over C. Download a generic.dll file and place it in your WiresharkDir/plugins folder. You create two files: the format description file (.fdesc) and WSGD file (.wsgd).

I am not sure how it works, but I am sure there is a translation layer to C code going on somewhere so that it can be used by Wireshark. But I have to say, for an abstraction over native C, it isn’t nicely documented and attractive for use. To be honest, I might as well stick with C.

Pyreshark
Written in Python. I was initially happy to find this script, and not only because of its abstraction over C, but I was able to find a good amount of documentation. When I actually got a fully dissector script written (the example on the Pyreshark page actually), I couldn’t find a way to filter the example protocol by TCP port… So disappointing and a waste of time, but at least I finally had some inkling on how a dissector worked. Pyreshark has a lot of potential, so if you know how to apply a port filter, please let me know!

And Finally… Lua
Before this, I never heard of Lua. Although, the Developer’s Guide mentions Lua, I shied away from it. Lua, sounds so strange and foreign, yet surprisingly easy to work with. Also has better documentation than its C counterpart, assuming there exists one for C.

Wireshark has a built in Lua interpreter, and on start-up loads up any .lua files in the Wireshark/plugins/1.*.* directory. It is faster than WSGD, obviously slower than C but less effort to work with.

Mist Protocol
Now, imagine that you were developing a revolutionary chat application (allows you to chat without typing maybe?), we’d need our own chat protocol, so let’s give it a fancy name – Mist.
The Mist Protocol is simple, a message header of

    message type – 2 bytes unsigned integer
    User – a fixed string of 5 bytes

In total our message header is just 7 bytes.

For the sake of simplicity for now our “revolutionary” chat application supports 3 messages: Connect, Disconnect and ChatMessage.
In pseudo code:

enum MessageType
Connect = 0x02
Disconnect
ChatMessage

struct MessageHeader
ushort messageType;
byte[7] User; -- Username

struct Connect
MessageHeader msgHeader

struct Disconnect
MessageHeader msgHeader

struct ChatMessage
MessageHeader msgHeader
ushort MessageLen
byte[] Message

Connect and Disconnect are more or less the same while ChatMessage contains extra bits.

To create packets I just wrote a little client server in C# to send data using the loopback address but had to capture the packets using RawPackets as Wireshark listens on the NIC level in comparison to RawPackets which listens on the socket level; if you know of a software that allows you to generate packets on Windows for testing please let me know.

The first step is to create the Mist protocol and define all the message fields that are supported in the Mist protocol. The protocol has a fields attribute – a collection of protocol fields:
The code in Lua is like so:

mist_protocol = Proto("Mist", "The Chat Protocol")
msg_type =ProtoField.uint16("Mist.MessageType","MessageType",base.DEC)
user = ProtoField.string("Mist.User","User","Text")
msglen = ProtoField.uint8("Mist.Length","Message Length",base.DEC)
message = ProtoField.string("Mist.Message","Message","Text")
mist_protocol.fields = {msg_type,user,msglen,message}

Next step is to create a callback function that will actually decode the TCP payload. Well, we actually extract the fields from the packet and build a tree based on what message the packet contains. Remember, we know what the packet contains based on the message type – a 2 byte unsigned integer.

The dissector callback takes a buffer, the packet info and a tree:


function mist_protocol.dissector(buffer, pinfo, tree)
-- This makes MIST appear in the protocol column, when a packet is loaded by Wireshark
pinfo.cols.protocol = mist_protocol.name;

subtree = tree:add(mist_protocol,buffer()) --add our message subtree to Wireshark's display

mtype = buffer(0,2):le_uint() -- extract the message type in little endian format.
mtype_str = "Connect"

if mtype == 3 then mtype_str = "Disconnect" end
if mtype == 4 then mtype_str = "ChatMessage" end

subtree:add_le(msg_type,buffer(0,2)):append_text(" (" .. mtype_str .. ")")
subtree:add(user,buffer(2,5))

if mtype == 4 then
subtree:add_le(msglen,buffer(7,2))
subtree:add(message,buffer(9,buffer(7,2):le_uint()))
end
end

The code simply appends a protocol field to our subtree, and the subtree is added to the main visual tree – at least that’s how I think of it.
So, we extract the message type and because the bytes for integers were sent in big endian byte order I had to extract them in little endian byte order.
Due to the extra info we have for ChatMessage we check that the TCP payload is of type ChatMessage and if so we add extra Proto fields to our subtree to represent the message length and the message body itself.

If I were to think of it in C# space, it’d be:

Tree.List Subtrees i.e new Tree().Subtrees.Add(new Subtree())
Subtree.List ProtoFields i.e. new Subtree.ProtoFields.Add(new ProtoField())

To be honest, and if it is possible, please let me know I’d prefer:


Subtree.List Messages
Message.List ProtoFields

I think that makes things a bit clearer.

Finally, we need to assign a port to our protocol. To do this is very simple:


local tcp_port = DissectorTable.get("tcp.port")
tcp_port:add(8888,mist_protocol)

Place your .lua script in WiresharkDir/plugins/1.*.*/

Filter the packets by the port number tcp.port == 8888, or simply type Mist.

Now, when you click on a packet or when our Mist packets are loading, Wireshark invokes the mist_protocol.dissector function. Bam! And we are done.Too awesome.

Again, any info on Pyreshark or designing a dissector in an object oriented paradigm can be sent to me 🙂

Download chatdissector.lua and chatpacket.pcap files

This is my first attempt at blogging. Suggestions on how to improve are well appreciated!
Thanks for reading. Now, go play with Wireshark and dissectors.

Comments
  1. Gaurav Roy says:

    Hey, you just saved my skin!
    Thanks a lot for the relevant comments and simple explanation!!

Leave a comment