Loading

Wireshark解析器(Dissector)插件-Lua

现有问题

如果直接使用Wireshark查看常见诸如Http等协议报文,并不会有任何不便,

但是如果查看私有协议报文,无法区分哪些是TCP协议相关报文,哪些是含有消息体的私有协议报文,也只能看到一串Byte和对应ASCII编码的Data值,无法知道数据中的各个字节对应哪个字段代表什么含义,更不能根据这些对应字段值进行过滤

什么是解析器(Dissector)

解析器(Dissector)是Wireshark中的概念,用于解析协议,将报文中对应的Bytes转为相应的字段值

可以简单理解为Wireshark中的解码器。

它不止能解析将相应位置的Bytes转为对应字段,还能将解析出来的字段用于报文过滤,还能自定义信息(Info)列中显示的自定义信息。

能方便开发人员调试诊断协议相关问题,比如断连后不重连,报文不正确,未发心跳。

编写流程

解析器可以使用C语言编写,也可以使用Lua编写

本篇使用Lua编写,因为该语言是脚本语言,不需要编译后执行,方便开发修改调试

缺点是比C语言编写的解析器慢

编写解析器需要使用lua 语言 所以建议先学一点基础,或者碰到看不懂的再查也行

Lua基础知识请参考

添加解析器插件文件

  • 在%APPDATA%\Wireshark 中新建文件夹plugins ,可以使用Win+R 键然后输入%APPDATA%\Wireshark然后回车,快速打开该目录
  • 在plugins 文件夹下新建xxx.lua 文件,其中xxx 一般以协议名为文件名

解析器由什么部分组成

  • 协议定义
  • 字段定义
  • 协议和字段关联
  • 解析器主函数
  • 协议注册

解析器代码详解

-- 协议定义,名称为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)

如何调试

重新加载脚本

  • 修改Lua 脚本
  • 在Wireshark 中使用Ctrl+Shift+L 快捷键重新加载修改后的Lua 脚本

查看控制台输出

  • Lua 脚本中print(VariableName) 在控制台输出对应变量值
  • Edit 编辑 -> Preferences... 首选项(Ctrl+Shif+P ) -> Advanced 高级
  • 搜索gui.console_open 并将对应值修改为 ALWAYS

如何使用

  • 在%APPDATA%\Wireshark 中新建文件夹plugins ,可以使用Win+R 键然后输入%APPDATA%\Wireshark然后回车,快速打开该目录
  • 复制编写好的脚本到该文件夹%APPDATA%\Wireshark\plugins
  • 重新打开抓包文件或者Ctrl+Shift+L 快捷键重新加载插件脚本

高级

  • 不绑定端口怎么办?

启发式解析器

在 Lua 中创建独立于端口(启发式)的 Wireshark 解调器|米卡的技术博客 (mika-s.github.io)

wireshark/README.heuristic at master - wireshark/wireshark (github.com)

  • 粘包,分包问题

粘包使用while 循环处理

分包设置pinfo.desegment_len 的值或者使用dissect_tcp_pdus 函数

使用 lua 编写 wireshark 协议解析插件_mb5fe55a9dbe9dd的技术博客_51CTO博客

11.6. Functions For New Protocols And Dissectors (wireshark.org)

  • 大端(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文档

Dissectors (wireshark.org)

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)


原文链接:https://www.cnblogs.com/KSPT/p/16688475.html

posted @ 2022-09-20 21:19  知科行技  阅读(3166)  评论(0编辑  收藏  举报