wireshark解析私有协议

wireshark 解析私有协议

1:本文很多写法并不合理,因为我不知道wireshark框架,也从来没写过lua。。纯属瞎捉摸出的结果。
2:文本以解析websocket协议为例。

准备lua文件

我的wireshark安装目录在:C:\Program Files (x86)\Wireshark\ ,所以我在plugins\2.6目录下,创建了一个websocket.lua文件。

lua插件解析框架

do

    --协议名称为mrprewebsocket,在Packet Details窗格显示为mrprewebsocket
    local p_mrprewebsocket = Proto("mwebsocket","mwebsocket")

    local data_dis = Dissector.get("data")

    local function mrprewebsocket_dissector(buf, pkt, root)
        return 1
    end

    -- 每个报文都会触发调用的函数,所以我们需要定义
    function p_mrprewebsocket.dissector(tvb, pkt, root) 

        ret = mrprewebsocket_dissector(tvb, pkt, root)
        if ret == 1
        then
            --valid mrprewebsocket diagram
        elseif ret == 0
            --not enough data
        then
        else
            --当发现不是我的协议时,就应该调用data
            data_dis:call(tvb, pkt, root)
        end
    end


    local tcp_encap_table = DissectorTable.get("tcp.port")
    --只定义只需要解析80端口就可以了
    tcp_encap_table:add(80, p_mrprewebsocket)
end

上面操作不是很完善

需要考虑的2点

1:数据分包,即应用层数据放在多个tcp负载里面。
2:一个tcp负载存在多个应用层封装。

所以修改框架为:

do

    --协议名称为mrprewebsocket,在Packet Details窗格显示为mrprewebsocket
    local p_mrprewebsocket = Proto("mwebsocket","mwebsocket")

    local data_dis = Dissector.get("data")

    local function mrprewebsocket_dissector(buf, pkt, root)

        local b_offset = pkt.desegment_offset or 0


        --一个tcp负载上可能有多个应用层封装,所以需要循环处理
        while true do

            total_len = 解析头部获取当前头部指定的应用层payload长度

            --tcp的负载长度小于应用层头部指定的应用层数据,即应用层数据分包
            if b_offset + total_len > buf:len() then
                message("not enough")
                --标记一下缺多少报文,不知道是否必要
                pkt.desegment_len = b_offset + total_len - buf:len()
                --标记当前的buf已经处理了多少,下次从offset开始处理
                pkt.desegment_offset = b_offset
                return 0
            end

            -- 处理


            -- 处理完成,判断剩余是否还有报文,有则继续continue
            b_offset =  b_offset + total_len

            if b_offset > buf:len()
            then
                return -1
            elseif b_offset == buf:len()
            then
                return 1
            end
        end
        return 1
    end

    function p_mrprewebsocket.dissector(tvb, pkt, root) 
        ret = mrprewebsocket_dissector(tvb, pkt, root)
        if ret == 1
        then
            --valid mrprewebsocket diagram
        elseif ret == 0
            --not enough data
        then
        else
            --当发现不是我的协议时,就应该调用data
            data_dis:call(tvb, pkt, root)
        end
    end

    local tcp_encap_table = DissectorTable.get("tcp.port")

    tcp_encap_table:add(80, p_mrprewebsocket)
end

完整的解析websocket的lua文件

