1. 普通解析
Wireshark启动时,所有解析器进行初始化和注册。要注册的信息包括协议名称、各个字段的信息、过滤用的关键字、要关联的下层协议与端口(handoff)等。在解析过程,每个解析器负责解析自己的协议部分, 然后把上层封装数据传递给后续协议解析器,这样就构成一个完整的协议解析链条。
解析链条的最上端是Frame解析器,它负责解析pcap帧头。后续该调用哪个解析器,是通过上层协议注册handoff信息时写在当前协议的hash表来查找的。
例如,考虑ipv4解析器有一个hash表,里面存储的信息形如下表。当它解析完ipv4首部后,就可以根据得到的协议号字段,比如6,那么它就能从此hash表中找到后续解析器tcp。
协议号 | 解析器指针 |
6 | *tcp |
17 | *udp |
…… |
Wireshark中实际的解析表有3种,分别是字符串表,整数表和启发式解析表。如下图所示:
下面以ip协议为例,说明一下它的注册过程。
相关的重要数据结构与全局变量如下。
proto.c
/* Name hashtables for fast detection of duplicate names */ static GHashTable* proto_names = NULL; static GHashTable* proto_short_names = NULL; static GHashTable* proto_filter_names = NULL; /** Register a new protocol. @param name the full name of the new protocol @param short_name abbreviated name of the new protocol @param filter_name protocol name used for a display filter string @return the new protocol handle */ int proto_register_protocol(const char *name, const char *short_name, const char *filter_name);
三个全局的哈希表分别用于保存协议名称、协议缩略名和用于过滤器的协议名。
packet.c:
struct dissector_table { GHashTable *hash_table; GSList *dissector_handles; const char *ui_name; ftenum_t type; int base; }; static GHashTable *dissector_tables = NULL; /* * List of registered dissectors. */ static GHashTable *registered_dissectors = NULL; static GHashTable *heur_dissector_lists = NULL; /* Register a dissector by name. */ dissector_handle_t register_dissector(const char *name, dissector_t dissector, const int proto); /** A protocol uses this function to register a heuristic sub-dissector list. * Call this in the parent dissectors proto_register function. * * @param name the name of this protocol * @param list the list of heuristic sub-dissectors to be registered */ void register_heur_dissector_list(const char *name, heur_dissector_list_t *list); /* a protocol uses the function to register a sub-dissector table */ dissector_table_t register_dissector_table(const char *name, const char *ui_name, const ftenum_t type, const int base);
dissector_tables可以说是“哈希表的哈希表”,它以解析表名为键(如“ip.proto”),以dissector_table结构指针为值。在dissector_table中的哈希表以无符号数的指针为键(如协议号,为指针是glib hash表API的参数要求),以解析器handle为值;heur_dissector_lists是启发式解析相关的东西,这个问题留待以后研究;registered_dissectors是解析器哈希表,它以解析器名为键(如”ip”),以解析器句柄为值。
packet.h:
typedef struct dissector_table *dissector_table_t;
packet-ip.c:
static dissector_table_t ip_dissector_table;
proto_register_ip函数中:
proto_ip = proto_register_protocol("Internet Protocol Version 4", "IPv4", "ip"); ... /* subdissector code */ ip_dissector_table = register_dissector_table("ip.proto", "IP protocol", FT_UINT8, BASE_DEC); register_heur_dissector_list("ip", &heur_subdissector_list); ... register_dissector("ip", dissect_ip, proto_ip); register_init_routine(ip_defragment_init); ip_tap = register_tap("ip");
register_dissector_table这个函数在packet.c中,在此函数内,创建了名为“ip.proto”的哈希表。解析ip协议后,会查询这个表,找出下一个解析器,并将后续数据的解析移交给它。
packet-ip.c,dissect_ip函数内:
dissector_try_uint_new(ip_dissector_table, nxt, next_tvb, pinfo, parent_tree, TRUE, iph)
packet.c:
/* Look for a given value in a given uint dissector table and, if found, call the dissector with the arguments supplied, and return TRUE, otherwise return FALSE. */ gboolean dissector_try_uint_new(dissector_table_t sub_dissectors, const guint32 uint_val, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const gboolean add_proto_name, void *data)
在dissector_try_uint_new函数中,会找到协议号对应的解析器句柄,并使用它解析其余数据。
2. 启发式(heuristic)解析
//TODO
3. 参考
Wireshark开发指南第6章"How wireshark works"