1. 需求
在各个行业中,产品(模块)往往需要与其他产品进行数据交互,尤其是不同生产商,会采用非标准协议,通过抓包工具抓到报文分析问题时,只能看到二进制字节,不能对这些报文进行详细分析,对问题分析带来较大不便。
wireshark支持通过编辑lua脚本实现通信协议的分析。问题说明:
1.1. 数据帧在网络中会出现合包、分包。即一个包中可能包含x.y个数据帧,其中x≥1,y≥0,如果需要解析一个完整的数据帧,可能需要将上一帧的数据带到下一帧中解析;
1.2. 点击每个解析树节点时,是否可以直接对应到报文窗口中的每个字节;
1.3. 解析时是否支持中文;
2. 解决方案
通过编辑wireshark安装目录中init.lua文件,最后几行中可以列出所有自定义协议的lua文件:
dofile(DATA_DIR.."console.lua") dofile(DATA_DIR.."zyhd/zj104.lua") |
编辑zj104.lua文件:
local my_proto = Proto ("zj104","zhejiang 104 protocol") function my_proto.dissector(buffer,pinfo,tree) local pktlen = buffer:len() local bytes_consumed = 0
--逐个解析每个数据帧,如果数据帧不完整,则将不完整的部分保留到下一个数据帧中,解决问题1.1 while bytes_consumed < pktlen do local result = dissectFrm(buffer, pinfo, tree, bytes_consumed) if result > 0 then bytes_consumed = bytes_consumed + result elseif result == 0 then return 0 else pinfo.desegment_offset = bytes_consumed result = -result pinfo.desegment_len = result return pktlen end end
return bytes_consumed end
--计算数据帧的帧长 checkFpmLength = function (buffer, offset) local msglen = buffer:len() - offset if msglen ~= buffer:reported_length_remaining(offset) then return 0 end
if msglen < 7 then return -DESEGMENT_ONE_MORE_SEGMENT end
local length_tvbr = buffer:range(offset + 1, 2) local length_val = length_tvbr:le_uint() + 3 if msglen < length_val then return -(length_val - msglen) end
return length_val, length_tvbr end
--对单个数据帧的完整解析 dissectFrm = function (buffer, pinfo, tree, offset) local length_val, length_tvbr = checkFpmLength(buffer, offset) if length_val <= 0 then return length_val end
local myProtoTree = tree:add(my_proto, buffer(offset, length_val), "浙江104通信协议@中元华电") --第二个参数,告诉解析器对应报文窗口中的位置。解决问题1.2 local apcitree = myProtoTree:add(my_proto, buffer(offset, 7),"APCI")
start = buffer(offset, 1):le_uint()
apcitree:add_le(my_proto,buffer(offset, 1),"起始:", string.format("0x%x",start)) offset = offset + 1
frm_len = buffer(offset, 2):le_uint() apcitree:add_le(my_proto,buffer(offset, 2),"长度:", frm_len) offset = offset + 2
local pbuf3 = buffer(offset, 1):le_uint()
if(frm_len==4) then if(bit32.band(pbuf3,3)==1) then --s frame offset=offset+2 apcitree:add_le("s frame") apcitree:add_le(my_proto,buffer(offset, 2),"recvSN:", bit32.rshift(buffer(offset, 2):le_uint(),1)) elseif(bit32.band(pbuf3,3)==3) then --u frame apcitree:add_le("u frame") if(bit32.rshift(pbuf3,2)==1) then apcitree:add_le("startDT") elseif(bit32.rshift(pbuf3,3)==1) then apcitree:add_le("startDT ack") elseif(bit32.rshift(pbuf3,4)==1) then apcitree:add_le("stopDT") elseif(bit32.rshift(pbuf3,5)==1) then apcitree:add_le("stopDT ack") elseif(bit32.rshift(pbuf3,6)==1) then apcitree:add_le("testDT") elseif(bit32.rshift(pbuf3,7)==1) then apcitree:add_le("testDT ack") end end else
apcitree:add_le("I帧") --i frame local sendSN = buffer(offset, 2):le_uint() apcitree:add_le(my_proto,buffer(offset, 2),"发送序号:", bit32.rshift(buffer(offset, 2):le_uint(),1)) --asdu:add_le("发送序号:", bit32.rshift(buffer(offset, 2):le_uint(),1)) offset = offset + 2 apcitree:add_le(my_proto,buffer(offset, 2),"接收序号:", bit32.rshift(buffer(offset, 2):le_uint(),1)) --local recvSN=buffer(offset, 2):le_uint() offset = offset + 2 asdutree = myProtoTree:add(my_proto,buffer(offset, length_val-7),"ASDU") local asduType=buffer(offset, 1):le_uint()
if(asduType==15 ) then offset = asdu15(buffer,pinfo,asdutree,offset) elseif(asduType==16) then offset = asdu16(buffer,pinfo,asdutree,offset) elseif(asduType==13) then offset = asdu13(buffer,pinfo,asdutree,offset) elseif(asduType==14) then offset = asdu14(buffer,pinfo,asdutree,offset) elseif(asduType==113) then asdutree:add_le("type:",string.format("%d",asduType),"(call brief report)") elseif(asduType==114) then asdutree:add_le("send brief report") elseif(asduType==1) then asdutree:add_le("switch shift") end end
return length_val end
--省略asdu13接口等定义
--注册解析器 local tcp_port_table = DissectorTable.get("tcp.port") local my_port = 2404 tcp_port_table:add(my_port, my_proto) |
将lua文件保存为utf8格式后,wireshark解析窗口中可正常显示中文,解决问题1.3。