wireshark协议解析器 源码分析 封装调用

源码分析

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函数中,会找到协议号对应的解析器句柄,并使用它解析其余数据。

 

涉及到的函数与数据结构

首先我们得知道需要调用哪些函数。通过调试自己编译的wireshark,我发现如果要实现简单的协议解析,主要只需要以下几个函数(源码位置均相对于wireshark源码主目录而言):

函 数功 能      源码位置
epan_init  初始化协议解析库epan/epan.h
epan_cleanup清理协议解析库同上
epan_dissect_new创建协议解析数据结构edt同上
epan_dissect_run执行协议解析同上
epan_dissect_free销毁协议解析数据结构edt同上
init_dissection初始化数据包级协议解析epan/packet.h
cleanup_dissection 清理数据包级协议解析 同上

除此之外,还需要导出一些辅助的函数,如register_all_protocols, register_all_protocol_handoffs,proto_item_fill_label等等。

不仅如此,还需要熟悉协议解析过程所涉及到的一些数据结构,主要有:

数据结构功 能      源码位置
epan_dissect_t  协议解析信息,保存协议数据及协议解析树epan/epan.h; epan/epan_dissect.h
field_info协议字段信息epan/proto.h
header_field_info协议首部字段信息同上
proto_tree/proto_node协议树同上
frame_data单帧(数据包)信息epan/frame_data.h
wtap_pseudo_headerwtap伪首部,主要是链路层协议信息wiretap/wtap.h

以上就是一些主要的函数及数据结构。实际的协议解析过程中,可能会涉及到更多的函数及数据结构,这里就不多说了,具体可以查看wireshark源码。如果对于某些函数,或者解析过程有不了解的,也可以自己编译wireshark,然后调试它。

 

代码实现

知道原理及所需的函数后,就可以编码实现了。环境的配置等基础知识,本系列前一篇已经讲过了。

我继续用Win32 Console工程来写这个示例。我这个示例代码分为两个部分:

一、wireshark导出函数及简单的封装;
二、实际解析代码

第一个部分分成wireshark.h和wireshark.cpp两个文件。第二部分为dissector.cpp,其中也包括了main函数。

wireshark导出函数及简单封装

没什么好说的,主要就是所需函数的声明,以及动态调用代码。

wireshark.h:

/*
 * wireshark协议解析相关的导出函数声明,以及简单函数封装
 *
 * Copyright (c) 2013 赵子清, All rights reserved.
 *
 */


#ifndef  __WIRESHARK_H__
#define  __WIRESHARK_H__

// see \wireshark-1.8.4\CMakeLists.txt, #481
#define WS_VAR_IMPORT       __declspec(dllimport) extern
// see \wireshark-1.8.4\CMakeLists.txt, #482
#define WS_MSVC_NORETURN    __declspec(noreturn)

#ifdef  TRY
#undef  TRY 
#endif
#ifdef  CATCH
#undef  CATCH
#endif
#ifdef  CATCH_ALL
#undef  CATCH_ALL 
#endif
#ifdef  THROW
#undef  THROW
#endif


// wireshark源码头文件
#include "epan/epan.h"
#include "epan/epan_dissect.h"
#include "epan/proto.h"
#include "epan/packet_info.h"
#include "epan/frame_data.h"
#include "epan/packet.h"
#include <Windows.h>


#define CHECK(x) if(!(x)) return FALSE;


/* \register.h -------------------------------------------------------------------------*/
typedef void (*register_cb) (register_action_e action, const char *message, gpointer client_data);
typedef void (*f_register_all_protocols) (register_cb cb, gpointer client_data);
typedef void (*f_register_all_protocol_handoffs) (register_cb cb, gpointer client_data);
typedef void (*f_register_all_tap_listeners)(void);
/*--------------------------------------------------------------------------------------*/

/* \epan\packet.h ----------------------------------------------------------------------*/
typedef void (*f_init_dissection) (void);
typedef void (*f_cleanup_dissection) (void);
/*--------------------------------------------------------------------------------------*/

/* \epan\epan.h -------------------------------------------------------------------------*/
typedef void (*f_epan_init) (void (*register_all_protocols)(register_cb cb, gpointer client_data),
                            void (*register_all_handoffs)(register_cb cb, gpointer client_data),
                            register_cb cb,
                            void *client_data,
                            void (*report_failure)(const char *, va_list),
                            void (*report_open_failure)(const char *, int, gboolean),
                            void (*report_read_failure)(const char *, int));
