文章目录
前言
如果直接使用Wireshark查看Http等协议报文,并不会有任何不便,
但是如果查看私有协议报文,无法区分哪些是TCP协议相关报文,哪些是含有消息体的私有协议报文,也只能看到一串Byte和对应ASCII编码的Data值,无法知道数据中的各个字节对应哪个字段代表什么含义,更不能根据这些对应字段值进行过滤
一、解析器(Dissector)是什么?
解析器(Dissector)是Wireshark中的概念,用于解析协议,将报文中对应的Bytes转为相应的字段值
可以简单理解为Wireshark中的解码器。
它不只能解析将相应位置的Bytes转为对应字段,还能将解析出来的字段用于报文过滤,还能自定义信息(Info)列中显示的自定义信息。
能方便开发人员调试诊断协议相关问题,比如断连后不重连,报文不正确,未发心跳。
解析器可以使用C语言编写,也可以使用Lua编写
本篇使用Lua编写,因为该语言是脚本语言,不需要编译后执行,方便开发修改调试
缺点是比C语言编写的解析器慢
二、编写流程
1.添加解析器插件文件
- 在%APPDATA%\Wireshark 中新建文件夹plugins ,可以使用Win+R 键然后输入%APPDATA%\Wireshark然后回车,快速打开该目录
- 在plugins 文件夹下新建xxx.lua 文件,其中xxx 一般以协议名为文件名
2.解析器由什么部分组成
- 协议定义
- 字段定义
- 协议和字段关联
- 解析器主函数
- 协议注册
3. 解析器代码详解
-- 协议定义,名称为ScoreBoard,在Packet Details窗格显示为ScoreBoard Protocol
local p_ScoreBoard = Proto('ScoreBoard', 'ScoreBoard Protocol')
-- 协议的各个字段定义
local f_identifier = ProtoField.bytes('ScoreBoard.identifier', 'Identifier')
local f_operator =
ProtoField.uint8(
'ScoreBoard.operator',
'Operator',
base.HEX,
-- 这个字段的数字值都有相应的含义,可以自动对应成字符串
{
[0] = 'get-value',
[1] = 'set-value',
[128] = 'resp-value',
[16] = 'get-color',
[17] = 'set-color',
[144] = 'resp-color'
}
)
-- 所有可能的字段都要定义,到时没有t:add就不会显示
local f_left = ProtoField.uint32('ScoreBoard.left', 'Value Left', base.DEC)
local f_right = ProtoField.uint32('ScoreBoard.right', 'Value Right', base.DEC)
local f_red = ProtoField.uint8('ScoreBoard.red', 'Color Red', base.DEC)
local f_green = ProtoField.uint8('ScoreBoard.green', 'Color Green', base.DEC)
local f_blue = ProtoField.uint8('ScoreBoard.blue', 'Color Blue', base.DEC)
-- 将字段添加到协议中
p_ScoreBoard.fields = {
f_identifier,
f_operator,
f_left,
f_right,
f_red,
f_green,
f_blue
}
-- 获取Wireshark自带的Data解析器
-- 后续解析发现不是有效报文需要调用该解析器显示报文数据
local data_dis = Dissector.get('data')
local function ScoreBoard_dissector(buf, pkt, root)
local buf_len = buf:len()
-- 先检查报文长度,太短的不是我的协议
if buf_len > 17 then
return false
end
-- 取得前16字节identifier字段的值
local v_identifier = buf(0, 16)
-- 验证identifier是否正确
if
((buf(0, 1):uint() ~= 226) or (buf(1, 1):uint() ~= 203) or (buf(2, 1):uint() ~= 181) or
(buf(3, 1):uint() ~= 128) or
(buf(4, 1):uint() ~= 203) or
(buf(5, 1):uint() ~= 9) or
(buf(6, 1):uint() ~= 78) or
(buf(7, 1):uint() ~= 186) or
(buf(8, 1):uint() ~= 163) or
(buf(9, 1):uint() ~= 107) or
(buf(10, 1):uint() ~= 246) or
(buf(11, 1):uint() ~= 7) or
(buf(12, 1):uint() ~= 206) or
(buf(13, 1):uint() ~= 149) or
(buf(14, 1):uint() ~= 63) or
(buf(15, 1):uint() ~= 43))
then -- 不正确就不是我的协议
return false
end
-- 取得operator的值
local v_operator = buf(16, 1)
local i_operator = v_operator:uint()
-- 现在知道是我的协议了,放心大胆添加Packet Details
local t = root:add(p_ScoreBoard, buf)
-- 在Packet List窗格的Protocol列也可以"做个小广告"
pkt.cols.protocol = 'ScoreBoard'
t:add(f_identifier, v_identifier)
t:add(f_operator, v_operator)
if ((i_operator == 1) or (i_operator == 128)) and (buf_len >= 25) then
-- 把存在的字段逐个添加进去
t:add(f_left, buf(17, 4))
t:add(f_right, buf(21, 4))
elseif ((i_operator == 17) or (i_operator == 144)) and (buf_len >= 20) then
t:add(f_red, buf(17, 1))
t:add(f_green, buf(18, 1))
t:add(f_blue, buf(19, 1))
end
return true
end
--[[
下面定义 foo 解析器的主函数,这个函数由 wireshark调用
第一个参数是 Tvb 类型,表示的是需要此解析器解析的数据
第二个参数是 Pinfo 类型,是协议解析树上的信息,包括 UI 上的显示
第三个参数是 TreeItem 类型,表示上一级解析树
--]]
function foo_proto.dissector(tvb, pinfo, treeitem)
-- 设置一些UI中报文列表中Proto列和Info列显示的信息
pinfo.cols.protocol:set('FOO')
pinfo.cols.info:set('Foo Protocol')
if ScoreBoard_dissector(buf, pkt, root) then
-- 如果是有效的报文
else
-- 当发现不是我的协议时,就使用Wiresahrk自带的Data解析器显示
data_dis:call(buf, pkt, root)
end
end
-- 向 wireshark 注册协议插件被调用的条件为TCP协议且端口为12345的报文
-- 符合条件就会调用上面的foo_proto.dissector函数
-- local udp_port_table = DissectorTable.get("udp.port")
local tcp_port_table = DissectorTable.get('tcp.port')
tcp_port_table:add(12345, foo_proto)
三、调试
1. 重新加载脚本
- 修改Lua 脚本
- 在Wireshark 中使用Ctrl+Shift+L 快捷键重新加载修改后的Lua 脚本
2. 查看控制台输出
- Lua 脚本中print(VariableName) 在控制台输出对应变量值
- Edit 编辑 -> Preferences… 首选项(Ctrl+Shif+P ) -> Advanced 高级
- 搜索gui.console_open 并将对应值修改为 ALWAYS
3. 如何使用
- 在%APPDATA%\Wireshark 中新建文件夹plugins ,可以使用Win+R 键然后输入%APPDATA%\Wireshark然后回车,快速打开该目录
- 复制编写好的脚本到该文件夹%APPDATA%\Wireshark\plugins
- 重新打开抓包文件或者Ctrl+Shift+L 快捷键重新加载插件脚本
四、进阶
1. 不绑定端口怎么办?
启发式解析器
在 Lua 中创建独立于端口(启发式)的 Wireshark 解调器|米卡的技术博客 (mika-s.github.io)
wireshark/README.heuristic at master - wireshark/wireshark (github.com)
2. 粘包,分包问题
粘包使用while 循环处理
分包设置pinfo.desegment_len 的值或者使用dissect_tcp_pdus 函数
使用 lua 编写 wireshark 协议解析插件
Functions For New Protocols And Dissectors
3. 大端(Big End)小端(Little End)问题
Wireshark中默认是大端,如果需要处理小端协议,可以将add 函数替换为add_le 的函数
参考
Lua语法
Lua 5.2 Reference Manual - contents
教程
用Lua语言编写Wireshark dissector插件 (archive.org)
Creating a Wireshark dissector in Lua - part 1 (the basics) | Mika’s tech blog (mika-s.github.io)
强烈推荐:Mika’s tech blog (mika-s.github.io)
Wireshark Dissector文档
10.3. Example: Dissector written in Lua (wireshark.org)
Chapter 11. Wireshark’s Lua API Reference Manual
11.7. Adding Information To The Dissection Tree (wireshark.org)