700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > wireshark协议插件开发--官方文档中文翻译

wireshark协议插件开发--官方文档中文翻译

时间:2022-11-08 02:56:38

相关推荐

wireshark协议插件开发--官方文档中文翻译

1. 工作机制

每个解剖器(dissector )解码其协议的一部分,然后将解码交给后续解剖器以获得封装协议。

每个解剖都从Frame解剖器开始,它解剖捕获文件本身的数据包细节(例如时间戳)。从那里它将数据传递给最低级别的数据解剖器,例如以太网头部的以太网解剖器。然后将有效载荷传递给下一个解剖器(例如IP),依此类推。在每个阶段,将解码和显示分组的细节。

解剖可以以两种可能的方式实施。一种是将解剖模块编译到主程序中,这意味着它始终可用。另一种方法是创建一个插件(共享库或DLL)来注册自己来处理解剖。

将解剖器作为插件或内置的方法几乎没有什么区别。最重要的是,插件的重建周期比内置插件的重建周期要短得多。因此,从插件开始使初始开发更简单,而完成的代码可能作为内置解剖器更有意义。

2. 添加一个基本的dissector

我们将从制作的“foo”协议开始。它包含以下基本项目。

A packet type - 8 bits, possible values: 1 - initialisation, 2 - terminate, 3 - data.A set of flags stored in 8 bits, 0x01 - start packet, 0x02 - end packet, 0x04 - priority packet.A sequence number - 16 bits.An IPv4 address.

2.1 设置dissector

你需要做出的第一个决定是,这个解剖器是一个内置的解剖器,包含在主程序中,还是一个插件。

插件最初是最容易编写的,所以让我们从头开始。插件可以很容易地作为内置运行,所以我们没有丢失任何东西。

// 1. 初始化#include“config.h” #include <epan / packet.h> #define FOO_PORT 1234 static int proto_foo = -1; void proto_register_foo(void){ proto_foo = proto_register_protocol(“FOO Protocol”,/ * name * / “FOO”,/ * short name * / “foo”/ * abbrev * / ); }

首先,我们将调用proto_register_protocol()注册协议。我们可以给它三个名字,用于在不同的地方展示。完整和短名称用于例如“首选项”和“启用协议”对话框以及文档中生成的字段名称列表。缩写用作显示过滤器名称。

// 2. 注册handle,与端口关联void proto_reg_handoff_foo(void){ static dissector_handle_t foo_handle; foo_handle = create_dissector_handle(dissect_foo,proto_foo); dissector_add_uint(“udp.port”,FOO_PORT,foo_handle); }

j接下来,我们创建一个解剖器handle; 它与foo协议相关联,并与调用例程进行实际解剖。然后我们将句柄与UDP端口号相关联,这样主程序就知道在该端口上获得UDP流量时会调用我们。

// 3. dissect_foo回调函数static int dissect_foo(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree _U_,void * data _U_){ / *将字符串“FOO”设置为我们的协议* / col_set_str(pinfo-> cinfo,COL_PROTOCOL,“FOO”); / *清除信息列中的内容* / col_clear(pinfo-> cinfo,COL_INFO); return tvb_captured_length(tvb); }

dissect_foo这个函数被调用来剖析呈现给它的包。

包数据保存在一个特殊的缓冲区中,在这里称为tvb。随着我们对协议细节的深入了解,我们将对这一点相当熟悉。

packet_info结构包含协议的一般数据,我们可以在这里更新信息。

tree参数是进行详细剖析的地方。

2.2 dissector解码

现在我们已经启动并运行了基本的dissector,让我们用它做点什么。最简单的事情就是标记有效负载。

我们要做的第一件事是构建一个子树来解码我们的结果。这有助于在详细显示中保持良好的外观。

static int dissect_foo(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_){ col_set_str(pinfo-> cinfo,COL_PROTOCOL,“FOO”); / *清除信息列中的内容* / col_clear(pinfo-> cinfo,COL_INFO); proto_item * ti = proto_tree_add_item(tree,proto_foo,tvb,0,-1,ENC_NA); return tvb_captured_length(tvb); }

proto_tree_add_item为dissector添加一个子树。该子树将保存此协议的所有细节。

现在让我们进入下一步并添加一些协议解析。对于这一步,我们需要构建一些有助于解剖的表格。这需要对proto_register_foo()进行一些补充。