typedef void (*f_epan_cleanup) (void);
typedef epan_dissect_t* (*f_epan_dissect_new) (gboolean create_proto_tree, 
                                            gboolean proto_tree_visible);
typedef void (*f_epan_dissect_run) (epan_dissect_t *edt, void* pseudo_header,
                            const guint8* data, frame_data *fd, column_info *cinfo);
typedef void (*f_epan_dissect_free) (epan_dissect_t* edt);
typedef void (*f_epan_dissect_fill_in_columns) (epan_dissect_t *edt);
/*--------------------------------------------------------------------------------------*/

/* \epan\proto.h -----------------------------------------------------------------------*/
typedef void (*f_proto_item_fill_label) (field_info *fi, gchar *label_str);
/*--------------------------------------------------------------------------------------*/

extern f_epan_init                            ws_epan_init;
extern f_epan_cleanup                        ws_epan_cleanup;
extern f_register_all_protocols                ws_register_all_protocols;
extern f_register_all_protocol_handoffs        ws_register_all_protocol_handoffs;
extern f_init_dissection                    ws_init_dissection;
extern f_cleanup_dissection                    ws_cleanup_dissection;
extern f_epan_dissect_new                    ws_epan_dissect_new;
extern f_epan_dissect_run                    ws_epan_dissect_run;
extern f_epan_dissect_free                    ws_epan_dissect_free;
extern f_proto_item_fill_label                ws_proto_item_fill_label;


HINSTANCE  LoadWiresharkDLL(const TCHAR* szDLLPath);
BOOL  FreeWiresharkDLL(HMODULE hModule);
BOOL  GetWiresharkFunctions(HMODULE hDLL);

#endif /* WIRESHARK_H_ */


wireshark.cpp:

/*
* wireshark协议解析相关的导出函数声明,以及简单的函数封装
*
* Copyright (c) 2013 赵子清, All rights reserved.
*
*/


#include "wireshark.h"

f_epan_init                                ws_epan_init;
f_epan_cleanup                            ws_epan_cleanup;
f_register_all_protocols                ws_register_all_protocols;
f_register_all_protocol_handoffs        ws_register_all_protocol_handoffs;
f_init_dissection                        ws_init_dissection;
f_cleanup_dissection                    ws_cleanup_dissection;
f_epan_dissect_new                        ws_epan_dissect_new;
f_epan_dissect_run                        ws_epan_dissect_run;
f_epan_dissect_free                        ws_epan_dissect_free;
f_proto_item_fill_label                    ws_proto_item_fill_label;

HINSTANCE  LoadWiresharkDLL(const TCHAR* szDLLPath)
{
    return ::LoadLibrary(szDLLPath);
}

BOOL  FreeWiresharkDLL(HMODULE hModule)
{
    return ::FreeLibrary(hModule);
}

BOOL  GetWiresharkFunctions(HMODULE hDLL)
{
    CHECK(ws_epan_init = (f_epan_init)::GetProcAddress(hDLL, "epan_init"));
    CHECK(ws_epan_cleanup = (f_epan_cleanup)::GetProcAddress(hDLL, "epan_cleanup"));
    CHECK(ws_register_all_protocols = (f_register_all_protocols)
                    ::GetProcAddress(hDLL, "register_all_protocols"));
    CHECK(ws_register_all_protocol_handoffs = (f_register_all_protocol_handoffs)
                    ::GetProcAddress(hDLL, "register_all_protocol_handoffs"));
    CHECK(ws_init_dissection = (f_init_dissection)::GetProcAddress(hDLL, "init_dissection"));
    CHECK(ws_cleanup_dissection = (f_cleanup_dissection)::GetProcAddress(hDLL, "cleanup_dissection"));
    CHECK(ws_epan_dissect_new = (f_epan_dissect_new)::GetProcAddress(hDLL, "epan_dissect_new"));
    CHECK(ws_epan_dissect_run = (f_epan_dissect_run)::GetProcAddress(hDLL, "epan_dissect_run"));
    CHECK(ws_epan_dissect_free = (f_epan_dissect_free)::GetProcAddress(hDLL, "epan_dissect_free"));
    CHECK(ws_proto_item_fill_label = (f_proto_item_fill_label)::GetProcAddress(hDLL, "proto_item_fill_label"));

    return TRUE;
}

 

实际解析代码

以下代码调用wireshark协议解析库,解析了一段数据。这段数据,如注释里所说,是我上网时随便用wireshark抓的。解析完成后,把结果输出到控制台。

主要的流程是:

动态调用所需的wireshark函数 -> 初始化协议解析库 -> 解析数据 -> 将解析结果按协议层次输出到控制台 -> 清理协议解析库。

解析的结果主要是一个树形结构,因为我写了一个递归函数print_tree来遍历此树。

/*
 * 调用wireshark解析库完成数据解析
 *
 * Copyright (c) 2013 赵子清, All rights reserved.
 *
 */

#include "wireshark.h"
#include <stdio.h>
#include <tchar.h>

#define DATA_LEN 73
#define WIRESHARK_DLL_PATH  _T("E:\\dev\\wireshark-1.8.4\\release\\libwireshark.dll")

// 帧数据, 不包括PCAP文件头和帧头
// 数据为ethernet - ipv4 - udp - DNS, 上网时随便捕获的.
const guchar data[DATA_LEN] = 
{
0x7E, 0x6D, 0x20, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00,
0x00, 0x3B, 0x5F, 0x15, 0x00, 0x00, 0x40, 0x11, 0xF1, 0x51, 0x73, 0xAB, 0x4F, 0x08, 0xDB, 0x8D,
0x8C, 0x0A, 0x9B, 0x90, 0x00, 0x35, 0x00, 0x27, 0xEF, 0x4D, 0x43, 0x07, 0x01, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x74, 0x04, 0x73, 0x69, 0x6E, 0x61, 0x03, 0x63, 0x6F,
0x6D, 0x02, 0x63, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x01
};


void print_tree(proto_tree* tree, int level)
{
    if(tree == NULL)
        return;

    for(int i=0; i<level; ++i)
        printf("    ");

    gchar field_str[ITEM_LABEL_LENGTH + 1] = {0};
    if(tree->finfo->rep == NULL)
        ws_proto_item_fill_label(tree->finfo, field_str);
    else
        strcpy_s(field_str, tree->finfo->rep->representation);

    if(!PROTO_ITEM_IS_HIDDEN(tree))
        printf("%s\n", field_str);   

    print_tree(tree->first_child, level+1);
    print_tree(tree->next, level);
}

void try_dissect()
{
    frame_data  *fdata;
    epan_dissect_t  *edt;
    union wtap_pseudo_header  pseudo_header;
    pseudo_header.eth.fcs_len = -1;

    fdata = (frame_data*)g_new(frame_data, 1);

    memset(fdata, 0, sizeof(frame_data));
    fdata->pfd  = NULL;
    fdata->num = 1;
    fdata->interface_id = 0;
    fdata->pkt_len  = DATA_LEN;
    fdata->cap_len  = DATA_LEN;
    fdata->cum_bytes = 0;
    fdata->file_off = 0;
    fdata->subnum = 0;
    fdata->lnk_t = WTAP_ENCAP_ETHERNET;
    fdata->flags.encoding = PACKET_CHAR_ENC_CHAR_ASCII;
    fdata->flags.visited = 0;
    fdata->flags.marked = 0;
    fdata->flags.ref_time = 0;
    fdata->color_filter = NULL;
    fdata->abs_ts.secs = 0;
    fdata->abs_ts.nsecs = 0;
    fdata->opt_comment = NULL;
    
    edt = ws_epan_dissect_new(TRUE, TRUE);
    ws_epan_dissect_run(edt, &pseudo_header, data, fdata, NULL);
    print_tree(edt->tree->first_child, 0);
    ws_epan_dissect_free(edt);
    g_free(fdata);
}


int main(int argc, char** argv)
{
    HINSTANCE hDLL = NULL;
    BOOL ret = FALSE;
    void* addr = NULL;

    hDLL = LoadWiresharkDLL(WIRESHARK_DLL_PATH);
    if(hDLL)
    {
        ret = GetWiresharkFunctions(hDLL);
        if(ret)
        {
            ws_epan_init(ws_register_all_protocols, ws_register_all_protocol_handoffs, 
                            NULL, NULL, NULL, NULL, NULL);
            ws_init_dissection();
            try_dissect();
            ws_cleanup_dissection();
            ws_epan_cleanup();
        }
        else
            fprintf(stderr, "某些导出函数获取失败!\n");
        FreeWiresharkDLL(hDLL);
    }
    else
        fprintf(stderr, "无法加载DLL!\n");
    

    system("PAUSE");
    return 0;
}

 

 解析结果

 编译运行以上代码,控制台输出的解析结果如下:

