标签: Wireshark
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://www.blogbus.com/shujiantang-logs/35858037.html
本文是Wireshark官方开发文档9.2节《添加一个基础的解析器》的翻译
9.2.2. 解析协议细节
现在我们已经有了一个可以运用的简单解析器,让我们再为它添点儿什么吧。首先想到的应该就是标示数据包的有效信息了。解析器在这方面给我们提供了支持。
首先要做的事情是创建一个子树以容纳我们的解析结果。这会使协议的细节显示得井井有条。现在解析器在两种情况下被调用:其一,用于获得数据包的概要信息;其二,用于获得数据包的详细信息。这两种情况可以通过树指针参数tree
来进行区分。如果树指针为NULL
,我们只需要提供概要信息;反之,我们就需要拆解协议完成细节的显示了。基于此,让我们来增强这个解析器吧。
例 9.4. 插入数据包解析.
static void dissect_foo(tvbuff_t *tvb,packet_info *pinfo,proto_tree *tree)
{
if(check_col(pinfo->cinfo,COL_PROTOCOL))
{
col_set_str(pinfo->cinfo,COL_PROTOCOL,"FOO");
}
/* Clear out stuff in the info column */
if(check_col(pinfo->cinfo,COL_INFO))
{
col_clear(pinfo->cinfo,COL_INFO);
}
if(tree)
{
/* we are being asked for details */
proto_item *ti=NULL;
ti=proto_tree_add_item(tree,proto_foo,tvb,0,-1,FALSE);
}
}
这里我们为解析添加一个子树。它将用于保管协议的细节,仅在必要时显示这些内容。
我们还要标识被协议占据的数据区域。在我们的这种情况下,协议占据了传入数据的全部,因为我们假设协议没有封装其它内容。因此,我们用
proto_tree_add_item
函数添加新的树结点,将它添加到传入的协议树tree
中,用协议句柄proto_foo
标识它,用传入的缓冲区tvb
作为数据,并将有效数据范围的起点设为0
,长度设为-1
(表示缓冲区内的全部数据)。至于最后的参数FALSE
,我们暂且忽略。做了这个更改之后,在包明细面板区中应该会出现一个针对该协议的标签;选择该标签后,在包字节面板区中包的剩余内容就会高亮显示。
现在进入下一步,添加一些协议解析功能。在这一步我们需要构建一组帮助解析的表结构。这需要对proto_register_foo
函数做些修改。首先定义一组静态数组。
例 9.5. 定义数据结构.
static hf_register_info hf[]=
{
{
&hf_foo_pdu_type,
{"FOO PDU Type","foo.type",FT_UINT8,BASE_DEC,NULL, 0x0,NULL,HFILL}
}
};
/* Setup protocol subtree array */
static gint *ett[]=
{
&ett_foo
};
接下来,在协议注册代码之后,我们对这些数组进行注册。
例 9.6. 注册数据结构.
proto_register_field_array(proto_foo,hf,array_length(hf));
proto_register_subtree_array(ett,array_length(ett));
变量hf_foo_pdu_type
和ett_foo
依然需要在文件顶部的某处予以声明。
例 9.7. 解析器全局数据结构.
static int hf_foo_pdu_type=-1;
static gint ett_foo=-1;
现在我们就可以对协议细节的显示做一番改善了。
例 9.8. 解析器开始数据包解析.
if(tree)
{
/* we are being asked for details */
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,0,1,FALSE);
}
协议的解析变得愈发有趣了。我们提取出协议的第一部分。数据包的首字节定义了foo协议的包类型。
函数
proto_item_add_subtree
的调用在协议树中添加了一个子树,我们就在这里进行细节解析。子树的展开受控于变量ett_foo
。当您在协议间切换时,由它记录子树是否展开。正像您从下面的函数调用中看到的那样,随后的所有解析都会添加到该子树中。函数
proto_tree_add_item
用于为子树foo_tree
添加项,这次调用使用变量hf_foo_pdu_type
控制项格式。PDU(协议数据单元)类型是一个单字节数据,位于数据包的首字节,我们将有效数据范围的起点设为0
,长度设为1
。我们假设它依照网络字节顺序,所以将最后一个参数设为FALSE
(TRUE
表示"little endian",FALSE
表示"big endian")。尽管对于单字节数据无所谓字节顺序,但我们最好还是保持指定字节顺序的良好习惯。如果详细查看静态数组中
hf_foo_pdu_type
的声明,我们能够获悉定义的明细:
hf_foo_pdu_type
:节点索引。FOO PDU Type
:项标示。foo.type
:过滤字符串。我们可以在过滤框中输入诸如foo.type=1
的结构。FT_UNIT8
:指定该项数据是一个8比特位的无符号整型。这和我们之前调用函数时设置的一字节有效数据是相一致的。BASE_DEC
:针对整型数据,指定将其作为十进制数显示。当然视具体情况也可以设置为“BASE_HEX”(十六进制)和“BASE_OCT”(八进制),以使数据更易辨识。- 至于结构中余下的部分我们暂且忽略。
如果您现在安装并试用这个插件,就会发现一些有用的东西了。
接下来让我们完成这个简单协议的解析工作吧。我们需要再添加一些hf数组成员和程序调用。
例 9.9. 完成数据包解析.
//添加到文件开始的某个地方,作为全局变量
static int hf_foo_flags=-1;
static int hf_foo_sequenceno=-1;
static int hf_foo_initialip=-1;
//添加到“proto_register_foo”函数中的“hf”数组中,作为数组的成员
{
&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}
},
//添加到“dissect_foo”函数中,实现数据包的解析
gint offset=0;
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;
- 这段代码解析了这个简单的虚构协议的全部内容。我们引入了一个新的变量
offset
以记录数据包解析的位置。将这些额外的代码块放入合适的位置,整个协议就可以得到全面的解析。