static int hf_foo_pdu_type = -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”, /* 这是过滤字符串。它使我们能够foo.type=1在过滤器框中输入构造。*/FT_UINT8,/* 指定此项为8位无符号整数。*/BASE_DEC,/*告诉它打印为十进制数。/NULL,0x0,NULL,HFILL} } }; / *设置协议子树数组* / static gint * ett [] = { &ett_foo }; proto_foo = proto_register_protocol(“FOO Protocol”,/ * name * / “FOO”,/ * short name * / “foo”/ * abbrev * / ); proto_register_field_array(proto_foo,hf,array_length(hf));proto_register_subtree_array(ett,array_length(ett)); }

接下来,数据包开头的一个字节数据,用于定义foo协议的数据包类型。

proto_item * ti = proto_tree_add_item(tree,proto_foo,tvb,0,-1,ENC_NA); proto_tree * foo_tree = proto_item_add_subtree(ti,ett_foo); proto_tree_add_item(foo_tree,hf_foo_pdu_type,tvb,0,1,ENC_BIG_ENDIAN);

proto_item_add_subtree()调用将一个子节点添加到协议树中,这是我们进行详细剖析的地方。该节点的扩展由ett_foo 变量控制。这会记住在数据包之间移动时是否应该扩展节点。所有后续解剖都将添加到此树中,您可以从下一个调用中看到。

proto_tree_add_item()在foo_tree中调用,这次使用hf_foo_pdu_type控制项的格式。pdu类型是一个字节的数据,从0开始。我们假设它是网络顺序(也称为大端)ENC_BIG_ENDIAN。

接下来解析所有的字节。

...static int hf_foo_flags = -1;static int hf_foo_sequenceno = -1;static int hf_foo_initialip = -1;...static intdissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_){gint offset = 0; /*引入了一个新的变量offset,以帮助跟踪我们在数据包解剖中的位置。*/...proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);proto_tree *foo_tree = proto_item_add_subtree(ti, ett_foo);proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN);offset += 1;proto_tree_add_item(foo_tree, hf_foo_flags, tvb, offset, 1, ENC_BIG_ENDIAN);offset += 1;proto_tree_add_item(foo_tree, hf_foo_sequenceno, tvb, offset, 2, ENC_BIG_ENDIAN);offset += 2;proto_tree_add_item(foo_tree, hf_foo_initialip, tvb, offset, 4, ENC_BIG_ENDIAN);offset += 4;...return tvb_captured_length(tvb);}voidproto_register_foo(void) {......{ &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 }},......}

2.3 改善dissector

我们当然可以通过一些额外的数据来改进协议的显示。第一步是添加一些文本标签。让我们从标记数据包类型开始。

/* 将名称添加到协议*/static const value_string packettypenames [] = { {1,“Initialise”},{2,“Terminate”},{3,“Data”},{0,NULL} };{&hf_foo_pdu_type,{“FOO PDU Type”,“foo.type”,FT_UINT8,BASE_DEC,VALS(packettypenames),0x0,NULL,HFILL} }/*将标志添加到协议*/#define FOO_START_FLAG 0x01 #define FOO_END_FLAG 0x02 #define FOO_PRIORITY_FLAG 0x04 static int hf_foo_startflag = -1; static int hf_foo_endflag = -1; static int hf_foo_priorityflag = -1; static int dissect_foo(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_){ ... ... static const int * bits [] = { &hf_foo_startflag,&hf_foo_endflag,&hf_foo_priorityflag }; proto_tree_add_bitmask(foo_tree,tvb,offset,hf_foo_flags,ett_foo,bits,ENC_BIG_ENDIAN); offset + = 1; ... ...return tvb_captured_length(tvb); } void proto_register_foo(void){ ... ... {&hf_foo_startflag,{“FOO PDU Start Flags”,“foo.flags.start”,FT_BOOLEAN,8,NULL,FOO_START_FLAG,NULL,HFILL} },{&hf_foo_endflag,{ “FOO PDU End Flags”,“foo.flags.end”,FT_BOOLEAN,8,NULL,FOO_END_FLAG,NULL,HFILL} },{&hf_foo_priorityflag,{“FOO PDU Priority Flags”,“foo.flags.priority”,NULL,FOO_PRIORITY_FLAG,NULL,HFILL} } ... ... } ...

现在看起来已经开始看起来相当全面,但我们还可以采取其他一些措施让事情看起来更漂亮。

首先,我们可以设置非详细视图的INFO列,以显示它是什么类型的PDU - 这在查看协议跟踪时非常有用。其次,我们还可以在解剖窗口中显示此信息。

static intdissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_){gint offset = 0;guint8 packet_type = tvb_get_guint8(tvb, 0);col_set_str(pinfo->cinfo, COL_PROTOCOL, "FOO");/* Clear out stuff in the info column */col_clear(pinfo->cinfo,COL_INFO);col_add_fstr(pinfo->cinfo, COL_INFO, "Type %s",val_to_str(packet_type, packettypenames, "Unknown (0x%02x)"));proto_item *ti = proto_tree_add_item(tree, proto_foo, tvb, 0, -1, ENC_NA);proto_item_append_text(ti, ", Type %s",val_to_str(packet_type, packettypenames, "Unknown (0x%02x)"));proto_tree *foo_tree = proto_item_add_subtree(ti, ett_foo);proto_tree_add_item(foo_tree, hf_foo_pdu_type, tvb, offset, 1, ENC_BIG_ENDIAN);offset += 1;return tvb_captured_length(tvb);}

3. 数据包重组

有些协议一个大包有时需要分割传送。在这种情况下,解剖不能正确进行,直到你有了所有的数据。第一个包没有足够的数据,随后的包没有预期的格式。要解剖这些包,您需要等到所有包都到达后再开始解剖。

3.1 重组udp包

作为一个例子,让我们检查一个协议,它是在UDP之上分层的,它分裂了自己的数据流。如果一个数据包大于某个给定大小,它将被分割成块,并在协议中以某种方式发出信号。

为了处理这些流,我们需要一些东西来触发。我们需要知道这个包是多包序列的一部分。我们需要知道序列中有多少包。我们还需要知道什么时候有所有的包。

对于本例,我们假设有一个简单的协议内信令机制来提供详细信息。一种标志字节,表示多包序列和最后一个包的存在,后面跟着该序列的ID和包序列号。

msg_pkt ::= SEQUENCE {.....flags ::= SEQUENCE {fragment BOOLEAN,last_fragment BOOLEAN,.....}msg_id INTEGER(0..65535),frag_id INTEGER(0..65535),.....}#include <epan/reassemble.h>...save_fragmented = pinfo->fragmented;flags = tvb_get_guint8(tvb, offset); offset++;if (flags & FL_FRAGMENT) { /* fragmented */tvbuff_t* new_tvb = NULL;fragment_data *frag_msg = NULL;guint16 msg_seqid = tvb_get_ntohs(tvb, offset); offset += 2;guint16 msg_num = tvb_get_ntohs(tvb, offset); offset += 2;pinfo->fragmented = TRUE;frag_msg = fragment_add_seq_check(msg_reassembly_table,/*用于记账*/tvb, /*正在解析的tvb缓冲区*/offset, /*数据包开始的偏移量*/pinfo, /*The provided packet info*/msg_seqid, /*The sequence number of the fragment stream*/NULL, /* ID for fragments belonging together */msg_num, /*the packet number within the sequence */tvb_captured_length_remaining(tvb, offset), /* fragment length - to the end */flags & FL_FRAG_LAST); /* More fragments? */

我们首先保存这个数据包的碎片状态,以便稍后恢复它。接下来是一些特定于协议的内容,从流中挖掘片段数据(如果有的话)。在确定了它的存在之后,我们让函数fragment_add_seq_check()来完成它的工作。

new_tvb = process_reassembled_data(tvb, offset, pinfo,"Reassembled Message", frag_msg, &msg_frag_items,NULL, msg_tree);if (frag_msg) { /* Reassembled */col_append_str(pinfo->cinfo, COL_INFO," (Message Reassembled)");} else { /* Not last packet of reassembled Short Message */col_append_fstr(pinfo->cinfo, COL_INFO," (Message fragment %u)", msg_num);}if (new_tvb) { /* take it all */next_tvb = new_tvb;} else { /* make a new subset */next_tvb = tvb_new_subset_remaining(tvb, offset);}}else { /* Not fragmented */next_tvb = tvb_new_subset_remaining(tvb, offset);}.....pinfo->fragmented = save_fragmented;

将片段数据传递给重组处理程序后,我们现在可以检查是否有整个消息。如果有足够的信息,该例程将返回新重组的数据缓冲区。

之后,我们向显示器添加一些信息性消息,以显示这是序列的一部分。然后可以继续对缓冲区和解剖进行一些操作。通常你可能不会进一步解剖,除非碎片已经重新组装,因为没有太多东西可以找到。有时,如果您愿意,可以对序列中的第一个数据包进行部分解码。

/*初始化*/static reassembly_table reassembly_table; static void proto_register_msg(void){ reassembly_table_register(&msg_reassemble_table,&addresses_ports_reassembly_table_functions); }

