wireshark protobuf插件开发(ubuntu)

前言

protobuf是google团队开发的用于高效存储和读取结构化数据的工具。相比于json和xml,protobuf会把数据压缩得更小,大约是json格式的1/10,xml格式的1/20。正因如此,protobuf编码后的数据,不能像json、xml那样直观地呈现数据。本文将介绍如何解析利用wireshark自定义插件,解析protobuf数据包。

概要

wireshark的插件可以使用c或是lua脚本进行开发,考虑到灵活性,推荐使用lua脚本。

我理解wireshark插件的执行过程就是用插件去解析每一个数据包,解析过程中如果出错,则继续由其他插件尝试解析;如果解析正确,则表示由该协议解析并显示结果。

值得注意的是,当插件只处理了部分数据时,未处理部分的数据切片要继续交由其他插件尝试解析。例如一个基于TCP协议的RTSP数据包依次由Ethernet、Internet、TCP、RTSP插件解析,Ethernet协议解析该数据包的0 - 13byte,Internet协议解析该数据包的14 - 33byte,TCP协议解析了34 - 65byte,RTSP协议解析剩余的274 byte,至此数据包解析完成。
在这里插入图片描述

1.环境准备

1.1.安装wireshark

#环境是ubuntu 64位,windows开发笔者比较弱鸡

    $sudo apt-get update
    $sudo apt-get install wireshark

1.2 安装lua-protobuf支持库

wireshark中lua版本是5.2,所以需要先安装lua5.2

    $sudo apt-get update
    $sudo apt=get install lua5.2

克隆lua-protobuf项目并编译

	$git clone https://github.com/starwing/lua-protobuf.git
    $gcc -O2 -fPIC -I/usr/include/lua5.2 -c pb.c -o pb.o
    $gcc -shared -o pb.so -L/usr/local/lib pb.o

2.创建wireshark插件

我们通过一个例子来说明如何创建一个插件:

数据端口:7200
数据格式:
在这里插入图片描述

  • 协议标识:固定为0x00 0x00 0x00 0x01
  • 消息ID:标识Protobuf数据类型,这里只有一个消息ID0x0001
  • 长度:Protobuf数据长度
  • Protobuf数据:
    我们的Protobuf数据目前只有一个Person,对应的消息ID为0x0001。
	message Person{
	    required string name = 1;
	    required int32  age  = 2;
	    optional string email = 3;
	    enum PhoneType{
	        HOME = 1;
	        MOBILE = 2;
	        WORK = 3;
	    }
	    message Phone{
	        required int64 id = 1;
	        optional PhoneType type = 2 [default = HOME];
	    }
	    repeated Phone phoneNum = 4;
	}

大体思路如下:
1.创建一个wireshark插件,完成我们自定义数据包的识别,并提取协议标识、消息ID、数据长度、以及protobuf数据;
2.使用protobuf lua兼容库解析步骤1中提取出的protobuf数据,并显示;

2.1 创建插件,识别数据包及提取协议各字段内容

wireshark的自定义插件在\usr\lib\x86_64-linux-gnu\wireshark\plugins目录下,在该文件下创建一个xx.lua文件。

一个典型的wireshark插件代码结果如下:

do  
    local struct = Struct
    local data_dis = Dissector.get("data")
    -- 协议名称为 meteoric_proto,在Packet Details窗格显示为 XXX Protocol   
    -- 注意这里的Proto与protobuf一点关系也没有。
    local m_MeteoricProto = Proto("meteoric_proto","XXX Protocol")
    -- 添加协议字段ident、id、length和data
    local f_ident= ProtoField.bytes("meteoric_proto.ident","ident")
    local f_id= ProtoField.int32("meteoric_proto.id","ID")
    local f_len= ProtoField.int32("meteoric_proto.len","length")
    local f_data = ProtoField.bytes("meteoric_proto.data","Data")
    m_MeteoricProto .fields = { f_ident, f_id, f_len, f_data }

	--定义协议解析函数,解析正确则返回true,反之返回false
    local function Meteoric_dissector(buffer, pinfo, tree)
    	--取得数据长度
        local buf_len = buffer:len()

		--长度检测
        if buf_len < 8 then return false end

 		--头检测
        if (buffer(0,1):uint()~=0x00 or 
            buffer(1,1):uint()~=0x00 or 
            buffer(2,1):uint()~=0x00 or 
            buffer(3,1):uint()~=0x01) then
            return false
        end

		--字段解析
        tree:add(f_ident, buffer(0,4))  
        tree:add(f_id , buffer(4, 6):uint())  
        tree:add(f_len , buffer(6, 8):uint())  
        --这里只提取出了protobuf数据,并没有开始解析protobuf。
        tree:add(f_data , buffer(8, buf_len))  
        --设置协议显示
        pinfo.cols.protocol:set("XX_Protobuf")
        return true
    end

    function m_MeteoricProto.dissector(buffer, pinfo, tree) 
        --在主窗口的 Protocol 字段显示的名称为 XX_Protobuf

        if Meteoric_dissector(buffer, pinfo, tree) then          
        else
            -- data 这个 dissector 几乎是必不可少的; 当发现不是我的协议时, 交由其他协议尝试解析
            data_dis:call(buffer, pinfo, tree)
        end
    end

    --将该协议对象添加到tcp 7200端口上
    DissectorTable.get("tcp.port"):add(7200, m_MeteoricProto)
