|
|||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
1.0 IntroductionWireshark is a powerful open source tool used to dissect Ethernet packets. Have you ever wondered what it takes to implement your own custom dissector? Furthermore, have you attempted to learn Wireshark's API and found it difficult to understand? This article will attempt to demystify the development of your very own protocol dissector. This article uses Amin Gholiha's "A Simple IOCP Server/Client class" [^] as a basis for dissection, thus producing the AMIN protocol. 1.1 Requirements
2.0 Configure Wireshark Build Environment (Win32)The Wireshark developer's guide [^] features a section on setting up the Win32 environment, which I found to be invaluable. This section will paraphrase much of the information found there. Step 1. C CompilerIf you do not have VS2005/VS2003 etc., you will need to download and install "Visual C++ 2005 Express Edition"[^]. Step 2. Platform SDKYou must download and install the Platform SDK Server 2003 R2[^]. Step 3. Install CygwinThis guide will not go into great details about the Cygwin package. In short, it allows Wireshark to be compiled on Windows and Linux – which is quite a feat. At the "Select Packages" page, you will need to select some additional packages which are not installed by default. Navigate to the required Category/Package row, and click on the "Skip" item in the "New" column so it shows a version number for:
After clicking the Next button several times, the setup will then download and install the selected packages (this may take a while). Step 4. Install PythonGet the Python 2.4 installer and install Python into the default location. Note: Python 2.5 doesn't work out of the box, so avoid it. Step 5. Install Subversion ClientSubversion is not required to build Wireshark, but it is required to follow this article. Subversion allows you to grab the latest source to work with. My personal choice for a subversion client is TortoiseSVN[^]. Download and install TortoiseSVN[^]. You may need to reboot after the installation to enable the context menu options. This is a pretty nifty feature that lets you right-click on folders in the "explorer" view and grab a source tree.
Step 6. Get the Latest Wireshark SourceYou may be wondering at this point why you are getting the entire source tree just to make a dissector. The source tree contains the "plugins" directories that contain examples and a place to build your dissector.
Step 7. Edit the config.nmakeOpen C:\Wireshark\config.nmake using Notepad or your favorite text editor. The following sections must be updated:
# "Microsoft Visual Studio 6.0" - RECOMMENDED # Visual C++ 6.0, _MSC_VER 1200, msvcrt.dll (version 6) #MSVC_VARIANT=MSVC6 # "Microsoft Visual Studio .NET (2002)" - WORKS # Visual C++ 7.0, _MSC_VER 1300, msvcr70.dll #MSVC_VARIANT=MSVC2002 # "Microsoft .Net Framework SDK Version 1.0" - WORKS # needs additional Platform SDK installation # Visual C++ 7.0, _MSC_VER 1300, msvcr70.dll #MSVC_VARIANT=DOTNET10 # "Microsoft Visual Studio .NET 2003" - WORKS # Visual C++ 7.1, _MSC_VER 1310, msvcr71.dll #MSVC_VARIANT=MSVC2003 # "Microsoft .Net Framework SDK Version 1.1" - WORKS # needs additional Platform SDK installation # Visual C++ 7.1, _MSC_VER 1310, msvcr71.dll #MSVC_VARIANT=DOTNET11 # "Microsoft Visual Studio 2005" - WORKS # Visual C++ 8.0, _MSC_VER 1400, msvcr80.dll MSVC_VARIANT=MSVC2005 <------ This is my compiler. All others are commented out. # "Microsoft Visual C++ 2005 Express Edition" - WORKS # needs additional Platform SDK installation # Visual C++ 8.0, _MSC_VER 1400, msvcr80.dll #MSVC_VARIANT=MSVC2005EE # "Microsoft .Net Framework 2.0 SDK" - WORKS # needs additional Platform SDK installation # Visual C++ 8.0, _MSC_VER 1400, msvcr80.dll #MSVC_VARIANT=DOTNET20 Step 8. Prepare cmd.exe
Your paths may be slightly different depending on the compiler you use. In the example zip file, you will find a step1.bat, step2.bat, and a step3.bat. I made these batch files to simplify this step. I know there are more fancy ways of constructing the batch files, so please feel free to submit your own. Step 9. Verify Tools are InstalledFrom the C:\WireShark directory, execute: C:\wireshark> Nmake –f Makefile.nmake verify_tools
Your output should look like: cl: /cygdrive/c/Programme/Microsoft Visual Studio 8/VC/BIN/cl
link: /cygdrive/c/Programme/Microsoft Visual Studio 8/VC/BIN/link
nmake: /cygdrive/c/Programme/Microsoft Visual Studio 8/VC/BIN/nmake
bash: /usr/bin/bash
bison: /usr/bin/bison
flex: /usr/bin/flex
env: /usr/bin/env
grep: /usr/bin/grep
/usr/bin/find: /usr/bin/find
perl: /usr/bin/perl
env: /usr/bin/env
C:/python24/python.exe: /cygdrive/c/python24/python.exe
sed: /usr/bin/sed
unzip: /usr/bin/unzip
wget: /usr/bin/wget
If something is missing, you may have to repeat the Cygwin install. Step 10. Install LibrariesIf you have closed your cmd.exe, you will have to reopen it and execute Step 8. You can use the step1, step2, step3 batch files to simplify the process. From C:\Wireshark, execute: nmake –f Makefile.nmake setup (This step may take a little while to complete.)
nmake –f Makefile.nmake distclean
Step 11. Build WiresharkIf you have closed your cmd.exe, you will have to reopen it and execute Step 8. You can use the step1, step2, step3 batch files to simplify the process. nmake –f Makefile.nmake all (this step will take quite a while to complete)
You're done; provided the above steps worked! Step 12. (Optional) Create a Wireshark InstallerI like the idea of distributing my version to friends. You can easily create an installer by doing the following:
nmake –f Makefile.nmake packaging
3.0 Execute the Compiled WiresharkTo launch Wireshark, you simply can run C:\wireshark\wireshark-gtk2\wireshark.exe and check if it starts. I found that I needed to download and install Wireshark before my build worked. This is probably due to not having WinPcap installed prior. You can download and install Wireshark from here. Note: If you are using Visual Studio 2005, you will have to use your build of Wireshark to test the dissector. For some reason, compiling dissector plug-ins with Visual Studio 2005 prevents their use in the mainstream release of Wireshark. If you are using a different compiler, you may not have to use the compiled version of Wireshark. 4.0 Understanding the AMIN ProtocolAmin was nice enough to code up an excellent IOCP client/server example. His code uses a very simple protocol to transmit data via TCP. This article is designed for the beginner, so we will only be dissecting the text messages sent from the client/server. The file transfer messages are a tad more complex, so we will ignore those for now.
4.1 Package Length PrefixAll of Amin's packets are prefixed with a four (4) byte long value which indicates the size of the package. This value is presented in network order over the wire, or, MSB. Network order means that the bytes are ordered in such a way that the most significant bytes are first. For example, let's say your length is 12 decimal. This would be 0x0000000c in hex (if you are storing the value in a long). If you read this value off the "wire", it would appear as 0c 00 00 00; this is "Network" order. You may be wondering why the bytes are reversed. If you think about it as an array of bytes, instead of a long, each byte is written in order. In this case, byte[0] is 0x0c, so it's written first. This is an important concept to understand. When we use Wireshark to dissect the packet, it is important to understand that 2 and 4 byte integer values are always written in "Network" order, or LSB. In other protocols, the order may be Host – which would be LSB. Note: The length accounts for the bytes to follow, meaning that the four (4) bytes which represent the length value are not included. For example, if you sent a packet that has 12 characters of text, the length would be 12+1 = 13 (the one byte is the type), as opposed to 12+1+4 = 17. 4.2 TypeAmin follows the length prefix with a byte that indicates the "type" of the packet he is delivering. We will only be dealing with type 0x00, which is text. Note: a single byte is not subject to byte order because it's a single byte. 4.3 Null Terminated StringThis field is simply a null terminated string which can be entered into Amin's MFC GUI. The default string is 'ABCDEFGHIJKLMNOPQRST123456789'. 5.0 Examining the AMIN protocol in Wireshark without a custom dissectorYou can download the Simple IOCP Client/Server written by Amin here. Note: This article does not discuss the basic use of Wireshark. This article assumes you have used it before and understand how to use it in a basic sense. Here is what Wireshark looks like without a dissector for the AMIN protocol:
Notice that we are simply given a field called "Data". Within the data portion, we can recognize our AMIN protocol based on the "1f 00 00 00 00" package length. The type follows, and after that is the "ABC.." ASCII. 6.0 Writing Your Own DissectorThe Wireshark source tree contains a directory called Plugins, which provides a reasonable amount of examples. However, it was impossible to find a really "simple" example to use. So, in combination with the H223 dissector, random examples from the internet, and the developer guide, I have prepared a simple example and placed it in the source zip file. The example can be found in the AMIN\ directory. 6.1 The make FilesIn order to compile your own protocol, you must create a set of files to compile your dissector. The easiest thing to do is take the following files from the AMIN\ directory in the source zip file.
Once you have copied the files into C:\Wireshark\plugins\yourprotocolname, you can begin editing them.
7.0 Your Dissector CodeYou can use a text editor of your choice to open packet-yourprotocol.c. Let's take it line by line: #ifdef HAVE_CONFIG_H # include "config.h" #endif #include <stdio.h> #include <glib.h> #include <epan/packet.h> #include <string.h> All dissectors use some standard headers. You need the config.h, glib.h, and packet.h for sure. #define PROTO_TAG_AMIN "AMIN" I like to avoid hard coding constants when I can. /* Wireshark ID of the AMIN protocol */ static int proto_amin = -1; This value is very important to Wireshark. Wireshark uses this to identify our protocol. /* These are the handles of our subdissectors */ static dissector_handle_t data_handle=NULL; static dissector_handle_t amin_handle; The dissector handle is what Wireshark uses to reference this dissector. void dissect_amin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
This is a forward declaration of our dissection function. We will pass this function to a registration function later on. static int global_amin_port = 999; This is the port that Wireshark will use to determine if the packet belongs to the AMIN protocol. static const value_string packettypenames[] = { { 0, "TEXT" }, { 1, "SOMETHING_ELSE" }, { 0, NULL } }; Here is where we add some optional text strings representing packet types. You can define many of these depending on the needs of your own protocol. It adds a level of detail that makes the dissector look well thought out. static gint hf_amin = -1; static gint hf_amin_header = -1; static gint hf_amin_length = -1; static gint hf_amin_type = -1; static gint hf_amin_text = -1; /* These are the ids of the subtrees that we may be creating */ static gint ett_amin = -1; static gint ett_amin_header = -1; static gint ett_amin_length = -1; static gint ett_amin_type = -1; static gint ett_amin_text = -1; These allow us to attach IDs to the subcomponents of our protocol. void proto_reg_handoff_amin(void) { static gboolean initialized=FALSE; if (!initialized) { data_handle = find_dissector("data"); amin_handle = create_dissector_handle(dissect_amin, proto_amin); dissector_add("tcp.port", global_amin_port, amin_handle); } } This function is called to register our protocol. Notice how the port and dissector handle are passed. void proto_register_amin (void) { /* A header field is something you can search/filter on. * * We create a structure to register our fields. It consists of an * array of hf_register_info structures, each of which are of the format * {&(field id), {name, abbrev, type, display, strings, bitmask, blurb, HFILL}}. */ static hf_register_info hf[] = { { &hf_amin, { "Data", "amin.data", FT_NONE, BASE_NONE, NULL, 0x0, "AMIN PDU", HFILL }}, { &hf_amin_header, { "Header", "amin.header", FT_NONE, BASE_NONE, NULL, 0x0, "AMIN Header", HFILL }}, { &hf_amin_length, { "Package Length", "amin.len", FT_UINT32, BASE_DEC, NULL, 0x0, "Package Length", HFILL }}, { &hf_amin_type, { "Type", "amin.type", FT_UINT8, BASE_DEC, VALS(packettypenames), 0x0, "Package Type", HFILL }}, { &hf_amin_text, { "Text", "amin.text", FT_STRING, BASE_NONE, NULL, 0x0, "Text", HFILL }} }; The array above defines what elements we will be displaying. These declarations are simply a definition Wireshark uses to determine the data type, when we later dissect the packet. static gint *ett[] = {
&ett_amin,
&ett_amin_header,
&ett_amin_length,
&ett_amin_type,
&ett_amin_text
};
The array above simply attaches IDs to our definitions. Notice the 1:1 relationship of the proto_amin = proto_register_protocol ("AMIN Protocol", "AMIN", "amin"); proto_register_field_array (proto_amin, hf, array_length (hf)); proto_register_subtree_array (ett, array_length (ett)); register_dissector("amin", dissect_amin, proto_amin); The above code registers our protocol. In most of the examples I saw, they check to see if static void dissect_amin(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { The dissect function is used to actually dissect and display the packet details. First, some points are initialized to trees/items: proto_item *amin_item = NULL;
proto_item *amin_sub_item = NULL;
proto_tree *amin_tree = NULL;
proto_tree *amin_header_tree = NULL;
guint16 type = 0;
This next call checks to see if the INFO column is displaying our "AMIN" tag. If it's not, then we supply it. if (check_col(pinfo->cinfo, COL_PROTOCOL)) col_set_str(pinfo->cinfo, COL_PROTOCOL, PROTO_TAG_AMIN); /* Clear out stuff in the info column */ if(check_col(pinfo->cinfo,COL_INFO)){ col_clear(pinfo->cinfo,COL_INFO); } //Here we check to see if the INFO column is present. If it is we output //which ports the packet came from and went to. Also, we indicate the type //of packet. // This is not a good way of dissecting packets. The tvb length should // be sanity checked so we aren't going past the actual size. type = tvb_get_uint8( tvb, 4 ); // Get the type byte if (check_col(pinfo->cinfo, COL_INFO)) { col_add_fstr(pinfo->cinfo, COL_INFO, "%d > %d Info Type:[%s]", pinfo->srcport, pinfo->destport, val_to_str(type, packettypenames, "Unknown Type:0x%02x")); } If there is a "tree" requested, we handle that request. if (tree) { /* we are being asked for details */ guint32 offset = 0; guint32 length = 0; This call adds our tree to the main dissection tree. amin_item = proto_tree_add_item(tree, proto_amin, tvb, 0, -1, FALSE); amin_tree = proto_item_add_subtree(amin_item, ett_amin); amin_header_tree = proto_item_add_subtree(amin_item, ett_amin); amin_sub_item = proto_tree_add_item( amin_tree, hf_amin_header, tvb, offset, -1, FALSE ); Here, we add our own subtree, so we can have a "header" collapsible branch. Following the subtree, we copy out the length bytes to our data type. The order of the long value representing length is "network" order. If someone has a more appropriate amin_header_tree = proto_item_add_subtree(amin_sub_item, ett_amin); //We use tvb_memcpy to get our length value out (Host order) tvb_memcpy(tvb, (guint8 *)&length, offset, 4); proto_tree_add_uint(amin_header_tree, hf_amin_length, tvb, offset, 4, length); //We increment the offset to get past the 4 bytes indicating length offset+=4; //Here we submit the type parameter to the tree. proto_tree_add_item(amin_header_tree, hf_amin_type, tvb, offset, 1, FALSE); type = tvb_get_guint8( tvb, offset ); // Get our type byte offset+=1; //If the type is TEXT, we add the text to the tree. if( type == 0 ) { proto_tree_add_item( amin_tree, hf_amin_text, tvb, offset, length-1, FALSE ); } 7.1 Compiling packet-yourprotocol.cIf your command line window is still open, you can use that, or use step1/2/3.bat to arrive at the c:\wireshark\plugins\yourprotocol directory. If you are building my source code, you should be at c:\wireshark\plugins\amin. From the "prepared" command line, see Step 8. Execute: nmake -f Makefile.nmake distclean
nmake -f Makefile.nmake all
If the build succeeds, you should have a yourprotocol.dll. Simply copy this file to your C:\WireShark\wireshark-gtk2\plugins\0.99.7-YOUR-BUILD directory. There should be a bunch of other dissector DLLs present there. You may need to copy it to your c:\program files\wireshark\plugins\0.99.7-YOUR-BUILD directory if you installed it there. The bottom line is to copy it in the appropriate directory based on how you are launching Wireshark. At this point, you should be able to launch Wireshark and dissect packets. A good test is to enter "yourprotocol" in the "filter" area of Wireshark and see if a green or red background forms. A green background, just like in the screenshots of this article, indicates that the dissector loaded correctly.
This is a screen capture of the AMIN protocol being dissected by Wireshark. 8.0 Tips8.1 CodeBaseI found an immense amount of examples in a searchable form at CodeBase[^]. Without this site, it would have been a much more difficult task locating functions for dissection. Someone really should spend time documenting the Wireshark API in a more formal manner. A coworker informed me that CodeBase is a pay service. I was able to get into CodeBase beta without having to authenticate. Use this link to do the same (use the search box to find undocumented methods). 8.2 Wireshark FilteringAfter you've defined your data type map, you can use the '.' operator to limit the packets being displayed. For example, amin.type==0 will only show packets where the type equals zero. 8.3 Other ExamplesThere are far more complex examples that can be found in the plugins directory. My suggestion is to check out the H223 dissector. Topics such as TCP segmentation are covered, as well as managing states across combinations of packets (i.e., watching a SIP session throughout multiple packets). 8.4 Capture Loopback PacketsIf you wish to locally capture packets using Wireshark, i.e., 127.0.0.1, you must perform a few extra steps. I found this link which helps solve the problem. The required steps, per the site suggestion, are:
arp -s 10.0.0.10 55-55-55-55-55-55
then: route add 10.0.0.10 10.0.0.10 mask 255.255.255.255
You can then test the capture by executing: telnet 10.0.0.10
9.0 ConclusionOnce the complexity has been removed from designing dissectors, it's quite easy to make your very own protocol present in Wireshark. In addition, the guys at Wireshark greatly appreciate those who take the time to legally reverse engineer protocols for inclusion within the Wireshark distribution. 10.0 References
11.0 History
| ||||||||||||||||||||||||||||