Frame 1: 73 bytes on wire (584 bits), 73 bytes captured (584 bits)
    WTAP_ENCAP: 1
    Frame Number: 1
    Frame Length: 73 bytes (584 bits)
    Capture Length: 73 bytes (584 bits)
    Frame is marked: False
    Frame is ignored: False
    Protocols in frame: eth:ip:udp:dns
Ethernet II, Src: 01:00:01:00:00:00 (01:00:01:00:00:00), Dst: 7e:6d:20:00:01:00 (7e:6d:20:00:01:00)
    Destination: 7e:6d:20:00:01:00 (7e:6d:20:00:01:00)
        Address: 7e:6d:20:00:01:00 (7e:6d:20:00:01:00)
        .... ..1. .... .... .... .... = LG bit: Locally administered address (this is NOT the factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Source: 01:00:01:00:00:00 (01:00:01:00:00:00)
        Expert Info (Warn/Protocol): Source MAC must not be a group address: IEEE 802.3-2002, Section 3.2.3(b)
            Message: Source MAC must not be a group address: IEEE 802.3-2002, Section 3.2.3(b)
            Severity level: Warn
            Group: Protocol
        Address: 01:00:01:00:00:00 (01:00:01:00:00:00)
        .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
        .... ...1 .... .... .... .... = IG bit: Group address (multicast/broadcast)
    Type: IP (0x0800)
Internet Protocol Version 4, Src: 115.171.79.8 (115.171.79.8), Dst: 219.141.140.10 (219.141.140.10)
    Version: 4
    Header length: 20 bytes
    Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
        0000 00.. = Differentiated Services Codepoint: Default (0x00)
        .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)
    Total Length: 59
    Identification: 0x5f15 (24341)
    Flags: 0x00
        0... .... = Reserved bit: Not set
        .0.. .... = Don't fragment: Not set
        ..0. .... = More fragments: Not set
    Fragment offset: 0
    Time to live: 64
    Protocol: UDP (17)
    Header checksum: 0xf151 [correct]
        Good: True
        Bad: False
    Source: 115.171.79.8 (115.171.79.8)
                Destination: 219.141.140.10 (219.141.140.10)
                Source GeoIP: Unknown
    Destination GeoIP: Unknown
User Datagram Protocol, Src Port: 39824 (39824), Dst Port: 53 (53)
    Source port: 39824 (39824)
    Destination port: 53 (53)
            Length: 39
        Checksum: 0xef4d [validation disabled]
        Good Checksum: False
        Bad Checksum: False
Domain Name System (query)
    Transaction ID: 0x4307
    Flags: 0x0100 Standard query
        0... .... .... .... = Response: Message is a query
        .000 0... .... .... = Opcode: Standard query (0)
        .... ..0. .... .... = Truncated: Message is not truncated
        .... ...1 .... .... = Recursion desired: Do query recursively
        .... .... .0.. .... = Z: reserved (0)
        .... .... ...0 .... = Non-authenticated data: Unacceptable
    Questions: 1
    Answer RRs: 0
    Authority RRs: 0
    Additional RRs: 0
    Queries
        t.sina.com.cn: type A, class IN
            Name: t.sina.com.cn
            Type: A (Host address)
            Class: IN (0x0001)

