Appearance
Appearance
本页说明如何用 Lua 编写 Wireshark dissector,并重点提醒 TCP 重组、post-dissector 和 chained dissector 的差异。它适合已经知道 Lua 在 Wireshark 中能做什么、现在需要落到 dissector 实现细节的读者。
| 主题 | 本页关注点 | 容易混淆的点 |
|---|---|---|
| 普通 dissector | 注册到某个 DissectorTable,处理特定 payload | 只有匹配注册表或被用户 Decode As 强制指定时才会调用 |
| TCP 重组 | 处理 TCP 分段、多消息、缺失前序分段和截断包 | 不能处理重组时,不应把 Proto 加到 TCP DissectorTable |
| post-dissector | 在其他 dissector 之后运行,读取已有字段并补充树节点 | 它不是替代原协议 dissector,而是事后补充 |
| chained dissector | 包装并调用原 dissector,再追加自己的分析 | 必须保留并调用原 dissector,否则原有解析会被替换掉 |
dissector 用于分析数据包数据的一部分。它们类似于用 C 编写的 dissector,但本节只讨论普通 Lua dissector;启发式 dissector 和 post-dissector 的工作方式不同。
| 要点 | 说明 |
|---|---|
| 注册位置 | dissector 必须注册为处理另一种协议或原始 wiretap 类型的某种 payload |
| 注册方式 | dissector 函数必须分配给一个 Proto 对象 |
| 调用参数 | Wireshark 调用 dissector 时会传入 Tvb 缓冲区、Pinfo 数据包信息记录、TreeItem 树根 |
| 调用条件 | 只有数据包匹配 Proto 所设置到的 DissectorTable,或用户用 Decode As 强制使用它时,dissector 才会被调用 |
| Decode As 限制 | 只有 Proto 对象注册到正确类型的 DissectorTable 时,用户才能用 Decode As 强制使用;例如注册到 UDP 表的 Proto 不能用于解码链路层 |
如果你无法处理重组,就不应该为 TCP payload 编写 dissector,也就是说,不要把你的 Proto 对象添加到 TCP 的 DissectorTable。与 C dissector 一样,Lua dissector 可以使用 Wireshark 重组 TCP 流的能力。
| 情况 | 说明 | 处理思路 |
|---|---|---|
| 消息被分段 | TCP 数据包分段可能只包含你的消息第一部分 | 解析到足以判断完整长度后,设置 pinfo.desegment_len 和 pinfo.desegment_offset |
| 一个分段含多条消息 | TCP 数据包分段可能包含多条你的消息 | 在 dissector 中编写自己的 while 循环,每次调用时解析多条消息 |
| 捕获从会话中间开始 | 第一个交给 dissector 的 TCP 分段可能是完整消息的中间部分 | 对早期字段做合理性检查;失败时返回 0,等待后续分段 |
| 数据包被截断 | 用户可能限制了捕获数据包大小 | 检查 Tvb 的 len() 与 reported_len();不同则表示该包被截断,可选择不解析 |
| 组合情况 | 上述情况可能同时出现 | 按协议格式设计更稳健的边界检查和重组逻辑 |
对于只收到消息前半段的情况,dissector 必须解析到足以判断完整长度的程度:
| 你知道什么 | 应设置什么 |
|---|---|
| 知道还需要多少字节 | 将 pinfo.desegment_len 设置为相对于当前 Tvb 中已有字节还需要的额外字节数 |
| 不知道确切还需要多少字节 | 将 pinfo.desegment_len 设置为 DESEGMENT_ONE_MORE_SEGMENT |
| 希望下次从哪里继续 | 将 pinfo.desegment_offset 设置为 tvbuff 中的偏移量 |
Wireshark 下次收到 TCP 数据包分段时,会再次调用你的 Proto dissector 函数,并传入一个 Tvb 缓冲区。该缓冲区由前一个 Tvb 中从 desegment_offset 开始的数据字节,以及另外 desegment_len 个字节组成。
| 场景 | 返回值与注意事项 |
|---|---|
| 数据包不属于你的 dissector | 返回 0;此时不得设置 pinfo.desegment_len 或 pinfo.desegment_offset |
| 需要更多字节 | 先设置 pinfo.desegment_len 和 pinfo.desegment_offset;然后不返回任何内容,或返回 Tvb 长度 |
| 不需要更多字节 | 不返回任何内容,或返回 Tvb 长度 |
已设置 desegment_len / desegment_offset | 不要返回 0 |
| 不知道确切长度 | 使用 DESEGMENT_ONE_MORE_SEGMENT 表示不知道消息有多长 |
| 已设置正数长度 | 一旦把 pinfo.desegment_len 设置为正数,该长度不可更改;不能之后把同一消息长度从 X 改为 Y |
-- trivial protocol example
-- declare our protocol
trivial_proto = Proto("trivial", "Trivial Protocol")
-- create a function to dissect it
function trivial_proto.dissector(buffer, pinfo, tree)
pinfo.cols.protocol = "TRIVIAL"
local subtree = tree:add(trivial_proto, buffer(), "Trivial Protocol Data")
subtree:add(buffer(0, 2), "The first two bytes: " .. buffer(0, 2):uint())
subtree = subtree:add(buffer(2, 2), "The next two bytes")
subtree:add(buffer(2, 1), "The 3rd byte: " .. buffer(2, 1):uint())
subtree:add(buffer(3, 1), "The 4th byte: " .. buffer(3, 1):uint())
end
-- load the udp.port table
udp_table = DissectorTable.get("udp.port")
-- register our protocol to handle udp port 7777
udp_table:add(7777, trivial_proto)post-dissector 是一种注册后在其他所有 dissector 都已被调用之后再运行的 dissector。它很适合读取已有协议字段,并向解析树追加自己的项目。
-- trivial postdissector example
-- declare some Fields to be read
ip_src_f = Field.new("ip.src")
ip_dst_f = Field.new("ip.dst")
tcp_src_f = Field.new("tcp.srcport")
tcp_dst_f = Field.new("tcp.dstport")
-- declare our (pseudo) protocol
trivial_proto = Proto("trivial", "Trivial Postdissector")
-- create the fields for our "protocol"
src_F = ProtoField.string("trivial.src", "Source")
dst_F = ProtoField.string("trivial.dst", "Destination")
conv_F = ProtoField.string("trivial.conv", "Conversation", "A Conversation")
-- add the field to the protocol
trivial_proto.fields = {src_F, dst_F, conv_F}
-- create a function to "postdissect" each frame
function trivial_proto.dissector(buffer, pinfo, tree)
-- obtain the current values the protocol fields
local tcp_src = tcp_src_f()
local tcp_dst = tcp_dst_f()
local ip_src = ip_src_f()
local ip_dst = ip_dst_f()
if tcp_src then
local subtree = tree:add(trivial_proto, "Trivial Protocol Data")
local src = tostring(ip_src) .. ":" .. tostring(tcp_src)
local dst = tostring(ip_dst) .. ":" .. tostring(tcp_dst)
local conv = src .. "->" .. dst
subtree:add(src_F, src)
subtree:add(dst_F, dst)
subtree:add(conv_F, conv)
end
end
-- register our protocol as a postdissector
register_postdissector(trivial_proto)chained dissector 允许你访问某个 dissector 的数据,但不必针对每个数据包运行。典型做法是先保存原 dissector,再让自己的 dissector 接管同一端口,并在内部调用原 dissector,随后追加自己的字段或分析结果。
-- works as of Wireshark v0.99.7
do
local http_wrapper_proto = Proto("http_extra", "Extra analysis of the HTTP protocol")
-- our new fields
local F_newfield1 = ProtoField.uint16("http.newfield1", "Our new field, #1", base.DEC)
local F_newfield2 = ProtoField.uint16("http.newfield2", "Our new field, #2", base.DEC)
-- add the fields to the protocol
-- NOT ProtoFieldArray, that stopped working a while ago
http_wrapper_proto.fields = {F_newfield1, F_newfield2}
-- declare the fields we need to read
local f_set_cookie = Field.new("http.set_cookie")
local f_referer = Field.new("http.referer")
local original_http_dissector
function http_wrapper_proto.dissector(tvbuffer, pinfo, treeitem)
-- we've replaced the original http dissector in the dissector table,
-- but we still want the original to run, especially because we need to read its data
original_http_dissector:call(tvbuffer, pinfo, treeitem)
if f_set_cookie() then
-- this has two effects:
-- 1. makes it so we can use "http_extra" as a display filter
-- 2. displays a new header in the tree pane for our protocol
local subtreeitem = treeitem:add(http_wrapper_proto, tvbuffer)
field1_val = 42
subtreeitem:add(F_newfield1, tvbuffer(), field1_val)
:set_text("Don't panic: " .. field1_val)
field2_val = 616
subtreeitem:add(F_newfield2, tvbuffer(), field2_val)
:set_text("The REAL number of the beast: " .. field2_val)
end
end
local tcp_dissector_table = DissectorTable.get("tcp.port")
original_http_dissector = tcp_dissector_table:get_dissector(80)
tcp_dissector_table:add(80, http_wrapper_proto)
end导入自 https://wiki.wireshark.org/Lua/Dissectors,时间为 2020-08-11 23:16:08 UTC