end

2.2 解析proto数据

首先生成将Person.proto生成.pb文件备用:

$protoc -o Person.pb Person.proto 
--加载pb对象
local pb = require "pb"
--载入pb文件 ,可以载入多个文件
pb.loadfile "Person.pb"
--反序列化,结果类型为table,
msgtable = pb.decode("Person", buffer:string())
--结果递归遍历显示
AddTreeNode(f_data, msgtable) 


local function AddTreeNode(nodeTree, msgTable)
     for k,v in pairs(msgTable) do
         if type(v) == "table" then
             AddTreeNode(nodeTree:add(k), v)
         else
             nodeTree:add(k..":", v)
         end
     end
 end

代码

    do  
        local struct = Struct
        local data_dis = Dissector.get("data")
        -- 协议名称为 meteoric_proto,在Packet Details窗格显示为 XXX Protocol   
        -- 注意这里的Proto与protobuf一点关系也没有。
        local m_MeteoricProto = Proto("meteoric_proto","XXX Protocol")
        -- 添加协议字段ident、id、length和data
        local f_ident= ProtoField.bytes("meteoric_proto.ident","ident")
        local f_id= ProtoField.int32("meteoric_proto.id","ID")
        local f_len= ProtoField.int32("meteoric_proto.len","length")
        local f_data = ProtoField.bytes("meteoric_proto.data","Data")
        m_MeteoricProto.fields = { f_ident, f_id, f_len, f_data }
        
    	-- protobuf解析结果递归显示
    	local function AddTreeNode(nodeTree, msgTable)
    	     for k,v in pairs(msgTable) do
    	         if type(v) == "table" then
    	             AddTreeNode(nodeTree:add(k), v)
    	         else
    	             nodeTree:add(k..":", v)
    	         end
    	     end
    	 end
    	--定义协议解析函数,解析正确则返回true,反之返回false
        local function Meteoric_dissector(buffer, pinfo, tree)
        	--取得数据长度
            local buf_len = buffer:len()
    
    		--长度检测
            if buf_len < 8 then return false end
    
     		--头检测
            if (buffer(0,1):uint()~=0x00 or 
                buffer(1,1):uint()~=0x00 or 
                buffer(2,1):uint()~=0x00 or 
                buffer(3,1):uint()~=0x01) then
                return false
            end
    
    		--字段解析
            tree:add(f_ident, buffer(0,4))  
            tree:add(f_id , buffer(4, 6):uint())  
            tree:add(f_len , buffer(6, 8):uint())  
            tree:add(f_data , buffer(8, buf_len))  
            
    		--加载pb对象
    		local pb = require "pb"
    		--载入pb文件 ,可以载入多个文件
    		pb.loadfile "Person.pb"
    		--反序列化,结果类型为table,
    		msgtable = pb.decode("Person", buffer(8, buf_len):string())
    		--结果递归遍历显示
    		AddTreeNode(f_data, msgtable) 
    		
            --设置协议显示
            pinfo.cols.protocol:set("XX_Protobuf")
            return true
        end
    
        function m_MeteoricProto.dissector(buffer, pinfo, tree) 
            --在主窗口的 Protocol 字段显示的名称为 XX_Protobuf
    
            if Meteoric_dissector(buffer, pinfo, tree) then          
            else
                -- data 这个 dissector 几乎是必不可少的; 当发现不是我的协议时, 交由其他协议尝试解析
                data_dis:call(buffer, pinfo, tree)
            end
        end
    
        --将该协议对象添加到tcp 7200端口上
        DissectorTable.get("tcp.port"):add(7200, m_MeteoricProto)
    end

参考资料

https://cloud.tencent.com/developer/article/1365481
https://www.jianshu.com/p/1e2f63a484d6

你可能感兴趣的:(wireshark)