我们当然也可以在自己的GUI界面上,使用TreeCtrl来把解析结果显示给用户,就像下面这样(我写的工具的截图):

 

 

 

  • 6
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
wireshark源码分析问题这几天在看wireshark(ethereal)源代码。看源代码的主要兴趣点是它的分析模块(dissect)。分析之后他的数据存在哪儿,怎么打印的(-V参数)。我想把分析后的数据,提取出来,存在自己定义的数据结构里面,或者按我自己的格式写入文本中。 看了几天,对一些数据结构,似懂非懂,一些流程也是似懂非懂。可能由于经验不足的原因,搞来搞去就在几个函数,结构体里面打转。好几次以为找到切入点,发现又回来原来的起点。 这两天看晕了。有点打击,水平太差劲了。。呵呵。先这边问问,看看有没有熟悉的朋友。指点一下。先谢谢了。 这样问问题可能太细了。感觉也不大合适。 1. 我应该如何来看代码?如何找到突破点? 2. 有wireshark有了解的朋友,说说你们关于源码剖析的体会。 3. 说什么都可以,朋友觉得对我有用,有启发就好。千万别 “我顶,UP啊”。呵呵:emn23:我觉得重要的是看 pcap库 本帖最后由 peidright 于 2010-04-02 16:36 编辑 楼上说得对!。 看源代码之前,问下你自己,看代码的目的是什么? 对于 wireshark 来说,你是想学他写界面? 还是抓包? 还是业务逻辑? 界面的话,wireshark 还行 抓包的话,应该看pcap库 业务逻辑的话。不应该看wireshark,看tcpdump.看下啊,:em03:看看这个也许对你有帮助 添加一个基础的RDP解析器 下面我们将循序渐进地设计一个基础的RDP解析器。它依次包含如下构成要素: 包类型字段(占用8比特位,可能的值为:1,初始;2,终结;3,数据); 标志集字段(占用8比特位:0x01,开始包;0x02,结束包;0x04先包); 序列号字段(占用16比特位); 1. 创建解析器 首先您需要选择解析器的类型:内置型(包含在主程序中)或插件型。 插件是容易编写的,先做一个插件型解析器吧。 例1. 解析器初始设定. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include void proto_register_rdp(); void proto_reg_handoff_rdp(); static void dissect_rdp(tvbuff_t *tvb,packet_info *pinfo,proto_tree *tree); static int proto_rdp=-1; static dissector_handle_t rdp_handle; static gint ett_rdp = -1; define TCP_PORT_RDP 3389 void proto_register_rdp(void) { proto_rdp=proto_register_protocol( "RDP Protocol", "RDP", "rdp"); } 现在来逐一分析这段代码。首先我们有一些常规的包含文件,最好依惯例在文件开始包含进来。随后是一些函数的前置声明,我们稍后定义它们。 接下来我们定义了一个整型变量"proto_rdp"用于记录我们的协议注册信息。它被初始化为"-1",当解析器注册到主程序中后,其值便会得到更新。这样做可保证我们方便地判断是否已经做了初始工作。将所有不打算对外输出的全局变量和函数声明为"static"是一个良好的习惯,因为这可以保证命名空间不被污染。通常这是容易做到的,除非您的解析器非常庞大以致跨越多个文件。 之后的模块变量"TCP_PORT_RDP"则包含了协议使用的TCP端口号,我们会对通过该端口的数据流进行解析。 solaris10下proc编译问题 >紧随其后的是解析器句柄"rdp_handle",我们稍后对它进行初始化。 至此我们已经拥有了和主程序交互的基本元素,接下来最好再把那些预声明的函数定义一下,就从注册函数"proto_register_rdp"开始吧。 首先调用函数"proto_register_protocol"注册协议。我们能够给协议起3个名字以适用不同的地方。全名和短名用在诸如"首选项(Preferences)"和"已激活协议(Enabled protocols)"对话框以及记录中已生成的域名列表内。缩略名则用于过滤器。 下面我们需要一个切换函数。 例2. 解析器切换. void proto_reg_handoff_rdp(void) { static gboolean initialized=FALSE; if(!initialized) { rdp_handle = create_dissector_handle(dissect_rdp, proto_rdp); dissector_add("tcp.port", TCP_PORT_RDP, rdp_handle); initialized=TRUE; } } 这段代码做了什么呢?如果解析器尚未初始化,则对它进行初始化。首先创建解析器。这时注册了了函数"dissect_rdp"用于完成实际的解析工作。之后将该解析器与TCP端口号相关联,以使主程序收到该端口的UDP数据流时通知该解析器。 至此我们终于可以写一些解析代码了。不过目前我们仅写点儿基本功能占个位置。 例3.解析 static void dissect_rdp(tvbuff_t *tvb,packet_info *pinfo,proto_tree *tree) { if(check_col(pinfo->cinfo, COL_PROTOCOL)) { col_set_str(pinfo->cinfo, COL_PROTOCOL, "RDP"); } if(check_col(pinfo->cinfo,COL_INFO)) { col_clear(pinfo->cinfo,COL_INFO); } } 该函数用于解析传递给它的数据包。包数据由"tvb"参数指向的特殊缓冲区保管。现在我们已深入到协议的细节,对它们您肯定是了若指掌。包信息结构参数"pinfo"包含了协议的基本数据,以供我们更新。参数"tree"则指明了详细解析发生的地方。 这里我们仅做了保证通过的少量工作。前两行检查UI中"协议(Protocol)"列是否已显示。如果该列已存在,就在这儿显示我们的协议名称。这样人们就知道它被识别出来了。另外,如果"信息(INFO)"列已显示,我们就将它的内容清除。 至此我们已经准备好一个可以编译和安装的基本解析器。不过它目前只能识别和标示协议。 为了编译解析器并创建插件,还需要在解析器代码文件"packet-rdp.c"所在目录下创建一些提供支持的文件: - Makefile.am - UNIX/Linux的makefile模板 - Makefile.common - 包含了插件文件的名称 - Makefile.nmake - 包含了针对Windows平台的Wireshark插件makefile - moduleinfo.h - 包含了插件版本信息 - moduleinfo.nmake - 包含了针对Windows平台的DLL版本信息 - packet-rdp.c - 这是您的解析器原代码文件 - plugin.rc.in - 包含了针对Windows平台的DLL资源模板 "Makefile.common"和"Makefile.am"文件中涉及到相关文件和解析器名称的地方一定要修改正确。"moduldeinfo.h"和"moduleinfo.nmake"文件中的版本信息也需要正确填充。一切准备妥善后就可以将解析器编译为DLL或共享库文件了(使用nmake工具)。在wireshark文件夹下的"plugins"文件夹中,建立"rdp"文件夹。将修改过的Makefile.common,Makefile.am,moduleinfo.nmake,moduldeinfo.h,Makefile.nmake及packet-rdp.c文件考到"rdp"文件夹下,然后进行编译,rdp插件自动生成完整,就可以正常工作了。 1. 解析协议细节 现在我们已经有了一个可以运用的简单解析器,让我们再为它添点儿什么吧。首先想到的应该就是标示数据包的有效信息了。解析器在这方面给我们提供了支持。 首先要做的事情是创建一个子树以容纳我们的解析结果。这会使协议的细节显示得井井有条。现在解析器在两种情况下被调用http://www.boomss.com:其一,用于获得数据包的概要信息;其二,用于获得数据包的详细信息。这两种情况可以通过树指针参数"tree"来进行区分。如果树指针为NULL,我们只需要提供概要信息;反之,我们就需要拆解协议完成细节的显示了。基于此,让我们来增强这个解析器吧。 例4 static void dissect_rdp(tvbuff_t *tvb,packet_info *pinfo,proto_tree *tree) { proto_item *ti=NULLV; if(check_col(pinfo->cinfo,COL_PROTOCOL)) { col_set_str(pinfo->cinfo,COL_PROTOCOL,"RDP"); } if(check_col(pinfo->cinfo,COL_INFO)) { col_clear(pinfo->cinfo,COL_INFO); } if(tree) { ti = proto_tree_add_item(tree, proto_rdp, tvb, offset, -1, FALSE);} } 这里我们为解析添加一个子树。它将用于保管协议的细节,仅在必要时显示这些内容。 我们还要标识被协议占据的数据区域。在我们的这种情况下,协议占据了传入数据的全部,因为我们假设协议没有封装其它内容。因此,我们用"proto_tree_add_item"函数添加新的树结点,将它添加到传入的协议树"tree"中,用协议句柄"proto_rdp"标识它,用传入的缓冲区"tvb"作为数据,并将有效数据范围的起点设为"0",长度设为"-1"(表示缓冲区内的全部数据)。至于最后的参数"FALSE",我们暂且忽略。 做了这个更改之后,在包明细面板区中应该会出现一个针对该协议的标签;选择该标签后,在包字节面板区中包的剩余内容就会高亮显示。 现在进入下一步,添加一些协议解析功能。在这一步我们需要构建一组帮助解析的表结构。这需要对"proto_register_rdp"函数做些修改。首先定义一组静态数组。 例5 定义数据结构 static hf_register_info hf[]= { { &hf;_rdp_version, { "TPKT Header:Version", "rdp.version",
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值