使用Lua脚本为wireshark编写自定义通信协议解析器插件
在网络通信应用中,我们往往需要自定义应用层通信协议,例如基于UDP的Real-Time Transport Protocol以及基于TCP的RTP over HTTP。鉴于RTP协议的广泛性,wireshark(ethereal)内置了对RTP协议的支持,调试解析非常方便。RTP over HTTP作为一种扩展的RTP协议,尚未得到wireshark的支持。在《RTP Payload Format for Transport of MPEG-4 Elementary Streams over http》中,使用wireshark只能抓获到原始的TCP数据包,需要我们自己解析蕴含其中的RTP报文,剥离RTP over HTTP 16字节的报文头,然后萃取出MP4V_ES码流数据。 怎么样使wireshark支持自定义通信协议的解析呢?基于GPL v2的wireshark强大的插件系统支持用户编写自定义协议解析器插件。wireshark使用C语言编写而成,它支持动态链接库形式的插件扩展。除此之外,wireshark内置了Lua脚本引擎,可以使用Lua脚本语言编写dissector插件。打开C:/Program Files/Wireshark/init.lua,确保disable_lua = false,以开启wireshark的Lua Console。 Lua是一种功能强大的、快速的、轻量的、嵌入式的脚本语言,使用Lua编写wireshark dissector插件是非常方便的。本文例解使用Lua编写ROH(RTP over HTTP)协议解析插件。 以下为GET?http://219.117.194.183:60151/rtpOverHttp?Url=nphMpeg4/nil-640x480,获取MP4V_ES的一个片段,在没有ROH解析支持时,Packet 294显示为TCP报文。
关于RTP over HTTP报文的解析,参考《RTP Payload Format for Transport of MPEG-4 Elementary Streams over http》。关于Lua语法,参考Wireshark User’s Guide的《Lua Support in Wireshark》一节。 以下为自定义RTP over HTTP协议解析器插件代码roh.lua: do ??? --[[ ??? Proto.new(name,desc) ??????? name: displayed in the column of “Protocol” in the packet list ??????? desc: displayed as the dissection tree root in the packet details ??? --]] ??? local PROTO_ROH = Proto("ROH","Rtp Over Http") ? ??? --[[ ??? ProtoField: ??????? to be used when adding items to the dissection tree ??? --]] ??? --[[ ??? (1)Rtp Over Http Header ??? --]] ??? -- rtp over http header flag(1 byte) ??? local f_roh_headerflag = ProtoField.uint8("ROH.HeaderFlag","Header Flag",base.HEX) ??? -- rtp over http interleaved channel(1 byte) ??? local f_roh_interleave = ProtoField.uint8("ROH.InterleavedChannel","Interleaved Channel",base.DEC) ??? -- rtp over http packet length(2 bytes) ??? local f_roh_packlen = ProtoField.uint16("ROH.PacketLen","Packet Length",base.DEC) ??? --[[ ??? (2)RTP Over Http ??? --]] ??? -- rtp header(1 byte = V:2+P:1+X:1+CC:4) ??? local f_rtp_header = ProtoField.uint8("ROH.Header","Rtp Header",base.HEX) ??? -- rtp payloadtype(1 byte = M:1+PT:7) ??? local f_rtp_payloadtype = ProtoField.uint8("ROH.PayloadType","Rtp Payload Type",base.HEX) ??? -- rtp sequence number(2 bytes) ??? local f_rtp_sequence = ProtoField.uint16("ROH.Sequence","Rtp Sequence Number",base.DEC) ??? -- rtp timestamp(4 bytes) ??? local f_rtp_timestamp = ProtoField.uint32("ROH.Timestamp","Rtp Timestamp",base.DEC) ??? -- rtp synchronization source identifier(4 bytes) ??? local f_rtp_ssrc = ProtoField.uint32("ROH.SSRC","Rtp SSRC",base.DEC) ? ??? -- define the fields table of this dissector(as a protoField array) ??? PROTO_ROH.fields = {f_roh_headerflag,f_roh_interleave,f_roh_packlen,f_rtp_header,f_rtp_payloadtype,f_rtp_sequence,f_rtp_timestamp,f_rtp_ssrc} ? ??? --[[ ??? Data Section ??? --]] ??? local data_dis = Dissector.get("data") ? ??? --[[ ??? ROH Dissector Function ??? --]] ??? local function roh_dissector(buf,pkt,root) ? ??????? -- check buffer length ??????? local buf_len = buf:len() ??????? if buf_len < 16 ??????? then ??????????? return false ??????? end ? ??????? -- check header flag ????? ??if buf(0,2):uint() ~= 0x2400 ??????? then ??????????? return false ??????? end ? ??????? --[[ ??????? packet list columns ??????? --]] ??????? pkt.cols.protocol = "ROH" ??????? pkt.cols.info = "Rtp Over Http" ? ??????? --[[ ??????? dissection tree in packet details ??????? --]] ??????? -- tree root ??????? local t = root:add(PROTO_ROH,buf(0,16)) ??????? -- child items ??????? -- ROH Header ??????? t:add(f_roh_headerflag,1)) ??????? t:add(f_roh_interleave,buf(1,1)) ??????? t:add(f_roh_packlen,buf(2,2)) ??????? -- ROH ??????? -- (1)header ??????? t:add(f_rtp_header,buf(4,1)) ??????? -- (2)payloadtype ??????? t:add(f_rtp_payloadtype,buf(5,1)) ??????? -- (3)sequence number ??????? t:add(f_rtp_sequence,buf(6,2)) ??????? -- (4)timestamp ??????? t:add(f_rtp_timestamp,buf(8,4)) ??????? -- (5)ssrc ??????? t:add(f_rtp_ssrc,buf(12,4)) ? ??????? if buf_len > 16 ??????? then ??????????? local data_len = buf:len()-16; ? ??????????? local d = root:add(buf(16,data_len),"Data") ??????????? d:append_text("("..data_len.." bytes)") ??????????? d:add(buf(16,"Data: ") ??????????? d:add(buf(16,0),"[Length: "..data_len.."]") ? ??????????? local start_code = buf(16,4):uint() ??????????? if start_code == 0x000001b0 ??????????? then ??????????????? d:add(buf(16,"[Stream Info: VOS]") ??????????? elseif start_code == 0x000001b6 ??????????? then ??????????????? local frame_flag = buf(20,1):uint() ??????????????? if frame_flag<2^6 ??????????????? then ??????????????????? d:add(buf(16,"[Stream Info: I-Frame]") ??????????????? elseif frame_flag<2^7 ??????????????? then ??????????????????? d:add(buf(16,"[Stream Info: P-Frame]") ??????????????? else ??????????????????? d:add(buf(16,"[Stream Info: B-Frame]") ??????????????? end ??????????? end ??????? end ? ??????? return true ??? end ? ??? --[[ ??? Dissect Process ??? --]] ??? function PROTO_ROH.dissector(buf,root) ??????? if roh_dissector(buf,root) ??????? then ??????? -- valid ROH diagram ??????? else ??????????? data_dis:call(buf,root) ??????? end ??? end ? ??? --[[ ??? Specify Protocol Port ??? --]] ??? local tcp_encap_table = DissectorTable.get("tcp.port") ??? tcp_encap_table:add(60151,PROTO_ROH) end 通过菜单“ToolsàLuaàEvaluate”打开Lua调试窗口,将以上代码输入Evaluate窗口,然后点击“Evaluate”按钮,若无错误提示,且末尾提示“--[[? Evaluated --]]”,则说明代码无语法错误。这时,通过菜单“HelpàSupported Protocols”可以发现,wireshark已经添加了ROH协议调试支持,在Display Filter中输入“roh”,则可以看到具体解析结果。 将以上代码保存为C:/Program Files/Wireshark/roh.lua。在C:/Program Files/Wireshark/init.lua末尾添加代码行:dofile("roh.lua")。这样在以后启动wireshark时,自动加载roh.lua。 增加roh.Lua插件扩展后,Packet 294将解析为RTP over HTTP报文。最后的Data部分为剥离16字节报文头后的码流。
由于尚未掌握Lua的位域(bitfield)操作,因此上面对于RTP的解析不完整,第一个字节(V:2+P:1+X:1+CC:4)和第二字节(M:1+PT:7)的相关位域没有解析出来。 既然wireshark内置了对RTP协议的支持,我们在解析完RTP over HTTP的头4个字节后,可以将余下的报文交由RTP协议解析器解析。以下为调用内置RTP协议解析器代码roh_rtp.lua: do ??? --[[ ??? Proto.new(name,"Rtp Over Http") ??? local PROTO_PANASONIC_PTZ = Proto("PANASONIC_PTZ","Panasonic PTZ Protocol") ? ??? --[[ ??? ProtoField: ??????? to be used when adding items to the dissection tree ??? --]] ??? --[[ ??? 1.ROH ProtoField ??? --]] ??? --rtp over http header flag(1 byte) ??? local f_roh_headerflag = ProtoField.uint8("ROH.HeaderFlag",base.HEX) ??? --rtp over http interleaved channel(1 byte) ??? local f_roh_interleave = ProtoField.uint8("ROH.InterleavedChannel",base.DEC) ??? --rtp over http packet length(2 bytes) ??? local f_roh_packlen = ProtoField.uint16("ROH.PacketLen",base.DEC) ??? --define the fields table of this dissector(as a protoField array) ??? PROTO_ROH.fields = {f_roh_headerflag,f_roh_packlen} ? ??? --[[ ??? 2.PANASONIC_PTZ ProtoField ??? --]] ??? -- panasonic ptz header flag(32 ascii) ??? local f_panasonic_ptz_flag = ProtoField.bytes("PANASONIC_PTZ","Header Flag") ??? -- panasonic ptz command(6~17 ascii) ??? local f_panasonic_ptz_cmd = ProtoField.bytes("PANASONIC_PTZ","Command") ??? --define the fields table of this dissector(as a protoField array) ??? PROTO_PANASONIC_PTZ.fields = {f_panasonic_ptz_flag,f_panasonic_ptz_cmd} ? ??? --[[ ??? Data Section ??? --]] ??? local data_dis = Dissector.get("data") ? ??? --[[ ??? ROH Dissector Function ??? --]] ??? local function roh_dissector(buf,root) ??????? -- check buffer length ??????? local buf_len = buf:len() ??????? if buf_len < 16 ??????? then ??????????? return false ??????? end ??????? ??????? -- check header flag ??????? if buf(0,2):uint() ~= 0x2400 ??????? then ??????????? return false ??????? end ? ??????? --[[ ??????? packet list columns ??????? --]] ??????? pkt.cols.protocol = "ROH" ??????? pkt.cols.info = "Rtp Over Http" ? ? ??????--[[ ??????? dissection tree in packet details ??????? --]] ??????? -- tree root ??????? local t = root:add(PROTO_ROH,4)) ??????? -- child items ??????? t:add(f_roh_headerflag,2)) ? ??????? return true ??? end ? ??? --[[ ??? PANASONIC_PTZ Dissector Function Helper ??? --]] ??? local function get_cmd_len(buf) ??????? local found=0 ??????? for i=0,17 do ??????????? if buf(i,1):uint() == 0x26 ??????????? then ?? ?????????????found = i ??????????????? break ??????????? end ??????? end ??????? return found ??? end ? ??? --[[ ??? PANASONIC_PTZ Dissector Function ??? --]] ??? local function panasonic_ptz_dissector(buf,root) ? ??????? -- check buffer length ??????? local buf_len = buf:len() ??????? if buf_len < 32 ??????? then ??????????? return false ??????? end ? ??????? -- check header flag ??????? if buf(0,32):string() ~= "GET /nphControlCamera?Direction=" ??????? then ??????????? return false ??????? end ? ??????? -- check direction ??????? local sub_buf = buf(32,18):tvb() ??????? local cmd_len = get_cmd_len(sub_buf) ? ??????? if cmd_len > 0 ??????? then ??????????? --[[ ??????????? packet list columns ??????????? --]] ??????????? pkt.cols.protocol = "PANASONIC_PTZ" ??????????? pkt.cols.info = "Panasonic PTZ Protocol" ? ??????????? --[[ ??????????? dissection tree in packet details ??????????? --]] ??????????? -- tree root ??????????? local t = root:add(PROTO_PANASONIC_PTZ,buf_len)) ??????????? -- child items ??????????? local flag = t:add(f_panasonic_ptz_flag,32)) ??????????? flag:add(buf(0,31),"["..buf(0,31):string().."]") ??????????? local cmd = t:add(f_panasonic_ptz_cmd,buf(32,cmd_len)) ??????????? cmd:add(buf(32,cmd_len),"["..buf(32,cmd_len):string().."]") ??????? else ??????????? return false ??????? end ? ??????? return true ??? end ? ??? --[[ ??? Dissect Process ??? --]] ??? function PROTO_ROH.dissector(buf,root) ??????? then ??????????? -- skip over rtp over http header ??????????? local rtp_buf = buf(4,buf:len()-4):tvb() ??????????? -- call internal rtp dissector ??????????? local rtp_dissector = Dissector.get("rtp") ??????????? rtp_dissector:call(rtp_buf,root) ??????? elseif panasonic_ptz_dissector(buf,root) ??????? then ??????????? -- valid ptz datagram ??????? else ??????????? data_dis:call(buf,root) ??????? end ??? end ? ??? --[[ ??? Specify Protocol Port ??? --]] ??? local tcp_encap_table = DissectorTable.get("tcp.port") ??? tcp_encap_table:add(60151,PROTO_ROH) end 此外,wireshark内置了对MP4V_ES码流解析器,选择菜单“EditàPreferencesàProtocolsàMP4V_ES”,在“MP4V_ES dynamic payload type:”中填写“96”。 将以上代码保存为C:/Program Files/Wireshark/roh_rtp.lua。在C:/Program Files/Wireshark/init.lua末尾添加代码行:dofile("roh_rtp.lua"),在dofile("roh.lua")前添加“--”注释,以免端口冲突(在roh.lua中tcp.port=60151)。保存init.lua后,重启wireshark。 自定义应用层协议基于传输层(UDP/TCP),自定义的协议端口号不能与wireshark内置应用层协议冲突。若将ROH协议指定为TCP 80,则需要选择菜单“AnalyzeàEnabled Protocols”勾掉HTTP,这样TCP 80的报文将按照ROH协议解析。 在Display Filter中输入“roh”可查看RTP+MP4V_ES解析结果,如下图所示:
此外,在Display Filter中输入“panasonic_ptz”可查看PTZ控制报文。 ? 参考: 《Programming in Lua》 《Lua 5.0 Reference Manual》 《Small is Beautiful: the design of Lua》 《Lua Scripting in Wireshark》 ? 《为Wireshark 开发插件》 《使用lua脚本编写wireshark协议插件》 《使用lua编写Wireshark的dissector插件》 《用Lua语言编写Wireshark dissector插件》 《Lua和Wireshark配合,调试通信程序》 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |