您的位置:首页 > 编程语言 > Lua

使用lua脚本为wireshake编写自定义协议解析器

2017-04-19 18:00 495 查看
以下内容转载自:转载地址

在网络通信应用中,我们往往需要自定义应用层通信协议,例如基于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, buf(0,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_len), "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,0), "[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,0), "[Stream Info: I-Frame]")
                elseif frame_flag<2^7
                then
                    d:add(buf(16,0), "[Stream Info: P-Frame]")
                else
                    d:add(buf(16,0), "[Stream Info: B-Frame]")
                end
            end
        end
 
        return true
    end
 
    --[[
    Dissect Process
    --]]
    function PROTO_ROH.dissector(buf, pkt, root)
        if roh_dissector(buf, pkt, root)
        then
        -- valid ROH diagram
        else
            data_dis:call(buf, pkt, 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, 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")
    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", "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)
    --define the fields table of this dissector(as a protoField array)
    PROTO_ROH.fields = {f_roh_headerflag, f_roh_interleave, 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, 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,4))
        -- child items
        t:add(f_roh_headerflag, buf(0,1))
        t:add(f_roh_interleave, buf(1,1))
        t:add(f_roh_packlen, buf(2,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, pkt, 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(0,buf_len))
            -- child items
            local flag = t:add(f_panasonic_ptz_flag, buf(0,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, pkt, root)
        if roh_dissector(buf, pkt, 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, pkt, root)
        elseif panasonic_ptz_dissector(buf, pkt, root)
        then
            -- valid ptz datagram
        else
            data_dis:call(buf, pkt, 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配合,调试通信程序
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  wireshake