如何自定义Wireshark协议解析插件

众所周知Wireshark是一款强大的协议解析工具,在网络优化/安全/分析领域独占鳌头,应用十分广泛。但是笔者在使用过程中遇到需要自定义协议解析插件的问题,故此再次记录问题及解决方法。

问题

笔者在使用Wireshark分析pcap数据包时遇到保存信息不足,有部分笔者认为比较重要的信息没有在pcap中进行保存。所以笔者需要自定义数据包格式以保存更多信息,同时还需要编写Wireshark插件使Wireshark支持解析自定义格式。

以常见HTTP数据包为例,常见的数据包层次为IP+TCP+HTTP。笔者想在IP之前添加一个自定义数据格式,以保存更多的信息,则为Self-Protocol + IP + TCP + HTTP。

因此作者需要解决的问题大致有二:

  1. 首先告诉Wireshark,X之后的数据应该是自定义协议而不是IP协议;
  2. 告诉Wireshark自定义协议的解析方法;

解决办法

既然问题已经明确,那我们分别解决他们就是了。

首先,我们解决第一个问题,如果告诉Wireshark 数据应该是自定义协议而不是IP协议呢?在此我们回顾一下pcap文件的全局头文件格式

// Global Header
// This header starts the libpcap file and will be followed by the first packet header:
typedef struct pcap_hdr_s {
    guint32 magic_number;   /* magic number */
    guint16 version_major;  /* major version number */
    guint16 version_minor;  /* minor version number */
    gint32  thiszone;       /* GMT to local correction */
    guint32 sigfigs;        /* accuracy of timestamps */
    guint32 snaplen;        /* max length of captured packets, in octets */
    guint32 network;        /* data link type */
} pcap_hdr_t;

在该结构体中network定义了pcap数据包的解析方法,即每个数据帧的最外层(或第一个)数据包的数据格式。Wireshark支持很多数据包格式解析(参见 linktypes),在此我们使用DLT_USER来指示自定义数据包格式,例如我们使用了DLT 153。

然后第二个问题就是如何指示Wireshark解析自定义协议。Wireshark支持C或lua编写插件。本次笔者采用lua编写脚本。总体而言编写脚本大致分为三个部分:首先,声明自定义协议,指定协议的名称;然后,定义协议格式,即协议解析方法。最后定义调用规则。下面我将展开一一介绍。

声明自定义协议。如下所示直接调用Proto方法声明协议,其中第一个参数声明协议的名称,可直接用在Wireshark的filter中,第二个参数则是协议的注释,显示在Packet Details窗格中。

local ip_wrapper_proto = Proto("self_proto", "a powerful wrapper for IP protocol")

定义协议格式。可以通过ProtoField.xx定义协议字段,其中xx是协议格式类型(参见lua_class_ProtoField)。例如

local F_IP_direction = ProtoField.uint8("ip.dir", "Direction", base.DEC)
local F_IP_sn = ProtoField.uint32("ip.sn", "Sequence Number", base.DEC)

ip_wrapper_proto.fields = {F_IP_direction, F_IP_sn}

上面的定义协议格式只是声明了协议中的字段,以及指定字段的数据类型、名称、注释,及格式。

接下来则是声明如何解析原始的数据解析,即上述字段与原始数据的映射方式和相应的解析判断规则。解析方法则是在一下函数中进行声明定义:

function ip_wrapper_proto.dissector(tvbuffer, pinfo, treeitem)
    // 声明tvbuffer的协议格式是ip_wrapper_proto。当然在此之前可以做一些检测,判断当前协议是否为ip_wrapper_proto。因为笔者这里使用dlt_user定义的,所以就略过了检测。
    local subtreeitem = treeitem:add(ip_wrapper_proto, tvbuffer)
    // 在主窗口Protocols中显示协议名称
    pinfo.cols.protocol = "IP_Extra"

    // 解析数据
    local direction = tvbuffer(2, 1):uint()
    subtreeitem:add(F_IP_direction, tvbuffer(2, 1), direction):set_text(
        string.format("Direction: %d (%s)",direction,direction_opts[direction]))
    ...
end

其中tvbuffer可以看作是个容器,包含着本层协议的数据。其中tvbuffer(start, len),表示从tvfuffer的第start位置开始读取len长度的数据,后面的uint()则指示将读取出的数据解析为uint类型。

其中subtreeitem:add则是指示将读取到的数据与前面定义的字段进行绑定,并设置其在Packet Details中的显示格式。因此add的参数分别为协议字段、原始数据、解析后数据。

到此为止我们已经定义好了自定义协议的解析方法。但是有时我们还需要声明Wireshark如何协议剩下的数据,例如这里我们要告诉Wireshark剩下的数据是IP格式的。因此我们要使用如下方法进行声明

local raw_data = tvbuffer(10, tvbuffer:len() - 10)
local ip_dissector = Dissector.get("ip")
ip_dissector:call(raw_data:tvb(), pinfo, treeitem)

解析来就是将lua脚本放在Wireshark的插件目录,然后在配置DLT_USER表格即可。

参考资料

  1. https://www.tcpdump.org/linktypes.html
  2. https://www.cnblogs.com/little-kwy/p/14888454.html

发表回复

您的电子邮箱地址不会被公开。