Wireshark插件开发

原文地址:http://blog.csdn.net/qgw_2000/article/details/5327519

以下是针对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).插件入口函数

[cpp]  view plain copy
  1. G_MODULE_EXPORT void plugin_register (void);  
  2. G_MODULE_EXPORT void plugin_reg_handoff(void);  
 
    Wireshark启动时会加载所有$installdir/plugins/$buildversion/*.dll,并且调用每个dll导出的这两个函数。
    插件在这两个函数里完成dissector的注册和初始化工作,其中plugin_register注册插件,plugin_reg_handoff注册协议。

    插件注册:
    i.注册协议名称

[cpp]  view plain copy
  1. int proto_register_protocol(const char *name, const char *short_name, const char *filter_name)  

    参数:
        name:协议名称
        short_name:协议名称简称
        filter_name:filter中使用的协议名称
    返回值:
        协议ID,后续注册相关的函数都会用到它
    工作流程:
        a.将名称插入存放协议名称的Hash表中,避免相同名称的协议
        b.创建protocol_t对象并将其加入全局表(一个维护所有协议的全局链表)
        c.创建header_field_info对象并调用proto_register_field_init注册并返回注册ID

    Sample code:

[cpp]  view plain copy
  1. proto_myprotocol = proto_register_protocol (  
  2.     "MyProtocol Packet",        /* name       */  
  3.     "MyProcotol (myprotocol)",  /* short name */  
  4.     "myprotocol"                /* abbrev     */  
  5.     );  

    简单描述一下header_field_info注册函数:

[cpp]  view plain copy
  1. static int proto_register_field_init(header_field_info *hfinfo, int parent)  
 
    参数:
        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结构,以下是它们两的定义。

[cpp]  view plain copy
  1. /* Structure for information about a protocol */  
  2. struct _protocol {  
  3.     const char *name;      /* long description */  
  4.     const char *short_name;  /* short description */  
  5.     const char *filter_name; /* name of this protocol in filters */  
  6.     int proto_id;          /* field ID for this protocol */  
  7.     GList *fields;          /* fields for this protocol */  
  8.     GList *last_field;      /* pointer to end of list of fields */  
  9.     gboolean is_enabled;  /* TRUE if protocol is enabled */  
  10.     gboolean can_toggle;  /* TRUE if is_enabled can be changed */  
  11.     gboolean is_private;  /* TRUE is protocol is private */  
  12. };  
 
        前三个成员分别对应注册时提供的三个name,proto_id为注册时返回的ID,fields和last_field指向header_field_info链表的头和尾。后面会看到header_field_info的注册过程。

[cpp]  view plain copy
  1. /** information describing a header field */  
  2. struct _header_field_info {  
  3.     /* ---------- set by dissector --------- */  
  4.     const char  *name;           /**< full name of this field */  
  5.     const char  *abbrev;         /**< abbreviated name of this field */  
  6.     enum ftenum   type;           /**< field type, one of FT_ (from ftypes.h) */  
  7.     int              display;        /**< one of BASE_, or number of field bits for FT_BOOLEAN */  
  8.     const void  *strings;        /**< value_string, range_string or true_false_string, 
  9.                                           typically converted by VALS(), RVALS() or TFS(). 
  10.                                           If this is an FT_PROTOCOL then it points to the 
  11.                                           associated protocol_t structure */  
  12.     guint32    bitmask;        /**< bitmask of interesting bits */  
  13.     const char  *blurb;          /**< Brief description of field */  
  14.   
  15.     /* ------- set by proto routines (prefilled by HFILL macro, see below) ------ */  
  16.     int        id;             /**< Field ID */  
  17.     int        parent;         /**< parent protocol tree */  
  18.     hf_ref_type   ref_type;       /**< is this field referenced by a filter */  
  19.     int        bitshift;       /**< bits to shift */  
  20.     header_field_info *same_name_next; /**< Link to next hfinfo with same abbrev */  
  21.     header_field_info *same_name_prev; /**< Link to previous hfinfo with same abbrev */  
  22. };  

    _header_field_info结构比较重要,后续如何显示协议中的各个field会用到它。
    下面代码会看到,对协议中的每个域都会注册一个header_field_info对象,它描述了此协议域的显示名称,filer中的使用的名称,数据类型以及显示格式等等。

    这里首次用到它是注册协议,此时_header_field_info.type为FT_PROTOCOL,string指向protocol_t对象并且parent为-1。

 

    其它显示相关的header fields的注册:

[cpp]  view plain copy
  1. static hf_register_info hf[] =  
  2. {  
  3.     {  
  4.         &hf_myprotocol_type,  
  5.         {  
  6.             "Packet Type""myprotocol.type",  
  7.             FT_UINT16, BASE_HEX,  
  8.             VALS(packettypenames), 0x0,  
  9.             NULL, HFILL  
  10.         }  
  11.     },  
  12.     {  
  13.         &hf_myprotocol_checksum,  
  14.         {  
  15.             "Checksum""myprotocol.checksum",  
  16.             FT_UINT16, BASE_HEX, NULL, 0x0,  
  17.             NULL, HFILL  
  18.         }  
  19.     },  
  20.     {  
  21.         &hf_myprotocol_owner,  
  22.         {  
  23.             "Owner""myprotocol.owner",  
  24.             FT_ETHER, BASE_NONE, NULL, 0x0,  
  25.             NULL, HFILL  
  26.         }  
  27.     },  
  28.     ...  
  29. };  
  30.   
  31. proto_register_field_array(proto_myprotocol, hf, array_length(hf));  
  32. proto_register_field_array(proto_myprotocol, hf_tuple, array_length(hf_tuple));  

    这里proto_register_field_array首先将header field对象插入到protocol_t.fields和last_fields所指向的链表中,然后调用proto_register_field_init进行真正的注册。

    ii.协议注册
        

[cpp]  view plain copy
  1. dissector_handle_t create_dissector_handle(dissector_t dissector, int proto)  
 
    参数:
        dissector:函数指针,负责处理数据包解析和显示,原型 typedef void (*dissector_t)(tvbuff_t *, packet_info *, proto_tree *);
        proto:协议ID,注册时返回
    返回值:
        dissector_handle_t对象用于真正的注册

        

[cpp]  view plain copy
  1. void dissector_add(const char *name, guint32 pattern, dissector_handle_t handle)  

    添加dissector_handle_t对象到系统的dissector table中,其中name为对于二层协议为ethertype,pattern为协议ID。

    Sample Code:

[cpp]  view plain copy
  1. dissector_handle_t myprotocol_handle;  
  2.   
  3. myprotocol_handle = create_dissector_handle(dissect_myprotocol, proto_myprotocol);  
  4. dissector_add("ethertype", ETHERTYPE_MyProtocol, myprotocol_handle);  
 
        其中ETHERTYPT_MyProtocol是以太网帧中协议ID。

 

    2).dissector入口函数
        

[cpp]  view plain copy
  1. static void dissect_myprotocol(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)  
 
    参数:
        tvb:数据包buffer指针,可以通过以下这些函数读取内容,注意字节序。
[cpp]  view plain copy
  1. guint8 tvb_get_guint8(tvbuff_t *tvb, gint offset);  
  2. guint16 tvb_get_ntohs(tvbuff_t *tvb, gint offset);  
  3. guint32 tvb_get_ntoh24(tvbuff_t *tvb, gint offset);  
  4. guint32 tvb_get_ntohl(tvbuff_t *tvb, gint offset);  
  5. guint64 tvb_get_ntoh64(tvbuff_t *tvb, gint offset);  

        pinfo:包括各种关于数据包和协议显示的相关信息,我们关注的是显示相关的信息如Wireshark界面上的protocol和info两个栏位。
            可以使用下面的函数对protocol和info栏位进行操作:

[cpp]  view plain copy
  1. col_set_str(pinfo->cinfo, COL_PROTOCOL, "MyProtocol");  
  2. col_clear(pinfo->cinfo, COL_INFO);  
  3. col_add_fstr(pinfo->cinfo, COL_INFO, "Type %s",  
  4.              val_to_str(packet_type, packettypenames, "Unknow (0x%04x)"));  
 
        tree:在Wireshark界面上点击某个数据包,在界面下方会以树状结构显示数据包的详细信息。tree就是描述这个树状结构的数据结构。
              当tree不为NULL时,我们就必须按照具体的协议添加相应的节点。这也是整个插件的主要工作。

        首先为协议添加一个subtree节点:

[cpp]  view plain copy
  1. myprotocol_item = proto_tree_add_item(tree, proto_myprotocol, tvb, 0, -1, FALSE);  
  2. protocol_tree = proto_item_add_subtree(myprotocol_item, ett_myprotocol);  
 
            其中proto_myprotocol是注册的协议ID,ett_myprotocol是在注册阶段注册的ID,如下所示:
[cpp]  view plain copy
  1. static gint *ett[] =  
  2. {  
  3.     &ett_ctsp,  
  4. };   
  5. proto_register_subtree_array(ett, array_length(ett));  

        往此subtree节点上添加子节点:

[cpp]  view plain copy
  1. /* display the type */  
  2. proto_tree_add_item(myprotocol_tree, hf_myprotocol_type, tvb, offset, 2, TRUE);  
  3. offset += 2;  
  4. /* display the checksum */  
  5. proto_tree_add_item(myprotocol_tree, hf_myprotocol_checksum, tvb, offset, 2, FALSE);  
  6. offset += 2;  
  7. /* display the owner */  
  8. proto_tree_add_item(myprotocol_tree, hf_myprotocol_owner, tvb, offset, 6, FALSE);  
 
        其中hf_myprotocol_*是在注册的header_field_info的ID,proto_tree_add_item会根据对应的header_field_info显示相应的协议域。

    3).其它问题
    Wireshark的developer guide中有一个UDP协议dissector的例子。另外还提到分片和协议解压解密等问题的解决方法。
    开发中还碰到对于tunnel协议的处理等都可以参考现有的dissector例子进行解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值