如何自定义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

什么是 LA, RA and TA?

术语/Terminology

  • LA: 位置区(Location Area)。LAI: 位置区标识符(Location Area Identify),由PLMN+LAC构成,其中LAC是位置区编码(Location Area Code)。
  • RA: 路由区(Route Area)。 RAI: 路由区标识符(Route Area Identify),由PLMN+LAC+RAC构成,其中RAC是路由区编码(Route Area Code)。
  • TA: 跟踪区(Track Area) TA: 跟踪区标识符(Track Area Identify),由PLMN+TAC构成,其中TAC是跟踪区编码(Track Area Code)。

LA和RA是存在于2G/3G中的概念,而TA是在4G中新引入的概念。通过引入TA使通信网络更好的支持通信设备在不同小区间的移动。

详情/Detail

LA是在2G/3G中电路域(circuit domain)中的概念,用来跟踪当前移动终端的位置。当核心网需要向移动终端发送信令时,将根据当前移动终端的LA信息决定改信令数据的传输目的地。当移动终端从一个小区移动到另一个小区时,它需要发送相应的信令通知核心网更新LAI。

RA同样也是2G/3G中的概念,同样用来跟踪当前移动终端的位置。与LA不同的是,RA是包域(packet domain)中的概念。通过RAI让SGSN(Serving GPRS Support Node,服务GPRS支撑节点)知道当前终端的位置,以传输用户数据。所以可以认为LAI是用来知道传输信令数据,RAI用来知道传输用户上网数据。

TA是在4G网络中引入的新概念,它定义了一个区域(主要不是一个小区),移动终端在该区域内移动时不需要更新TAI。所以移动终端可以在该区域内自由移动,但是当它移动到另外一个区域时则需要通知核心网更新TAI。TA是小区层次的配置,多个小区可以拥有相同的TA,但是一个小区只能分配到一个TA中。

4G中的TA/TA in 4G

获取eNB的TAI标识

移动终端在随机接入之前会收到eNB的广播`SystemInformationBlockType1`类型数据包,在该数据包中包含了当前eNB的一些基本信息,例如:PLMN标识、跟踪区编码、小区标识符等。

核心网分配TAI List

随后当移动终端成功建立第一个默认EPS承载中,会收到一个Attach Accept类型的数据包,该数据包中包含了当前核心网分配的TAI list,这里可以看到该List共包含4个小区信息。当移动终端在TAI list所列出的小区内进行移动时不需要向核心网更新TAI标识。

更新TAI

当移动终端移动到一个新的小区(该小区不在核心网分配的TAI List中)时,移动终端会发起Tracking area update request (0x48)请求以更新TAI。核心网也通过Tracking area update accept (0x49)返回一组新的TAI List。

流程如下