zoukankan      html  css  js  c++  java
  • 使用 lua 编写 wireshark 协议解析插件

    一、平台

    操作系统:windows 7

    wireshark:1.10.3

    lua:5.1

    二、准备

    lua 语言基本语法,特别是关于表操作和循环

    wireshark 文档,包括用户使用文档和开发者文档,这些在 wireshark 官方网站都能找到

    三、开始

    我们首先定义一个简单的协议,我们使用 C 语言的语法描述,

    复制代码
    1 struct foo
    2 {
    3     char protocol_type[16];     /* request response notify */
    4     char service_type[16];      /* 我们定义的各种服务类型 */
    5     unsigned int msg_len;       /* 消息体的长度 */
    6     char msg_content[0];        /* 消息体的内容,由于是变长的所以采用此方法定义 */
    7 };
    复制代码

    现在可以让我们使用 lua 定义一个最基本的框架

    复制代码
     1 do
     2     --[[
     3         创建一个新的协议结构 foo_proto
     4         第一个参数是协议名称会体现在过滤器中
     5         第二个参数是协议的描述信息,无关紧要
     6     --]]
     7     local foo_proto = Proto("foo", "Foo Protolcol")
     8     
     9     --[[
    10         下面定义字段
    11     --]]
    12     local foo_protocol_type = ProtoField.string("foo.prototype", "Protocol Type", base.NONE)
    13     local foo_service_type = ProtoField.string("foo.servicetype", "Service Type", base.NONE)
    14     local foo_msg_len = ProtoField.uint32("foo.msglen", "Message Length", base.DEC)
    15     local foo_msg_content = ProtoField.string("foo.msgcontent", "Message Content", base.NONE)
    16     
    17     -- 将字段添加都协议中
    18     foo_proto.fields = {
    19         foo_protocol_type,
    20         foo_service_type,
    21         foo_msg_len,
    22         foo_msg_content
    23     }
    24     
    25     --[[
    26         下面定义 foo 解析器的主函数,这个函数由 wireshark调用
    27         第一个参数是 Tvb 类型,表示的是需要此解析器解析的数据
    28         第二个参数是 Pinfo 类型,是协议解析树上的信息,包括 UI 上的显示
    29         第三个参数是 TreeItem 类型,表示上一级解析树
    30     --]]
    31     function foo_proto.dissector(tvb, pinfo, treeitem)
    32         
    33         -- 设置一些 UI 上面的信息
    34         pinfo.cols.protocol:set("FOO")
    35         pinfo.cols.info:set("Foo Protocol")
    36         
    37         local offset = 0
    38         local tvb_len = tvb:len()
    39         
    40         -- 在上一级解析树上创建 foo 的根节点
    41         local foo_tree = treeitem:add(foo_proto, tvb:range(offset))
    42         
    43         -- 下面是想该根节点上添加子节点,也就是自定义协议的各个字段
    44         -- 注意 range 这个方法的两个参数的意义,第一个表示此时的偏移量
    45         -- 第二个参数代表的是字段占用数据的长度
    46         foo_tree:add(foo_protocol_type, tvb:range(offset, 16))
    47         offset = offset+16        
    48         foo_tree:add(foo_service_type, tvb:range(offset, 16))
    49         offset = offset+16
    50         foo_tree:add(foo_msg_len, tvb:range(offset, 4))
    51         offset = offset+4
    52         
    53         -- 计算消息内容的长度
    54         local foo_content_len = tvb_len-offset
    55         foo_tree:add(foo_msg_content, tvb:range(offset, foo_content_len))
    56         offset = offset+foo_content_len
    57         
    58     end
    59     
    60     -- 向 wireshark 注册协议插件被调用的条件
    61     local tcp_port_table = DissectorTable.get("tcp.port")
    62     tcp_port_table:add(12345, foo_proto)
    63 end
    复制代码

    四、扩展

    (1)单 TCP 分节多应用 PDU

    这种情况可以在我们的协议解析函数中使用循环解决,但是应用层 PDU 一般都需要有我们自定义的分割标识符,不然无法区分,这里我们使用协议尾端标记的方式,采用0xFF分割,这里我们假设我们的协议体中不会出现这个值,也就是我们的消息体长度字段的值不会出现这个值。

    (2)单应用 PDU 多 TCP 分节

    这种情况就需要 wireshark 为我们重新拼接被 TCP 协议栈拆分的数据,不过这已经有接口可用了,下面是处理两种情况的框架

    复制代码
     1 do
     2     -- 这里便于运算需要将数据类型进行转换
     3     local tvbrange = tvb:range(0)
     4     local bytearray = tvbrange:bytes()
     5     local offset = pinfo.desegment_offset or 0
     6     
     7     while true do
     8         local pdu_flg = false
     9         local pdu_end_index = 0
    10         
    11         -- ByteArray 类型的索引与 C 类似
    12         -- 这与 lua 本身处理表的方式有所区别
    13         for i = offset, tvb_len do
    14             if 0xFF == bytearray:get_index(i) then
    15                 pdu_flg = true
    16                 pdu_end_index = i
    17             end
    18         end
    19         
    20         -- 如果没有找到 PDU 的结束符,进行合包延迟解析
    21         if false == frame_end_flg then
    22             -- 如果明确知道这个应用协议还需要多少字节
    23             -- 可以明确地将需要的值填上,而不是使用下面的值
    24             -- 只是表示需要更多的分节
    25             pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
    26             return
    27         end
    28         
    29         -- 下面是正常的处理流程,在上面的代码中已经有体现了
    30         -- 不过此时协议最大边界为 pdu_end_index
    31         
    32     end
    33 end
    复制代码

    五、完善

    (1)谈到网络协议,如果涉及到整数必然会有字节序的问题,如果 wireshark 默认使用的是大端字节序,如果协议中的整数采用的是小端字节序,那么请考虑使用 TreeItem 类型的 le_add() 方法替代 add() 方法。

    (2)在 add 和 le_add 方法中,我们可以显式设定我们自己的值,可以给 add 方法传递第三个值,例如,

    1         local protocol_type = tvb:range(offset, 16):string()
    2         foo_tree:add(foo_protocol_type, tvb:range(offset, 4), protocol_type)

    这样显示的就是 protocol_type 的内容,另外这里的 protocol_type 类型一定要与上面定义 foo_protocol_type 让 wireshark 解析的一致,不然这个值是无效的,也就是说这个地方 protocol_type 如果是整数,就是无效,反过来也是一样。

    (3)在协议解析树上增加节点不一定要提前定义字段,也就是字段的定义不是必须的,例如,

    1 local protocol_type = tvb:range(offset, 16):string()
    2 foo_tree:add(tvb:range(offset, 16), "Protocol Type: " .. protocol_type)

    效果是一样的,但是在过滤器中无法使用此字段。

    (4)pinfo.cols.info:set 方法可以延迟调用,也就说可以在确定了消息的各种属性之后在调用,这样可以更清晰的显示协议的摘要。

    六、参考

    (1)wireshark 用户手册,在 wireshark 的安装目录下有 chm 的版本,最值得参考的是第11章的数据类型和各种方法的解释。

    (2)重组被拆分的 TCP 分节,http://stackoverflow.com/questions/13138088/how-do-i-reassemble-tcp-packet-in-lua-dissector

    (3)处理一个分节多应用 PDU,http://stackoverflow.com/questions/14387426/reassembling-packets-in-a-lua-wireshark-dissector

    (4)调用子协议的方法,http://blog.csdn.net/phunxm/article/details/5972904

  • 相关阅读:
    flume和kafka整合(转)
    Flume目录
    Flume的安装配置
    HBase系统架构及数据结构(转)
    toolbar ,textfield,图片拉伸,Bundle
    poj 1017 Packets
    jQuery使用serialize(),serializeArray()方法取得表单数据+字符串和对象类型两种表单提交的方法
    Android数据加密概述及多种加密方式 聊天记录及账户加密 提供高质量的数据保护
    LintCode-落单的数 III
    LeetCode90:Subsets II
  • 原文地址:https://www.cnblogs.com/lvdongjie/p/6025465.html
Copyright © 2011-2022 走看看