Wireshark网络协议解析原理与新协议添加方法

时间:2022-08-30 08:43:54

Wireshark网络协议解析原理与新协议添加方法

摘要:为了快速对自定义网络协议进行协议解析,对Wireshark网络协议解析原理与添加方法进行了深入的分析。在此基础上,对Wireshark添加自定义网络协议的解析器。由于有效地利用了Wireshark已有框架,该方式相对传统自行开发协议分析工具具有工作量少、功能强大与兼容性好的优点。

关键词:协议解析;WireShark; 网络分析

中图分类号:TP393.03文献标识码:A文章编号:16727800(2011)012010503

作者简介:吕雪峰(1978-),男,湖北恩施人,硕士,中国船舶重工集团公司第七二二研究所工程师,研究方向为计算机软件;张春芳(1980-),女,湖北武汉人,中国船舶重工集团公司第七二二研究所助理工程师,研究方向为软件测试。

0引言

网络协议分析器(network analyzer)是对通用协议的数据包进行解码,并以人可读的格式显示网络流量内容的软件或设备。由于网络协议种类繁多,各种新协议层出不穷,因此分析器必须具有良好的可扩展性,可方便地支持新的协议。

Wireshark是一款免费开源的协议分析器,是目前应用最广泛的网络协议分析软件之一。本文介绍了WireShark网络协议解析的原理与新协议添加的方法,并分析了向WireShark中添加新协议解析器的两种方法,并对自定义的网络协议给出添加步骤。

1Wireshark系统结构

WireShark的系统结构如图1所示,主要功能模块如下:①GTK 1/2:图形窗口工具,操控所有的用户输入/输出界面;②Core:将其它模块连接起来,起到综合调度的作用;③Epan:Wireshark协议解析器;④Capture:数据包捕获引擎,依赖于底层库WinPcap/libpcap库;⑤Wiretap:从磁盘读写数据包文件的引擎。

2数据包协议解析原理

2.1协议树

基于OSI的7层协议模型,协议数据是从上到下封装后发送的,而对于协议解析需要从下至上进行。首先对网络层的协议识别后进行组包还原,然后脱去网络层协议头,将里面的数据交给传输层分析,这样一直进行下去,直到应用层。

由于网络协议种类很多,就Wireshark所识别的500多种协议来说,为使协议和协议间层次关系明晰,从而对数据流中各层次的协议能够逐层处理,Wireshark系统采用了协议树的方式,如图2中所示。图2中UDP协议的所有数据都是封装在IP协议中的,那么UDP协议就是IP协议的一个协议子节点,具有相同父节点的协议成为兄弟节点,如TCP与UDP节点。

图1Wireshark的系统结构图图2一个简单的协议树

Wireshark采用协议的特征字来识别拥有同样父协议的兄弟节点协议。每个协议注册自己的特征字,这些特征字给子节点协议提供可以互相区分开来的标识。在Wireshark中注册一个协议解析器首先需要指出它的父协议是什么,另外还需指出自己区别于父节点下所有兄弟节点协议的特征字。

由于采用了协议树加特征字的设计,Wireshark在协议解析上具备了很强的扩展性,增加一个协议只需要将该协议相关的解析函数挂到协议树上相应的节点即可。

2.2添加协议解析器的方式

向Wireshark添加新协议解析器有两种方式:内置型(builtin)和插件型(plugin)。

2.2.1插件型(plugin)

插件型是构建一个插件(如一个动态连接库),并把它自己注册到主程序中,执行解析工作。

插件技术,就是在程序的设计开发过程中,把整个应用程序分成宿主程序和插件两个部分,宿主程序与插件能够相互通信。并且,在宿主程序不变的情况下,可通过增减插件或修改插件来调整应用程序的功能。运用插件技术可以开发出伸缩性良好、便于维护的应用程序。

由于现在网络协议种类繁多,为了可以随时增加新的协议解析器,Wireshark也采用了插件技术。如果需要对一个新的协议进行解析,只需要开发这个协议解析器,并调用注册函数向主程序注册就可以了。

2.2.2内置型(builtin)

内置型是把一个协议解析器编译进主程序中,它将一直可用。

内置型与组件型的差异很小,只不过内置型的重构周期比组件型的长很多,同时没有插件型那么灵活。

3数据包解析器代码结构

新增加一个协议解析器的步骤一般分为注册协议,初始化解析器,实际解析处理3个步骤,下面以自定义协议演示如何增加一个协议解析器。插件型与内置型两种方式的代码结构基本相同,此处不做区别。自定义协议(FOO Protocol)格式如表1所示。

表1自定义协议格式

数据包类型标识序号IP地址该协议为UDP协议的子协议,端口为1234,其中:

数据包类型―― 一个字节,可能取值:1―初始化,2―终止,3―数据。

标识―― 一个字节, 可能取值:0x01―开始数据包, 0x02―结束数据包, 0x04―优先数据包。

序号―― 两个字节。

IP地址―― 四个字节。

3.1注册协议

主要调用proto_register_protocol函数来注册协议。

#define FOO_PORT 1234

static int proto_foo = -1;

static gint ett_foo = -1;

void proto_register_foo(void)

