【wireshark】协议解析

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"

原文地址:https://www.cnblogs.com/zzqcn/p/4823243.html