Wireshark中lua脚本介绍
概述
Wireshark是非常强大的报文解析工具,是网络定位中不可缺的使用工具,在物联网中很多为自定义协议,wireshark无法解析,此时lua脚本就有了用武之地。Lua是一个脚本语言,不需要编译可以直接调用,完美解决了自定义报文解析。
代码框架
-- create a new dissector
local NAME = "Doip"
local PORT = 13400
local Doip = Proto(NAME, " Doip Protocol")
-- dissect packet
function Doip.dissector (tvb, pinfo, tree)
end
-- register this dissector
DissectorTable.get("udp.port"):add(PORT, Doip)
如上一个简单的lua代码分为三部分:
- 创建Proto对象
- 创建dissector方法
- 注册解析器
加载解析器到wireshark
将lua文件放在wirekshark的安装目录,在wireshark的根目录中找到init.Lua,打开后将文件中的enable_lua设置为true,并在文件目录中增加我们编写的lua脚本,使用的为dofile(DATA_DIR.."DoIP.lua”)。
转存失败重新上传取消
Figure 1lua脚本放置位置
转存失败重新上传取消
Figure 2修改enable_lua为true
转存失败重新上传取消
Figure 3注册新编写脚本
重新打开wireshark或者shift+ctrl+L快捷键进行lua加载即可进行解析。
Lua插件API接口
- Proto对象
表示一个新的protocol,使用
接口 | 说明 |
proto:__call (name,desc) | 创建Proto对象。name和desc分别是对象的名称和描述,前者可用于过滤器等 |
proto.name | get名称 |
proto.fields | get/set字段 |
proto.prefs | get配置项 |
proto.init | 初始化,无参数 |
proto.dissector | 解析函数,3个参数tvb,pinfo,tree,分别是报文内容,报文信息和解析树结构 |
proto:register_heuristic (listname, func) | 为Proto注册一个启发式解析器,被调用时,参数func将被传入与dissector方法相同的3个参数 |
常用使用:
local NAME1 = "red"
local PORT = 5004
local RTP_PROTO_TYPE = 106
local red = Proto(NAME1, "Red Protocol")
- Protofield
此对象为协议字段,用于解析字段后在描述字上添加节点,根据接口不同可以分成两大类:
整型:
ProtoFiled.{type}(abbr,[name],desc,base,valuestring,mask)
Type包括:uint8,uint16,uint24,uint32,uint64,framenum
Abbr:过滤器的名字
Name:在解析树中的名字
Base:One of base.DEC, base.HEX or base.OCT, base.DEC_HEX, base.HEX_DEC, base.UNIT_STRING or base.RANGE_STRING.
Valuestring:可以用表的形式来进行解析,也可以理解为switch语句
Mask:类型掩码
Desc:字段描述
其他类型:
ProtoField.{type},(abbr,[bane],[desc])
Type包括:float,double,string,stringz,bytes,bool,ipv4,ipv6,ether,oid,guid.
[]内的事可选字段,{}中的事可替换字段。
示例如下:
-- create fields of red
fields_M = ProtoField.uint8 (NAME1 .. ".M", "M",base.HEX,Payload_type,0x80)
fields_pt = ProtoField.uint8 (NAME1 .. ".PT", "PT", base.DEC,Payload_type,0x7F)
fields_seqno = ProtoField.uint16(NAME1 .. ".seqno", "Sequence number")
fields_h264bytes = ProtoField.bytes(NAME1 .. ".bytes", "H264Data")
fields_fec = ProtoField.bytes(NAME1 .. ".fec", "FEC Payload")
这些字段添加到结构树种后显示如下:
转存失败重新上传取消
- Tvb
Testy vitual Buffer表示报文缓存,也就是实际报文数据,可以通过一下方法将报文数据中解析出信息,接口如下:
tvb:__tostring() | 将报文数据转化为字符串,可用于调试 |
tvb:reported_len() | get tvb的(not captured)长度 |
tvb:len() | get tvb的(captured)长度 |
tvb:reported_length_remaining() | 获取当前tvb的剩余长度,如果偏移值大于报文长度,则返回-1 |
tvb:offset() | 返回原始偏移 |
常用的字段是tvb:len()
- Pinfo
报文信息(packet information),接口如下:
接口 | 说明 |
pinfo.len pinfo.caplen | get报文长度 |
pinfo.abs_ts | get报文捕获时间 |
pinfo.number | get报文编号 |
pinfo.src pinfo.dst | get/set报文的源地址、目的地址 |
pinfo.columns pinfo.cols | get报文列表列(界面) |
获得列表信息后就可以设备该列文本,比如
-- show protocol name in protocol column
pinfo.cols.protocol = “RED”
如上,可以将协议类型显示为RED
- Treeitem
解析树,用来展示字段的树形结构
接口 | 说明 |
tree_item:add_packet_field(protofield, [tvbrange], encoding, [label]) | 使用协议字段创建一个子树,可以根据入参调整大小字节序ENC_BIG_ENDIAN or ENC_LITTLE_ENDIAN |
treeitem:add([protofield], [tvbrange], [value], [label]) | 增加一个子树,并返回该子树,使用大字节序解析 |
treeitem:add_le([protofield], [tvbrange], [value], [label]) | 增加一个子树,并返回该子树,使用小字节序解析 |
treeitem:append_text | 附加文本到这个表 |
treeitem:prepend_text(text) | 在表前增加文本 |
treeitem:add_proto_expert_info(expert, [text]) | 设置树项的专家标志并将专家信息添加到包中。 |
- DissectorTable
这个是一个具体协议解析表,比如TCP的解析表tcp.port包括http,SMTP,ftp等
此对象的接口如下:
接口 | 说明 |
DissectorTable.get(name) | get名为name的解析表的引用 |
dissectortable:add(pattern, dissector) | 将Proto或Dissector对象添加到解析表,即注册。pattern可以是整型值,整型值范围或字符串,这取决于当前解析表的类型 |
dissectortable:remove(pattern, dissector) | 将满足pattern的一个或一组Proto、Dissector对象从解析表中删除 |
- 其他API接口
https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Proto.html
- 样例:
local version_str = string.match(_VERSION, "%d+[.]%d*")
local version_num = version_str and tonumber(version_str) or 5.1
local bit = (version_num >= 5.2) and require("bit32") or require("bit")
-- create a new dissector to decode rtp private payload
local NAME1 = "red"
local PORT = 5004
local RTP_PROTO_TYPE = 106
local red = Proto(NAME1, "Red Protocol")
-- create fields of red
fields_M = ProtoField.uint8 (NAME1 .. ".M", "M", base.HEX,Payload_type,0x80)
fields_pt = ProtoField.uint8 (NAME1 .. ".PT", "PT", base.DEC,Payload_type,0x7F)
fields_seqno = ProtoField.uint16(NAME1 .. ".seqno", "Sequence number")
fields_h264bytes = ProtoField.bytes(NAME1 .. ".bytes", "H264Data")
fields_fec = ProtoField.bytes(NAME1 .. ".fec", "FEC Payload")
red.fields = { fields_M, fields_pt, fields_seqno, fields_h264bytes,fields_fec }
local RTP_dis = Dissector.get("rtp")
local H264_dis = Dissector.get("h264")
local Data_dis = Dissector.get("data")
-- dissect packet
function red.dissector(tvb, pinfo, tree)
length = tvb:len()
if length == 0 then return end
-- decode private header
local subtree = tree:add(red, tvb(0,3))
subtree:add(fields_M, tvb(0,1))
subtree:add(fields_pt, tvb(0,1))
subtree:add(fields_seqno, tvb(1,2))
-- show protocol name in protocol column
pinfo.cols.protocol = red.name
local fec_id = tvb(0,1):uint()
local fec_type = bit.band(fec_id,0x7F)
if fec_type == 109 then
tree:add(fields_fec,tvb(3))
else
H264_dis:call(tvb(3):tvb(), pinfo, tree)
end
end
--decode first layer as rtp
local udp_dissector_table = DissectorTable.get("udp.port")
udp_dissector_table:set(PORT,RTP_dis)
-- register this dissector
-- DissectorTable.get("rtp.pt"):add(PORT, red)
--decode private protocol layer 3-bytes private datas + standard h264
local rtp_dissector_table = DissectorTable.get("rtp.pt")
rtp_dissector_table:set(RTP_PROTO_TYPE,red)
附录:
red.dissector(tvb, pinfo, tree)