do
    -- ProtoField 用,将对应的值变成字符串显示
    local op_text = {
    [0x0] = "Continuation",
    [0x1] = "Text",
    [0x2] = "Binary",
    [0x8] = "Close",
    [0x9] = "Ping",
    [0xa] = "Pong",
    }

    local mk_text = {
    [0x0] = "False",
    [0x1] = "True",
    }

    local len_text = {
    [126] = "2",
    [127] = "8",
    }

    --协议名称为mrprewebsocket,在Packet Details窗格显示为mrprewebsocket
    local p_mrprewebsocket = Proto("mwebsocket","mwebsocket")

    --定义协议的各个字段

    --所有可能的字段都要定义,到时没有t:add就不会显示
    -- 第一个参数是过滤器过滤中显示的字段
    -- 第二个参数是协议解析时显示的字段
    -- 第三个参数是解析成number时显示的格式,十进制显示还是16进制显示
    -- 第四个参数是解析出的结果,通过table转换成对应的值,如果不需要转换,则置为nil
    -- 第五个参数是会将待解析的入参进行与操作,取对应的bit位进行后续的解析。当一个字节中存在多个字段解析时是必要的

    local FIN = ProtoField.uint8("mrprewebsocket.fin","Fin", base.DEC, nil, 0x80)
    local RSV = ProtoField.uint8("mrprewebsocket.rsv","Rsv", base.DEC, nil, 0x70)
    local OPCODE = ProtoField.uint8("mrprewebsocket.opcode","Opcode", base.DEC, op_text, 0x0f)
    local MASKED = ProtoField.uint8("mrprewebsocket.masked","Masked", base.DEC, mk_text, 0x80)
    local LENHEAD = ProtoField.uint8("mrprewebsocket.lenhead","flowing byte", base.DEC, len_text, 0x7f)
    local LEN = ProtoField.uint8("mrprewebsocket.len","Payload length", base.DEC)
    local LENRAW = ProtoField.uint8("mrprewebsocket.lenraw","Payload length", base.DEC, nil, 0x7f)
    local KEY = ProtoField.bytes("mrprewebsocket.key","Masking-Key")
    local PAYLOAD = ProtoField.bytes("mrprewebsocket.payload","Payload")
    local MKPAYLOAD = ProtoField.bytes("mrprewebsocket.maskedpayload","Masked Payload")
    local UMKPAYLOAD = ProtoField.string("mrprewebsocket.unmaskedpayload","Unmasked Payload", base.ASCII)

    -- 需要将所有字段如下加入到fields
    p_mrprewebsocket.fields = { FIN, RSV, OPCODE, MASKED, LENHEAD, LENRAW, LEN, KEY, PAYLOAD, MKPAYLOAD, UMKPAYLOAD}

    local data_dis = Dissector.get("data")

    local function mrprewebsocket_dissector(buf, pkt, root)

        local b_offset = pkt.desegment_offset or 0
        local buf_len  = buf:len();

        --头部分包我暂时没处理,目前假设头部不分包。
        if buf_len < 4
        then
            return false
        end

        --一个tcp负载上可能有多个应用层封装,所以需要循环处理
        while true do
            --message("in while b_offset "..b_offset)

            -- 取对应字节的bit位数
            -- buf(b_offset,1)表示取buf得第b_offset开始的1字节长度的值
            -- :bitfield(1,3) 表示 从第1bit开始取,取3bit
            rsv = buf(b_offset,1):bitfield(1,3)
            op  = buf(b_offset,1):bitfield(4,4)
            mked = buf(b_offset + 1,1):bitfield(0,1)
            plen = buf(b_offset + 1,1):bitfield(1,7)


            if plen == 126
            then
                offset = 4
                plen = buf(b_offset + 2, 2):uint()
            elseif  plen >= 126
            then
                offset = 10
                plen = buf(b_offset + 2, 8):uint()
            else
                offset = 2
            end

            --message("plen: " .. plen.." offset: "..offset)

            if mked == 1            
            then
                total_len = plen + offset + 4
            else
                total_len = plen + offset
            end

            --tcp的负载长度小于应用层头部指定的应用层数据,即应用层数据分包
            if b_offset + total_len > buf:len() then
                message("not enough")
                --标记缺多少
                pkt.desegment_len = b_offset + total_len - buf:len()
                --标记当前的buf已经处理了多少,下次从offset开始处理
                pkt.desegment_offset = b_offset
                return 0
            end

            --将当头部前信息显示在wireshark中
            local t = root:add(p_mrprewebsocket, buf)
            pkt.cols.protocol = "mrprewebsocket"
            t:add(FIN, buf(b_offset,1))
            t:add(RSV, buf(b_offset,1))
            t:add(OPCODE, buf(b_offset,1))
            t:add(MASKED, buf(b_offset + 1,1))

            --显示websocket的len,len有不同形式
            plen = buf(b_offset + 1,1):bitfield(1,7)
            if plen == 126
            then
                t:add(LENHEAD, buf(b_offset + 1, 1))
                t:add(LEN, buf(b_offset + 2, 2))
                offset = 4
                plen = buf(b_offset + 2, 2):uint()
            elseif  plen >= 126
            then
                t:add(LENHEAD, buf(b_offset + 1, 1))
                t:add(LEN, buf(b_offset + 2, 8))
                offset = 10
                plen = buf(b_offset + 2, 8):uint()
            else
                t:add(LENRAW, buf(b_offset + 1, 1))
                offset = 2
            end

            --如果有masked,则进行亦或获取明文,然后显示出来
            --这里写的复杂了,可以更简单
            if mked == 1
            then
                t:add(KEY, buf(b_offset + offset, 4))
                local mk = buf(b_offset + offset, 4):bytes()
                offset = offset + 4
                t:add(MKPAYLOAD, buf(b_offset + offset, plen))

                local mkd = buf(b_offset + offset, plen):bytes()
                local nmked = ByteArray.new()
                nmked:set_size(plen)
                j = 1
                -- 亦或操作
                for i=0, mkd:len()-1 do

                    a = mkd:get_index(i)
                    b = mk:get_index(i%4)

                    r = bit32.bxor(a, b)
                    --r = 0
                    nmked:set_index(i, r)
                end


                if nmked:len()~= 0 then
                local tmptvb = ByteArray.tvb(nmked:subset(0, nmked:len()), "plaintext")
                t:add(UMKPAYLOAD, tmptvb(0, nmked:len()))
                end
            else
                --non masked
                if plen ~= 0 then
                t:add(PAYLOAD, buf(b_offset + offset, plen))
                end

                local mkd = buf(b_offset + offset, plen):bytes()
                local nmked = ByteArray.new()
                nmked:set_size(plen)
                -- 简单的拷贝
                for i=0, mkd:len()-1 do
                    nmked:set_index(i, mkd:get_index(i))
                end

                if nmked:len() ~= 0 then
                local tmptvb = ByteArray.tvb(nmked:subset(0, nmked:len()), "plaintext")
                t:add(UMKPAYLOAD, tmptvb(0, nmked:len()))
                end
            end

            --set column info                    
            info = op_text[op]

            if buf(b_offset,1):bitfield(0,1) == 1
            then
            info = info.."[FIN]"
            end

            if b_offset == 0
            then
            pkt.cols.info = info
            else
            pkt.cols.info:append(", "..info)
            end

            b_offset =  b_offset + total_len
            --message("end b_offset "..b_offset)

            if b_offset > buf:len()
            then
                return -1
            elseif b_offset == buf:len()
            then
                return 1
            end

        end
        return 1
    end

    function p_mrprewebsocket.dissector(tvb, pkt, root) 
        ret = mrprewebsocket_dissector(tvb, pkt, root)
        if ret == 1
        then
            --valid mrprewebsocket diagram
        elseif ret == 0
            --not enough data
        then
        else
            --当发现不是我的协议时,就应该调用data
            data_dis:call(tvb, pkt, root)
        end
    end

    -- register our new dummy protocol for post-dissection
    --register_postdissector(p_mrprewebsocket)
    local tcp_encap_table = DissectorTable.get("tcp.port")
    --只需要处理tcp 80端口就可以了
    tcp_encap_table:add(80, p_mrprewebsocket)
end

解析结果通过wireshark如下:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/mrpre/article/details/80924148