首先,reassembly_table,在协议初始化例程中声明并初始化结构。第二个参数指定应该用于标识片段的函数。我们将使用 addresses_ports_reassembly_table_functions以便通过给定的序列号(msg_seqid),来自数据包的源和目标地址以及端口来识别片段。

接下来,fragment_items分配一个结构并用一系列ett项,hf数据项和字符串标记填充。ett和hf值应该包含在相关表中,就像协议可能使用的所有其他变量一样。hf变量需要放在结构中,如下所示。当然,名称可能需要调整。

/*数据结构*/static int hf_msg_fragments = -1;static int hf_msg_fragment = -1;static int hf_msg_fragment_overlap = -1;static int hf_msg_fragment_overlap_conflicts = -1;static int hf_msg_fragment_multiple_tails = -1;static int hf_msg_fragment_too_long_fragment = -1;static int hf_msg_fragment_error = -1;static int hf_msg_fragment_count = -1;static int hf_msg_reassembled_in = -1;static int hf_msg_reassembled_length = -1;...static gint ett_msg_fragment = -1;static gint ett_msg_fragments = -1;...static const fragment_items msg_frag_items = {/* Fragment subtrees */&ett_msg_fragment,&ett_msg_fragments,/* Fragment fields */&hf_msg_fragments,&hf_msg_fragment,&hf_msg_fragment_overlap,&hf_msg_fragment_overlap_conflicts,&hf_msg_fragment_multiple_tails,&hf_msg_fragment_too_long_fragment,&hf_msg_fragment_error,&hf_msg_fragment_count,/* Reassembled in field */&hf_msg_reassembled_in,/* Reassembled length field */&hf_msg_reassembled_length,/* Tag */"Message fragments"};...static hf_register_info hf[] ={...{&hf_msg_fragments,{"Message fragments", "msg.fragments",FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } },{&hf_msg_fragment,{"Message fragment", "msg.fragment",FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },{&hf_msg_fragment_overlap,{"Message fragment overlap", "msg.fragment.overlap",FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },{&hf_msg_fragment_overlap_conflicts,{"Message fragment overlapping with conflicting data","msg.fragment.overlap.conflicts",FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },{&hf_msg_fragment_multiple_tails,{"Message has multiple tail fragments","msg.fragment.multiple_tails",FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },{&hf_msg_fragment_too_long_fragment,{"Message fragment too long", "msg.fragment.too_long_fragment",FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } },{&hf_msg_fragment_error,{"Message defragmentation error", "msg.fragment.error",FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },{&hf_msg_fragment_count,{"Message fragment count", "msg.fragment.count",FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },{&hf_msg_reassembled_in,{"Reassembled in", "msg.reassembled.in",FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } },{&hf_msg_reassembled_length,{"Reassembled length", "msg.reassembled.length",FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } },...static gint *ett[] ={...&ett_msg_fragment,&ett_msg_fragments...

这些hf变量在重组例程内部使用,以创建有用的链接,并将数据添加到解剖。它产生从一个数据包到另一个数据包的链接,例如具有到完全重组数据包的链接的部分数据包。同样地,存在来自重组的各个分组的后向指针。其他变量用于标记错误。

3.2 重组TCP分段

#include "config.h"#include <epan/packet.h>#include <epan/prefs.h>#include "packet-tcp.h"...#define FRAME_HEADER_LEN 8/* This method dissects fully reassembled messages */static intdissect_foo_message(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_){/* TODO: implement your dissecting code */return tvb_captured_length(tvb);}/* determine PDU length of protocol foo */static guintget_foo_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_){/* TODO: change this to your needs */return (guint)tvb_get_ntohl(tvb, offset+4); /* e.g. length is at offset 4 */}/* The main dissecting routine */static intdissect_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data){tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,get_foo_message_len, dissect_foo_message, data);return tvb_captured_length(tvb);}...

只需调用tcp_dissect_pdus(),将消息解析代码移动到另一个函数中。每当重新组装消息时,都会调用此函数。

参数tvb,pinfo,tree和data只是移交给 tcp_dissect_pdus()。

第4个参数是一个标志,用于指示是否应重新组装数据。这也可以根据解剖器偏好来设置。

参数5表示至少有多少数据可用于确定foo消息的长度。

参数6是指向返回此长度的方法的函数指针。当至少前一个参数中给出的字节数可用时,它会被调用。

参数7是指向真实消息解析器的函数指针。

参数8是从父解剖器传入的数据。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。