Wireshark Lua 插件教程

本⽂主要介绍 Lua 脚本在 Wireshark 中的应⽤, Lua 脚本可以在 Wireshark 中完成如下功能:

  • 从⽹络包中提取数据, 或者统计⼀些数据包(Dumper)

  • 需要解析⼀种 Wireshark 不提供原⽣⽀持的协议(Dissector)

⽰例

协议解析

VREP 协议是 NOGD 框架对于 TRIP 协议的⼀种延伸和扩展. Wireshark 原⽣并不提供对于 VREP 协议的⽀持, 下图展⽰了脚本前后对⽐.

(a) VREP 原始字节流如下
VREP raw byte stream

(b) 脚本解析过后的信息
VREP with Lua script

数据流提取

Wireshark 对 RTPRTSP 均提供⽀持, 但是没提供对于 RTP over RTSP 协议的⽀持, 可以利⽤此处脚本提供的协议对此完成解析. 下图展⽰了这种差异.

(a) RTP over RTSP 原始信息
RTP over RTSP raw

(b) 脚本加强解析后的信息
RTP over RTSP with Lua

使⽤⽅法

假定你要使⽤ foo.lua 脚本

  1. 将脚本拷⻉到 Wireshark home ⽬录, 如 C:\Program\Files\Wireshark\foo.lua
  2. 修改 init.lua 脚本(C:\Program\Files\Wireshark\init.lua), 在末尾添加⼀⾏ dofile("foo.lua")
  3. 重启 Wireshark 使脚本⽣效

不同类型的脚本的使⽤⽅法:

  • Dissector 在选定的数据流中右击 -> 解码为… -> 选择脚本注册的协议, 如 RTPP.
  • Dumper 在⼯具菜单下⾯选择注册的 dumper.(如 Dump MPEG TS Packet)

解析器(Dissector)

注册新协议

注册新协议的⼀般步骤.

  1. 注册新协议

    • 基于 UDP 相对而⾔⽐较简单, 逐个解析 IP 包即可
    • 基于 TCP 解析器⽐较复杂, 需要考虑 TCP 重组(TCP Reassembly)
  2. 定义协议字段

  3. 注册协议字段

  4. 定义解析函数

  5. 注册到协议端口号

解析器代码框架

local ror = Proto("ror", "RTP over RTSP Protocol")-- 定义协议字段
local pf_ror_magic = ProtoField.uint8("ror.magic", "Magic", base.HEX)
local pf_ror_channel = ProtoField.uint8("ror.channel", "Interleaved Channel", base.HEX)
local pf_ror_length = ProtoField.uint16("ror.length", "Length")-- 注册协议字段
ror.fields = {pf_ror_magic ,pf_ror_channel ,pf_ror_length ,
}-- 在此处定义你精妙绝伦的解析函数function ror.dissector(tvbuf, pinfo, root)-- tvbuf: TCP segment-- pinfo: packet column info-- root: node info in the display zoneend-- 指定协议端⼝, 此处是tcp 端⼝
local tcp_dissector_table = DissectorTable.get("tcp.port")
tcp_dissector_table:add(554, ror)

TCP 包重组问题

作为 tcp 解析器必须要⽤能⼒处理下⾯⼏种情况

  1. TCP 数据段只含有协议数据包的前⼀部分
  2. TCP 数据段含有多个协议数据包
  3. 协议数据包在 TCP 数据段的中间部分, 因为协议包的前⼀部分可能没有被捕获到
  4. 数据包可能有被截断的情形
  5. 以上⼏种情形的任意组合

对以上问题的应对策略.

  1. 针对 4, 简单来说就是不解析切断的包(return 0)
  2. 针对 3, 解析函数必须要做⼀定的合法性检查, 如果不是属于该协议的包, 那么就丢弃该包(return 0)
  3. 针对 2, 在解析函数⾥⾯做⼀个 while 循环(return 已解析的⻓度)
  4. 针对 1, 尽最⼤可能去确定协议数据包的⻓度, 如果不能确定, 那么就返回⼀个默认值 DESEGMENT_ONE_MORE_SEGMENT

dissector 函数的返回值如下:

  1. 如果 TCP 数据段携带的数据不属于当前协议, 那么返回 0
  2. 如果需要更多的数据才能继续解析, 那么设置 desegment_len, desegment_offset, 返回值为 nil 或者已解析的⻓度都可以
  3. 如果不需要更多数据, 那么返回 nil 或者已经解析的⻓度都可以

Dumper

可以导出指定的协议字段到⽂件. 可选的字段:

  1. 来⾃预定义的字段(ip.src, tcp.port, rtsp.length)
  2. ⾃定义的字段(ror.magic)

Dumper 代码框架

-- 定义感兴趣的字段
-- wireshark 现已⽀持的协议的字段
local mpeg_pid = Field.new("mp2t.pid")
local mpeg_pkt = Field.new("mp2t")
-- ⾃定义协议的字段
local ror_channel = Field.new("ror.channel")-- 激活对话框时候的回调
local function init_payload_dump(file, filter)local tap = Listener.new(nil, filter)-- this function is going to be called once each time our filter matchesfunction tap.packet(pinfo, tvb)-- do some fancy workendretap_packets()tap:remove()
end-- 窗⼝回调
local function begin_dialog_menu()new_dialog("Dump MPEG TS Packets", init_payload_dump, "Output file", ilter")
end-- 注册窗⼝
register_menu("Dump MPEG TS Packets", begin_dialog_menu, MENU_TOOLS_UNSORTED)

样例解析

如下是⼀些解析样例.

  1. VREP Dissector
  2. RTP Over RTSP Dissector
  3. MPEG Dumper
  4. RTP over RTSP Dumper

VREP Dissector 解析样例

这个文件比较长, 请参考我的Github Repo

RTP Over RTSP Dissector 解析样例

-- 1. declare a new type of protocol
local rtpp = Proto("rtpp", "RTP over RTSP(iPanel Flavor)")-- 2. define some field into the rtpp
local pf_rtpp_magic = ProtoField.uint8("rtpp.magic", "Magic", base.HEX)local pf_rtpp_channel = ProtoField.uint8("rtpp.channel", "Interleaved Channel", base.HEX)
local pf_rtpp_length = ProtoField.uint16("rtpp.length", "Length")-- data 就是⽆法识别净荷内容的情况下简单的将其设置为⼆进制数据的⽅法
local default_parser = Dissector.get("data")-- 3. 注册新协议拥有的字段, 这些字段可作为以后抽取数据之⽤
rtpp.fields = {pf_rtpp_magic,pf_rtpp_channel,pf_rtpp_length,
}-- 前向声明
local dissect_rtpp
local rtpp_min_len = 4-- 此处处理TCP 重传的逻辑
function rtpp.dissector(tvbuf, pktinfo, root)local segment_length = tvbuf:len()local bytes_consumed = 0local reported_len = tvbuf:reported_length_remaining()if segment_length ~= reported_len then-- captured packets are being sliced/cut-off,-- so don't try to desegment/reassemblereturn 0endwhile bytes_consumed < segment_length do-- 此处会调⽤具体的解析函数local result = dissect_rtpp(tvbuf, pktinfo, root, bytes_consumed)if result == 0 thenreturn 0elseif result > 0 thenbytes_consumed = bytes_consumed + resultelsetinfo.desegment_offset = bytes_consumedresult = -resultpktinfo.desegment_len = resultreturn segment_lengthendendreturn bytes_consumed
end-- RTP over RTSP 有基本的RTSP 协议信令也有数据流,
-- 对基本的信令⽤默认的RTSP 解析器来解析
-- 对interleaved-channel 形式的按照其载荷类型来解析
dissect_rtpp = function(tvbuf, pinfo, root, offset)local msglen = tvbuf:len() - offsetdebug("sub_buf len=" .. msglen .. ", offset=" .. offset)if msglen < rtpp_min_len thendebug("sliced packet")return - DESEGMENT_ONE_MORE_SEGMENTend-- 分类解析if tvbuf:range(offset, 1):uint() ~= 0x24 then-- 以普通的rtsp 消息来解析debug("interpret packet as normal rtsp")local rtsp_dissector = Dissector.get("rtsp")-- 此处的返回值尚不清楚, 对此的处理也⽐较幼稚rtsp_dissector:call(tvbuf:range(offset, msglen):tvb(), pinfo, root)info("ret=" .. ret)return msglenelse-- interleaved-channel 形式debug("interpret packet as interleaved channel")local len_buf = tvbuf:range(offset + 2, 2)local payload_len = len_buf:uint()local packet_len = payload_len + 4debug("rtsp packet_len=" .. packet_len .. ", payload_len load=" .. pyload_len)-- ⾄少需要4 个字节才可以区分出该包是否属于RTP over RTSP 协议if msglen < packet_len thenreturn -(packet_len - msglen)end-- 添加⼀些界⾯显⽰信息root:add(pf_rtpp_magic,   tvbuf:range(offset + 0, 1))root:add(pf_rtpp_channel, tvbuf:range(offset + 1, 1))root:add(pf_rtpp_length,  len_buf)offset = offset + 4-- 取净荷的第⼀个字节来区分mpeg 和rtplocal probe_byte = tvbuf:range(offset, 1):uint()debug("probe_byte=" .. string.format( "0x%x ", probe_byte))if probe_byte == 0x47 then-- 0x47 开头的就⽤mpeg 的解析器来解析debug("raw mp2t packet, offset=" .. offset .. ", payload_len=" .. payload_len)local mpeg_dissector = Dissector.get("mp2t")while (offset + 188) <= packet_len dolocal mpeg_tvb = tvbuf:range(offset, packet_len - offset):tvb()-- 暂时不知道该函数的返回值是什么情况mpeg_dissector:call(mpeg_tvb, pinfo, root)offset = offset + 188endreturn offsetelseif probe_byte == 0x80 then-- 0x80 开头的尝试⽤rtp 来解析debug("RTP packet, offset=" .. offset .. ", payload_len=" .. payload_len)local rtp_dissector = Dissector.get("rtp")local rtp_tvb = tvbuf:range(offset, payload_len):tvb()-- 同样也是对返回值的情况不太了解.rtp_dissector:call(rtp_tvb, pinfo, root)if msglen ~= packet_len thendebug("length not match, payload_len + 4=" .. packet_len.. ", msglen=" .. msglen)endreturn packet_lenelsedefault_parser(tvbuf, pinfo, root)return packet_lenendend
end-- 将RTP over RTSP 协议注册到554 端⼝
-- 需要注意的是因为调⽤了原⽣RTSP 解析器, 所以我们的解析结果并不影响普通的rtsp 解析
tcp_dissector_table:add(554, rtpp)
tcp_dissector_table:add(2554, rtpp)
info("rtpp (RTP over RTSP) protocol registed at TCP port 554")

MPEG 流抽取器

if not GUI_ENABLED thenprint("mpeg_packets_dump.lua only works in Wireshark")return
end-- 声明要抽取的字段
local mpeg_pid = Field.new("mp2t.pid")
local mpeg_pkt = Field.new("mp2t")-- 窗口回调函数
local function init_payload_dump(file, filter)local packet_count = 0-- 对任意的udp 包进⾏过滤-- filter 为符合BPF 格式的任意过滤器local tap = Listener.new(nil, filter)local myfile = assert(io.open(file, "w+b"))-- 每次BPF 过滤器过滤出⼀个ip 包就会调⽤下⾯的函数function tap.packet(pinfo, tvb)-- 检查当前包⾥⾯是否有mpeg 包if (mpeg_pid()) thenpacket_count = packet_count + 1-- dump 出所有的mpeg 包local contents = {mpeg_pkt()}-- 逐个包输出到⽂件for i, finfo in ipairs(contents) dolocal tvbrange = finfo.rangelocal subtvb = tvbrange:tvb()myfile:write(subtvb:raw())-- myfile:flush()endendend-- re-inspect all the packets that are in the current capture, thereby-- triggering the above tap.packet functionretap_packets()-- cleanupmyfile:flush()myfile:close()tap:remove()debug("Dumped mpeg packets: " .. packet_count)
endlocal function begin_dialog_menu()new_dialog("Dump MPEG TS Packets", init_payload_dump, "Output file", "Packet filter (optional)\n\nExamples:\nip.dst == 225.1.1.4\nmp2t\nmp2t.pid == 0x300")
end-- 注册到程序菜单
register_menu("Dump MPEG TS Packets", begin_dialog_menu, MENU_TOOLS_UNSORTED)

RTP over RTSP 负载抽取

local mpeg_pid = Field.new("mp2t.pid")
local mpeg_pkt = Field.new("mp2t")
local rtp_payload = Field.new("rtp.payload")local function init_payload_dump(file, filter)local packet_count = 0local real_filter = "(rtpp.channel)"if filter ~= nil and filter ~= "" then-- 拼接⽤⼾输⼊的过滤参数real_filter = real_filter .. " and (" .. filter ..")"endlocal tap    = Listener.new(nil, real_filter)local myfile = assert(io.open(file, "w+b"))function tap.packet(pinfo, tvb)-- 检查是否有mpeg 数据包if (mpeg_pid()) thenlocal contents = {mpeg_pkt()}for i, finfo in ipairs(contents) dolocal tvbrange = finfo.rangelocal subtvb = tvbrange:tvb()myfile:write(subtvb:raw())endelse-- 检查是否是rtp 包local payload = rtp_payload()if payload thenlocal tvbrange = payload.rangelocal subtvb = tvbrange:tvb()myfile:write(subtvb:raw())endendendretap_packets()myfile:flush() myfile:close() tap:remove()
end

调试脚本

在命令⾏中启⽤ Wireshark, 然后可以在当前命令⾏中看到 Lua 脚本的打印输出. debug,warn(), info() 会输出到 Lua consolestdout

Debug Lua Script

参考链接

  • Wireshark Lua
  • Lua Dissector
  • Wireshark Lua Example

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/72066.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

吐血整理:在 Docker 中运行 Milvus

直接用docker 错误命令&#xff08;这个我试了三遍&#xff0c;浪费了很多时间&#xff09;&#xff1a; docker run -d --name milvus -p 19530:19530 -p 9091:9091 -v /var/lib/milvus:/var/lib/milvus milvusdb/milvus:latest 先看报错&#xff1a; 2025-02-24 16:02:39 …

【uniapp】在UniApp中实现持久化存储:安卓--生成写入数据为jsontxt

在移动应用开发中&#xff0c;数据存储是一个至关重要的环节。对于使用UniApp开发的Android应用来说&#xff0c;缓存&#xff08;Cache&#xff09;是一种常见的数据存储方式&#xff0c;它能够提高应用的性能和用户体验。然而&#xff0c;缓存数据在用户清除缓存或清除应用数…

【Excel】 Power Query抓取多页数据导入到Excel

抓取多页数据想必大多数人都会&#xff0c;只要会点编程技项的人都不会是难事儿。那么&#xff0c;如果只是单纯的利用Excel软件&#xff0c;我还真的没弄过。昨天&#xff0c;我就因为这个在网上找了好久发好久。 1、在数据-》新建查询-》从其他源-》自网站 &#xff0c;如图 …

星环科技推出DeepSeek全场景解决方案:即开即用、企业级部署、端侧智能三位一体

星环科技&#xff08;688031.SH&#xff09;正式发布DeepSeek全场景解决方案&#xff0c;全面覆盖个人用户、企业客户及行业场景需求&#xff0c;为用户提供从个人到企业、从云端到本地的全方位AI应用支持&#xff0c;为不同需求的用户提供了灵活、高效且安全的AI解决方案。 省…

let、const【ES6】

‌“我唯一知道的就是我一无所知。” - 苏格拉底 目录 块级作用域&#xff1a;var、let、const的对比&#xff1a;Object.freeze()&#xff1a; 块级作用域&#xff1a; 块级作用域指由 {} 包围的代码块&#xff08;如 if、for、while、单独代码块等&#xff09;形成的独立作用…

C++ 常见面试知识点

主要介绍C常见面试题 1、说一下你理解的C中的四种智能指针 常用接口 T* get(); T& operator*(); T* operator->(); T& operator(const T& val); T* release(); 将 封装在内部的指针置为nullptr, 但并不会破坏指针所指向的内容, 函 数返回的是内部指针置空之前…

AWS API Gateway灰度验证实现

在微服务架构中,灰度发布(金丝雀发布)是验证新版本稳定性的核心手段。通过将小部分流量(如 10%)导向新版本服务,可以在不影响整体系统的情况下快速发现问题。AWS API Gateway 原生支持流量按比例分配功能,无需复杂编码即可实现灰度验证。本文将详细解析其实现方法、最佳…

基于coze+微信小程序实现图片上传并利用大模型解析

项目截图&#xff1a; 实现代码&#xff08;直接搬去可用&#xff09; 前提&#xff1a;需要填写你的oss配置coze的api授权配置&#xff01;&#xff01;&#xff01; <template><view class"container"><!-- 高斯模糊背景 --><view class&qu…

Spring-boot3.4最新版整合swagger和Mybatis-plus

好家伙,今天终于开始用spring-boot3开始写项目了&#xff0c;以后要彻底告别1.x和2.x了&#xff0c;同样的jdk也来到了最低17的要求了,废话不多说直接开始 这是官方文档的要求jdk最低是17 maven最低是3.6 一. 构建工程,这一步就不需要给大家解释了吧 二. 整合Knife4j 1.大于…

jQuery UI API 文档

jQuery UI API 文档 引言 jQuery UI 是一个基于 jQuery 的用户界面库,它提供了丰富的交互式组件和效果,使得网页开发变得更加简单和高效。本文档旨在为开发者提供全面的 jQuery UI API 信息,帮助您更好地理解和应用 jQuery UI。 jQuery UI 简介 什么是 jQuery UI? jQu…

java GUI编程实现一个计算器

概述 闲来无事&#xff0c;利用java awt库写个简单的计算器玩玩。 实现 pom.xml <dependencies><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.26</version></dependency&…

C#装箱拆箱机制详解

在C#中&#xff0c;装箱&#xff08;Boxing&#xff09;和拆箱&#xff08;Unboxing&#xff09; 是值类型与引用类型之间转换的核心机制。它们的实现直接影响程序的性能和类型安全。 一、装箱&#xff08;Boxing&#xff09; 定义&#xff1a; 将值类型转换为引用类型&#…

MySQL 8.4 SQL 全攻略:所有知识点与实战场景

一、引言 MySQL 作为一款广泛使用的开源关系型数据库管理系统&#xff0c;在数据存储和管理领域占据着重要地位。MySQL 8.4 版本在性能、功能和安全性等方面都有了显著的提升。本文将全面介绍 MySQL 8.4 中 SQL 的各种知识点&#xff0c;并结合实战场景进行详细讲解&#xff0…

Qt监控系统远程回放/录像文件远程下载/录像文件打上水印/批量多线程极速下载

一、前言说明 在做这个功能的时候&#xff0c;着实费了点心思&#xff0c;好在之前做ffmpeg加密解密的时候&#xff0c;已经打通了极速加密保存文件&#xff0c;主要就是之前的类中新增了进度提示信号&#xff0c;比如当前已经处理到哪个position位置&#xff0c;发个信号出来…

超高速工业相机的应用

超高速工业相机一般安装在机器流水线上代替人眼来做测量和判断&#xff0c;通过数字图像摄取目标转换成图像信号&#xff0c;传送给专用的图像处理系统。图像处理系统对这些信号进行各种运算来抽取目标的特征&#xff0c;进而根据判别的结果来控制现场的设备动作。一般来说&…

Plugin ‘mysql_native_password‘ is not loaded`

Plugin ‘mysql_native_password’ is not loaded mysql_native_password介绍1. 使用默认的认证插件2. 修改 my.cnf 或 my.ini 配置文件3. 加载插件&#xff08;如果确实没有加载&#xff09;4. 重新安装或检查 MySQL 版本 遇到错误 ERROR 1524 (HY000): Plugin mysql_nativ…

苍穹外卖-阿里云OSS文件上传

苍穹外卖-阿里云OSS文件上传 一、阿里云OSS简介**获取AccessKey**获取enpoint 二、代码实现1 引入依赖2 定义OSS相关配置2.1 application-dev.yml2.2 application.yml 3 读取OSS配置3.1 AliOssProperties 4 生成OSS工具类对象4.1 AliOssUtil4.2 OssConfiguration2.5 CommonCont…

【工具】前端 js 判断当前日期是否在当前自然周内

【工具】前端 js 判断当前日期是否在当前自然周内 function isCurrentNaturalWeek(targetDate) {const today new Date();const dayOfWeek today.getDay(); // 0&#xff08;周日&#xff09;到6&#xff08;周六&#xff09;// 计算本周一的日期&#xff08;自然周从周一开…

【操作系统】处理机调度

处理机调度 一、调度的概念、层次1.1 三个层次1.2 七状态模型 二、调度算法的评价指标2.1 CPU利用率2.2 系统吞吐率2.3 周转时间2.4 等待时间2.5 响应时间 三、进程调度&#xff08;低级调度&#xff09;的时机3.1 需要进程调度的情况3.2 不能进程调度的情况3.3 闲逛进程 四、进…

SpringBoot 使用 spring.profiles.active 来区分不同环境配置

很多时候&#xff0c;我们项目在开发环境和生产环境的配置是不一样的&#xff0c;例如&#xff0c;数据库配置&#xff0c;在开发的时候&#xff0c;我们一般用测试数据库&#xff0c;而在生产环境&#xff0c;我们要用生产数据库&#xff0c;这时候&#xff0c;我们可以利用 p…