标签归档:lua

用Lua为Wireshark写协议解析器 真实案例

    众所周知Wireshark是分析网络协议不可或缺的工具之一,Wireshark为我们提供了很多的协议支持,如果我们有自定义的协议需要分析,则可以通过自己用Lua写插件来解析。Lua是一个小巧的脚本语言,是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组(由Roberto Ierusalimschy、Waldemar Celes 和Luiz Henrique de Figueiredo所组成)于1993年开发。Wireshark默认提供了对Lua的支持,我们可以很方便地调用Wireshark提供的API来解析网络协议。
    下面我通过一个简单的例子来说明一下写插件的方法和步骤。
    — 协议格式:

—- 文件名称:mypro.lua  —–

do
  ——————————————–
  —
  — Version: v1.0
  — Author: bryan.whp  2012-11-27
  — Description: Analyze My Proprietary Protocol Interface v1.0 package
  — 
  ——————————————–
  — Create a new dissector
  local my_proto = Proto ("mypro","My Proprietary Protocol Interface v1.0")
  local f = my_proto.fields
 
  — Create the protocol fields
  local pkeys = {
    [0x01] = "publish",
    [0x02] = "unpublish",
  }
 
  local tlvtypes = {
    [1] = "uint",
    [2] = "string",
    [3] = "tlv",
  }

  — 定义状态码对应的含义,Wireshark会自动将该值转为相应的字符串。
  local tagNames = {
    [1] = "nid",
    [2] = "nna",
    [3] = "node",
  }
 
  f.f_Header = ProtoField.bytes("mypro.header","Message Header")
  f.f_Headerpkey = ProtoField.uint8("mypro.pkey", "pkey", base.HEX, pkeys)
  f.f_Headernid = ProtoField.uint8("mypro.nid", "nid", base.DEC)
  f.f_HeaderLength = ProtoField.uint16("mypro.length", "Length", base.DEC)
  f.f_HeaderReserve = ProtoField.uint16("mypro.reserve", "Reserve", base.DEC)
  f.f_HeaderSeq = ProtoField.uint16("mypro.seq","Seq", base.DEC)
  f.f_Headeradrressa = ProtoField.uint32("mypro.adrressa","adrressa", base.DEC)
  f.f_Headeradrressb = ProtoField.uint32("mypro.adrressb","adrressb", base.DEC)
  f.f_HeaderTLVs = ProtoField.bytes("mypro.tlvs","Message TLVs")
  f.f_TLV = ProtoField.bytes("mypro.tlv","TLV")
  f.f_TlvTag = ProtoField.uint8("mypro.tag","TagNum", base.DEC)
  f.f_TlvLength = ProtoField.uint8("mypro.taglength","Length", base.DEC)
  f.f_TlvLongLength = ProtoField.uint16("mypro.taglonglength","Length", base.DEC)
  f.f_TlvBody = ProtoField.bytes("mypro.value", "Value")
 
  f.f_TlvUintBody = ProtoField.uint32("mypro.value", "Value", base.DEC)
  f.f_TlvUlongBody = ProtoField.uint64("mypro.value", "Value", base.DEC)
  f.f_TlvStringBody = ProtoField.string("mypro.value", "Value")
  f.f_TlvByteBody = ProtoField.uint8("mypro.value", "Value", base.DEC)
 
  — RTP package Dissector
  local rtp_dissector = Dissector.get("rtp")
 
  ——————————————–
  —
  — Main function
  —
  ——————————————–
  — add tlv
  function tlv_bundle(subtree, tagNum, tagLength, tagValue, subbuffer)
    local tagNum_int = tagNum:uint()
    local tlv_tree = subtree:add_le(f.f_TLV, subbuffer)
 
    if tlvtypes[tagNum_int] ~= "tlv" then
      tlv_tree:add(f.f_TlvTag, tagNum)
 
      local tagLengthDec = subbuffer(1,2):uint()
      if tagLengthDec > 127 then
        tlv_tree:add(f.f_TlvLongLength, tagLength)
      else
        tlv_tree:add(f.f_TlvLength, tagLength)
      end
 
      if tlvtypes[tagNum_int] == "ulong" then
        tlv_tree:add(f.f_TlvUlongBody, tagValue)
        tagValue = tagValue:uint64()
      elseif tlvtypes[tagNum_int] == "string" then
        tlv_tree:add(f.f_TlvStringBody, tagValue)
        tagValue = tagValue:string()
      elseif tlvtypes[tagNum_int] == "uint" then
        tlv_tree:add(f.f_TlvUintBody, tagValue)
        tagValue = tagValue:uint()
      elseif tlvtypes[tagNum_int] == "byte" then
        tlv_tree:add(f.f_TlvByteBody, tagValue)
        tagValue = tagValue:uint()
      else
        tlv_tree:add(f.f_TlvBody, tagValue)
      end
 
      tlv_tree:set_text(tagNames[tagNum_int]..": "..tagValue)
    else
      tlv_bundles(tagValue, tlv_tree)
      tlv_tree:set_text(tagNames[tagNum_int]..": TLVs")
    end
  end
 
  — add tlvs
  function tlv_bundles(buffer, subtree)
    local tagNum = 0
    local tagLength = 0
    local tagValue = 0
    local offset = 0
    local buffter_len = buffer:len()
    local tlv_tree = subtree:add(f.f_HeaderTLVs)
    tlv_tree:set_text("Message TLVs")
 
    while offset < buffter_len do
      tagNum = buffer(offset,1)
      tagLength = buffer(offset+1,1)
      local tagLengthDec = tagLength:uint()
 
      if tagLengthDec > 127 then
        tagLength = buffer(offset+1,2)
        tagLengthDec = tagLength:uint() – 32768
        tagLength = tagLength:bitfield(1,15)
        inc = 3
      else
        tagLength = buffer(offset+1,1)
        tagLengthDec = tagLength:uint()
        inc = 2
      end
 
      — tlv_tree:set_text(offset..":"..inc..":"..tagLengthDec..":"..buffer)
      tagValue =  buffer(offset+inc, tagLengthDec)
      tlv_bundle(tlv_tree, tagNum, tagLength, tagValue, buffer(offset, inc+tagLengthDec))
      offset = offset + inc + tagLengthDec
    end
 
  end

  — 解析函数,这里就是解析消息包内容的过程
  — add header and body
  function my_proto.dissector(buffer, pinfo, tree)
      — 在主窗口的Protocol 字段显示的名称为“Mypro”
      pinfo.cols.protocol:set("Mypro")
 
      local offset = 0
      local pkey = buffer(offset,1)
      local IsmyPPI = 0  — not My PPI V4.0 default
      local key
      local value
      for key,value in pairs(pkeys) do
        if pkey:uint() == key then
          IsmyPPI = 1
        end
      end
 
      if IsmyPPI == 0 then
        return false
      end

      — 增加一个子项目到Packet Details窗口
      local buffer_len = buffer:len()
      local my_proto_tree = tree:add(my_proto, buffer(0, buffer_len), "My Proprietary protocol interface v1.0")
      local header_tree = my_proto_tree:add(f.f_Header)
      header_tree:set_text("Message Header (p_key: 0x"..pkey..")")
 
      header_tree:add(f.f_Headerpkey, pkey)

      — 提取1个字节,该字节代表的是id。
      offset = offset + 1
      header_tree:add(f.f_Headernid, buffer(offset,1))
 
      offset = offset + 1
      header_tree:add(f.f_HeaderLength, buffer(offset,2))
 
      offset = offset + 2
      header_tree:add(f.f_HeaderReserve, buffer(offset,2))
 
      offset = offset + 2
      header_tree:add(f.f_HeaderSeq, buffer(offset,2))
 
      offset = offset + 2
      local adrressa = buffer(offset,4):uint()
      header_tree:add(f.f_Headeradrressa, buffer(offset,4))
 
      offset = offset + 4
      header_tree:add(f.f_Headeradrressb,  buffer(offset,4))
 
      — BODY: AMR: p_key == 0x0d    TLVs: others
      offset = offset + 4
      local tlvsstream = buffer(offset, (buffer_len-16))
      if buffer_len ~= 16 then
        if pkey:uint() ~= 96 then
          tlv_bundles(tlvsstream, my_proto_tree)
        else
          — Body is rtp message
          rtp_dissector:call(tlvsstream:tvb(), pinfo, header_tree)
        end
      end
 
      pinfo.cols.info:set(pkeys[pkey:uint()].." (0x"..pkey.."), adrressa: "..adrressa)
 
      return True
  end

  — 将该解析器应用于61020号端口。因该协议使用的是61020号端口,采用UDP协议传输
  — 这就意味着通过61020号端口的UDP将由这个解析器来分析
  local my_port = 61020
  — UDP package
  local udp_port_table = DissectorTable.get("udp.port")
  udp_port_table:add(my_port, my_proto)
 
end
 
—-  文件:mypro.lua 完毕—-

用法:
将mypro.lua放入wireshark根目录下,同时在根目录下的init.lua文件末尾添加 dofile(DATA_DIR.."mypro.lua") 

重启wireshark即可
 
解析结果如下: