以下是针对Windows平台的插件开发,其它平台非常类似。
1.开发环境准备
1).下载Source code
使用TortoiseSVN,checkout代码(http://anonsvn.wireshark.org/wireshark/trunk)
TortoiseSVN还是相当好用的,直接和windows exploer集成,操作对象就是exploer中的文件和文件夹,
右键文件或文件夹就可进行管理。
2).编辑src/config.nmake
修改跟系统相关的变量,包括MSVC的安装路径,Python(2.x)路径等等
3).安装Cygwin,选择安装编译过程中需要的所有工具,包括bash、perl、sed等等。
4).检查编译过程中所需工具是否完备
>nmake -f Makefile.nmake verify_tools
5).安装Wireshark依赖的所有Library
>nmake -f Makefile.nmake
6).删除其他平台的代码
>nmake -f Makefile.nmake distclean
7).编译Wireshark
>nmake -f Makefile.nmake all
以上过程可以参考Wireshark的developer guide(http://www.wireshark.org/docs/wsdg_html_chunked/)。
2.插件开发
项目中使用了自己的私有协议。调试时会经常用tcpdump抓包然后在Windows上用Wireshark分析,为了方便就写个简单的插件。
关于插件开发Wireshark的developer guide有一些描述,但都比较有限,并且关于Wireshark中的各种API也没有文档描述。
还好Wireshark有很多插件,可以参考其中的插件代码进行分析调试。
项目里使用的是L2协议,所以参考WiMax的2层协议的dissector实现(src/plugins/m2m)。
3.开发步骤
这里说的插件(dissector)就是单独的dll,将dll放到特定位置,Wireshark启动时会加载每个dll完成注册。
这里简单记录一下大概的步骤。
1).插件入口函数
Wireshark启动时会加载所有$installdir/plugins/$buildversion/*.dll,并且调用每个dll导出的这两个函数。
插件在这两个函数里完成dissector的注册和初始化工作,其中plugin_register注册插件,plugin_reg_handoff注册协议。
插件注册:
i.注册协议名称
参数:
name:协议名称
short_name:协议名称简称
filter_name:filter中使用的协议名称
返回值:
协议ID,后续注册相关的函数都会用到它
工作流程:
a.将名称插入存放协议名称的Hash表中,避免相同名称的协议
b.创建protocol_t对象并将其加入全局表(一个维护所有协议的全局链表)
c.创建header_field_info对象并调用proto_register_field_init注册并返回注册ID
Sample code:
简单描述一下header_field_info注册函数:
参数:
hfinfo:header field对象
parent:header field对象注册后返回的ID,parent为-1时为注册父对象
返回值:
注册ID
工作流程:
a.将header field对象添加到一个全局数组中
b.将协议注册时的filter_name和header field对象指针添加到一个全局平衡树中,方便通过filter_name查找
c.返回当前的header field对象在全局数据中的索引
现在说说protocol_t和header_field_info结构,以下是它们两的定义。
前三个成员分别对应注册时提供的三个name,proto_id为注册时返回的ID,fields和last_field指向header_field_info链表的头和尾。后面会看到header_field_info的注册过程。
_header_field_info结构比较重要,后续如何显示协议中的各个field会用到它。
下面代码会看到,对协议中的每个域都会注册一个header_field_info对象,它描述了此协议域的显示名称,filer中的使用的名称,数据类型以及显示格式等等。
这里首次用到它是注册协议,此时_header_field_info.type为FT_PROTOCOL,string指向protocol_t对象并且parent为-1。
其它显示相关的header fields的注册:
这里proto_register_field_array首先将header field对象插入到protocol_t.fields和last_fields所指向的链表中,然后调用proto_register_field_init进行真正的注册。
ii.协议注册
参数:
dissector:函数指针,负责处理数据包解析和显示,原型 typedef void (*dissector_t)(tvbuff_t *, packet_info *, proto_tree *);
proto:协议ID,注册时返回
返回值:
dissector_handle_t对象用于真正的注册
添加dissector_handle_t对象到系统的dissector table中,其中name为对于二层协议为ethertype,pattern为协议ID。
Sample Code:
其中ETHERTYPT_MyProtocol是以太网帧中协议ID。
2).dissector入口函数
参数:
tvb:数据包buffer指针,可以通过以下这些函数读取内容,注意字节序。
pinfo:包括各种关于数据包和协议显示的相关信息,我们关注的是显示相关的信息如Wireshark界面上的protocol和info两个栏位。
可以使用下面的函数对protocol和info栏位进行操作:
tree:在Wireshark界面上点击某个数据包,在界面下方会以树状结构显示数据包的详细信息。tree就是描述这个树状结构的数据结构。
当tree不为NULL时,我们就必须按照具体的协议添加相应的节点。这也是整个插件的主要工作。
首先为协议添加一个subtree节点:
其中proto_myprotocol是注册的协议ID,ett_myprotocol是在注册阶段注册的ID,如下所示:
往此subtree节点上添加子节点:
其中hf_myprotocol_*是在注册的header_field_info的ID,proto_tree_add_item会根据对应的header_field_info显示相应的协议域。
3).其它问题
Wireshark的developer guide中有一个UDP协议dissector的例子。另外还提到分片和协议解压解密等问题的解决方法。
开发中还碰到对于tunnel协议的处理等都可以参考现有的dissector例子进行解决。