{

static hf_register_info hf[] = {

{ &hf_foo_pdu_type,

{ "FOO PDU Type", "foo.type",

FT_UINT8, BASE_DEC,

NULL, 0x0,

NULL, HFILL }

},

{ &hf_foo_flags,

{ "FOO PDU Flags", "foo.flags",

FT_UINT8, BASE_HEX,

NULL, 0x0,

NULL, HFILL }

},

{ &hf_foo_sequenceno,

{ "FOO PDU Sequence Number", "foo.seqn",

FT_UINT16, BASE_DEC,

NULL, 0x0,

NULL, HFILL }

},

{ &hf_foo_initialip,

{ "FOO PDU Initial IP", "foo.initialip",

FT_IPv4, BASE_NONE,

NULL, 0x0,

NULL, HFILL }

}

};

/* 设置协议子树数组*/

static gint *ett[] = {

&ett_foo

};

/*调用proto_register_protocol函数注册协议*/

proto_foo = proto_register_protocol (

"FOO Protocol", /* 协议名称 */

"FOO", /* 短名称(short name) */

"foo" /* 协议简写名称*/

);

proto_register_field_array(proto_foo, hf, array_length(hf));

proto_register_subtree_array(ett, array_length(ett));

}

3.2初始化解析器

把UDP端口进行绑定,所以主程序在接收到该端口的UDP数据包后就调用该解析器。其中dissect_foo为做实际解析工作的函数。

void proto_reg_handoff_foo(void)

{

static dissector_handle_t foo_handle;

foo_handle = create_dissector_handle(dissect_foo, proto_foo);

dissector_add("udp.port", FOO_PORT, foo_handle);

}

3.3实际解析处理

添加对具体协议各字段的解析。

static int hf_foo_pdu_type = -1;

static int hf_foo_flags = -1;

static int hf_foo_sequenceno = -1;

static int hf_foo_initialip = -1;

static void dissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)

{

gint offset = 0;

/*检查协议列是否可以在用户界面显示*/

if (check_col(pinfo->cinfo, COL_PROTOCOL))

{

col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");

}

/* 如果能显示,清除INFO列中的数据*/

if (check_col(pinfo->cinfo,COL_INFO))

{

col_clear(pinfo->cinfo,COL_INFO);

}

/* 添加一个子协议,并对协议进行解析*/

if (tree) {

proto_item *ti = NULL;

proto_tree *foo_tree = NULL;

ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, FALSE);

foo_tree = proto_item_add_subtree(ti, ett_foo);

proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, FALSE); offset += 1;

proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, FALSE); offset += 1;

proto_tree_add_item(foo_tree, hf_foo_sequenceno, tvb, offset, 2, FALSE); offset += 2;

proto_tree_add_item(foo_tree, hf_foo_initialip, tvb, offset, 4, FALSE); offset += 4;

}

}

有关协议解析器添加更详细、更具体的内容参见文献[2]。

4解析器代码的编译

插件型与内置型解析器代码的编译区别较大,对此分别说明。

4.1.1插件型(plugin)

为了编译该解析器代码来创建该插件,除了解析器的源代码packet-foo.c外,还需要在该源代码文件所在目录下创建下列一些文件,其包含了Windows平台与UNIX/Linux平台下的各自所需文件。①Makefile.省略mon ―― 包含插件文件的名称;③Makefile.nmake ―― 包含Wireshark插件的Windows平台下的makefile;④moduleinfo.h ―― 包含插件版本信息;⑤moduleinfo.nmake ―― 包含Windows 平台下动态链接库(DLL)的版本信息;⑥packetfoo.c ―― 自定义协议解析器的源代码;⑦plugin.rc.省略mon与Makefile.am文件必须修改,来反映相关的文件与解析器名称。

moduleinfo.h 与moduleinfo.nmake文件必须填写版本信息。

把该解析器编译成一个动态连接库或共享库,并复制到已安装Wireshark的plugin目录下,即可使用。

4.1.2内置型(builtin)

内置型协议解析器源代码都在目录wireshark-1.2.0\\epan\\dissectors下。此处,我们以添加UDP协议为例,说明内置型添加协议解析器的步骤:

第一步:将要添加的协议解析器源文件packet-udp.c和头文件packet-udp.省略mon文件,分别将源文件和头文件名添加到对应的DISSECTOR_SRC和DISSECTOR_INCLUDE宏下面。

DISSECTOR_SRC = \ …

packetudp.c \ …

DISSECTOR_INCLUDES = \ …

packetudp.h \ …

第三步:最后对整个工程进行编译连接,就可以把要添加的协议解析器,如同Wireshark大多数内置解析器一起被整合编译到libwireshark中。

5结束语

在自行开发自定义协议分析器时,除了需要处理大量底层数据捕获、缓存与存储的开发工作外,还涉及到复杂的数据包过滤、网络信息统计与数据包内容显示的开发工作,并且很难提供足够的实用功能。

通过有效利用Wireshark已有框架,使得自定义协议解析器的开发量可显著减少,并可提供功能强大与兼容性更好的协议解析器,从而为网络分析工作提供强有力的工具支持。

参考文献:

[1]WinPcap 技术文档4.0.1[EB/OL].www.省略.20110610.

[2]Wireshark Developer's Guide 29135 for Wireshark 1.2.0[EB/OL].www.省略.20110610.

[3]罗青林,徐克付,臧文羽,等.Wireshark环境下的网络协议解析与验证方法[J].计算机工程与设计,2011(3).

上一篇:职业教育黄页网站的设计与实现 下一篇:Ad Hoc网络未来发展方向探讨