wireshark protobuf lua插件是指使用wireshark针对lua语言的插件支持,通过lua语言开发针对自定义的protobuf协议的插件,在wireshark中将protobuf格式数据转换为明文数据;
如下文章参考wireshark官方文档,测试数据也均来自官方文档提供,如存在纰漏或对文章存在疑问,欢迎讨论,也可跳转至最后参考文档处,自行参考官方文档,一切请以官方文档为准;
1.配置用户proto文件读取路径
在wireshark中配置用户定义或google官方proto文件读取路径;
1.1. 进入protobuf路径设置菜单
编辑-》首选项-》protocols-》protobuf
1.2. 编辑protobuf search paths
如果用户在proto文件中import了官方提供的标准proto文件,则需要配置官方文件的加载目录,官方标准proto文件导入语法如下:
syntax = "proto3";
package tutorial;
import "google/protobuf/timestamp.proto";
官方标准的proto文件一般在下载protoc项目时,include目录下
点击Protobuf search paths后的Edit按钮;
wireshark proto文件加载路径设置如下图:
1.3. protobuf 其他相关设置
2.在上述配置的目录下准备proto文件
这里以官方给出的为实例:
将如下内容保存为xx.proto文件后,放置如第1步中设置的用户proto文件加载路径中;
syntax = "proto3";
package tutorial;
import "google/protobuf/timestamp.proto";
message Person {
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phone = 4;
google.protobuf.Timestamp last_updated = 5;
bytes portrait_image = 6;
}
message AddressBook {
repeated Person people = 1;
}
3.编写lua插件脚本
如果protobuf数据通过UDP传输,则无需写对应的插件,wireshark可默认支持解析,直接跳转到:第5步:数据查看;
由于UDP是无连接的,所以一次数据传输的就是一个完整的protobuf数据;而TCP协议是有连接的,这里可能存在诸如确认包等等数据,所以需要写插件,将对应的非protobuf定义的数据进行过滤掉,然后通过wireshark对protobuf数据的解析功能进行解析;如下脚本重点解决的便是TCP传输过程中,将非protobuf数据去除,然后过滤出完整的protobuf数据,传输给wireshark解析器;
阅读脚本前,先了解下官方文档给出的实验数据是如何工作的(此处仅分析TCP协议):
Wireshark TCP实验数据包下载路径
Wireshark UDP实验数据包下载地址
3.1. 下载并查看TCP实验数据
首先根据握手包追踪完整的流
查看数据通信流程
通过上述数据可以看出,在握手成功后的数据发送阶段,51151端口发送数据给18127端口,每次发送protobuf数据时中间会间隔发送4字节的非protobuf数据,所以在编写lua插件时,需要将对应的4字节数据进行去除;
3.2. 编写lua插件
详细实现脚本如下:
-- 文件名: create_protobuf_dissector.lua
do
-- 解析器的一个引用, 用于针对一个数据包或该数据包的一部分调用解析器;
-- 参数: name 该解析器的名称;
-- 返回值: 如果通过名称检索到了该解析器,则返回其的一个引用,否则返回nil;
local protobuf_dissector = Dissector.get("protobuf")
-- Create protobuf dissector based on UDP or TCP.
-- The UDP dissector will take the whole tvb as a message.
-- The TCP dissector will parse tvb as format:
-- [4bytes length][a message][4bytes length][a message]...
-- @param name The name of the new dissector.
-- @param desc The description of the new dissector.
-- @param for_udp Register the new dissector to UDP table.(Enable 'Decode as')
-- @param for_tcp Register the new dissector to TCP table.(Enable 'Decode as')
-- @param msgtype Message type. This must be the root message defined in your .proto file.
local function create_protobuf_dissector(name, desc, for_udp, for_tcp, msgtype)
-- 创建或定义协议,可以用于定义协议名,协议描述,类型等等
local proto = Proto(name, desc)
-- 展示协议长度: ProtoField.uint32(name, desc, [base], [mask], [presence], [epan_field_type])
-- name: 表示协议字段的名称
-- desc: 表示协议字段的描述
-- base: 表示数据进制,默认16进制,DEC: 十进制
local f_length = ProtoField.uint32(name .. ".length", "Length", base.DEC)
-- 定义协议字段
proto.fields = { f_length }
-- 匿名函数写法,用于定义Wireshark Lua协议解析器的主要功能。它指定了一个函数,用于解析数据包并显示其内容。
-- tvb参数是数据包的缓冲区
-- pinfo参数是有关数据包的信息(例如时间戳、协议类型等)
-- tree参数是Wireshark窗口中的树形结构。解析器将使用这些参数来解析数据包。
proto.dissector = function(tvb, pinfo, tree)
local subtree = tree:add(proto, tvb())
if for_udp and pinfo.port_type == 3 then -- UDP
if msgtype ~= nil then
pinfo.private["pb_msg_type"] = "message," .. msgtype
end
-- 捕获执行函数的错误,pcall(function, arg[, arg1[, arg2]])
pcall(Dissector.call, protobuf_dissector, tvb, pinfo, subtree)
elseif for_tcp and pinfo.port_type == 2 then -- TCP
local offset = 0
local remaining_len = tvb:len()
-- 使用一个循环,将TCP流数据进行切割处理
while remaining_len > 0 do
if remaining_len < 4 then -- head not enough
pinfo.desegment_offset = offset
pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
return -1
end
local data_len = tvb(offset, 4):uint()
if remaining_len - 4 < data_len then -- data not enough
pinfo.desegment_offset = offset
pinfo.desegment_len = data_len - (remaining_len - 4)
return -1
end
subtree:add(f_length, tvb(offset, 4))
if msgtype ~= nil then
pinfo.private["pb_msg_type"] = "message," .. msgtype
end
pcall(Dissector.call, protobuf_dissector,
tvb(offset + 4, data_len):tvb(), pinfo, subtree)
offset = offset + 4 + data_len
remaining_len = remaining_len - 4 - data_len
end
end
pinfo.columns.protocol:set(name)
end
if for_udp then DissectorTable.get("udp.port"):add(0, proto) end
if for_tcp then DissectorTable.get("tcp.port"):add(0, proto) end
return proto
end
-- default pure protobuf udp and tcp dissector without message type
create_protobuf_dissector("protobuf_udp", "Protobuf UDP")
create_protobuf_dissector("protobuf_tcp", "Protobuf TCP")
-- add more protobuf dissectors with message types
create_protobuf_dissector("AddrBook", "Tutorial AddressBook", true, true, "tutorial.AddressBook")
end
4.将插件脚本放入wireshark的个人插件目录下
4.1. 插件目录查找
进入菜单:帮助-》关于wireshark-》文件夹
5.解析捕获网络文件的内容
5.1. 打开对应的抓包文件后,找到对应的protobuf数据,右键点击decodes as
5.2. 选择注册的协议名-ADDRBOOK
5.3. 查看解析后的数据