wireshark插件 - 用自己做的插件,来简单分析ICMP协议

前言

今天中午,已经可以对wiresharkUI元素进行操作了。

刚开始做试验的时候,将所有协议都当作foo协议来解析,因为数据和逻辑都是假的,整的没意思。
后来想接管wireshark ICMP的协议分析, 这样协议又简单,数据又真实,又不用自己做简单协议程序,又可以学到经典协议的细节实现。

开始研究foo插件分析ICMP协议的实现细节。

今天收获不小,会用自产的wireshark插件来分析真实的通讯协议了. wireshark插件这层窗户纸已经捅破了:)

看wireshark文档,也有开发规范, 不能这个,不能那个。如果不向官方贡献代码,就不用管wireshark的开发规范。自己用起来爽才是真的好。

wireshark官方代码复杂度还是蛮高的,不是指协议分析有多难,而是必须用wireshark的API来做事,这个对于我这样的业余选手来说,有点多余了。只要掌握怎么向wirsharkUI上添加子树,如何在子树上添加子树,如何在子树上增加文本类型的数据,这就够了。任何数据都可以转成文本类型的buf来显示。任何挎包的处理都可以自己来做。

等有时间还要再研究一下,程序退出时,在插件的那个回调上能收到通知,要不对于挎包开辟的缓冲区,就存在内存泄漏。开始看官方文档,好像看到过,现在想找,一时半会还找不出来,已经忘了在哪看到的。等以后有时间再玩。

ICMP协议下载点

Unit5_ICMP.pdf

运行效果

这里写图片描述

第1个要解决的问题

如何接管ICMP的协议,打开pcap文件后,直接到自己的foo插件,不去wireshark实现的packet-icmp.c.
看了packet-icmp.c的协议注册代码,试试和他一样会如何.

void proto_reg_handoff_foo(void)
{
    // 建立解析器
    // 指定协议使用的解析器处理函数
    dissector_handle_foo = create_dissector_handle(dissect_proc_foo, protocol_handle_foo);

    // 绑定端口
    // 指定要处理哪个端口的载荷
    // dissector_add_uint("tcp.port", foo_TCP_PORT, dissector_handle_foo);

    // 注册了ICMP的回调处理后, wireshark不分析了, 只交给插件来处理ICMP协议
    dissector_add_uint("ip.proto", IP_PROTO_ICMP, dissector_handle_foo);
}

一试居然可以,打开pcap文件后,ICMP数据直接进了foo插件. ok.

第2个要解决的问题

写到累加和校验时,发现自己算的和ICMP数据中的累加和,总是差了几个数,看起来很像,高字节相同,低字节不同。
后来参考了http://www.faqs.org/rfcs/rfc1071.html, 搞定。
原来,算累加和时,要再加上carry值,再取反才是最后的累加和.

wireshark代码走读

看了wireshark的累加和实现,精致,兼容性强。还需要花时间再扒出来,以后再弄吧。

看了wireshark分析ICMP的实现,精致,有些包,我这样粗糙的分析,是不行的。不过手头的样本(win10x64下, ping 存在的公网域名)粗略的分析起来,还是效果挺好的。ICMP协议分ICMP和ICMP64两种。我手头样本属于ICMP协议。

自产wireshark插件foo主实现

官方packet-icmp.c的实现有2000行.
我这foo插件,只写了400行,写的太糙了,差距咋这么大呢…

// @file packet-foo.c
// @brief the foo plugin parse ICMP protocol
// @ref http://www.dgtech.com/foo/sys/www/docs/html/
//      https://www.wireshark.org/docs/wsdg_html_chunked/
//      https://www.wireshark.org/docs/wsdg_html_chunked/PartDevelopment.html
//      https://www.wireshark.org/docs/wsdg_html_chunked/ChDissectAdd.html#idm1589259872

// @note how to use foo plugin
// open a pcap file or capture any packet, select a tcp frame(have payload), Decode as ... => foo => ok

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

#include "config.h"

#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/dissectors/packet-tcp.h>
#include <epan/ipproto.h>

#include "packet-foo.h"

#define PROTOCOL_FULL_NAME_FOO "ICMP protocol parse by foo plugin"
#define PROTOCOL_SHORT_NAME_FOO "foo"
#define PROTOCOL_DISPLAY_FILTER_NAME_FOO PROTOCOL_SHORT_NAME_FOO

// interface declare for plugin.c (plugin dll interface plugin_register(), plugin_reg_handoff())
void proto_register_foo(void);
void proto_reg_handoff_foo(void);

// #define foo_TCP_PORT 7000 /* Not IANA registed */

static dissector_handle_t dissector_handle_foo = NULL;
static int protocol_handle_foo = -1;

// hf means "header field name"
static int hf_foo_message = -1;
static int hf_foo_pdu_type = -1;

// 要注册的字段信息数组, use use proto_register_field_array to register
static hf_register_info hf[] = {
    { &hf_foo_message,
        {
            "FOO message", // field name
            "foo.msg", // field short name
            FT_STRING, // field type, see ftypes.h
            BASE_NONE, // data base type, see proto.h field_display_e
            NULL, // value_string
            0x0, // bitmask
            NULL, // Brief description of field
            HFILL // info fill by proto routines
        }
    },

    { &hf_foo_pdu_type,
        {
            "FOO PDU Type", // field name
            "foo.type", // field short name
            FT_UINT8, // field type, see ftypes.h
            BASE_DEC, // data base type, see proto.h field_display_e
            NULL, // value_string
            0x0, // bitmask
            NULL, // Brief description of field
            HFILL // info fill by proto routines
        }
    }
};

static gint ett_foo = -1;
static gint ett_foo_subtree_1 = -1;

// ett means "protocol subtree array"
static gint *ett[] = {
    &ett_foo,
    &ett_foo_subtree_1
};

static int dissect_proc_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_);

void proto_register_foo(void)
{
    // first entry proto_register_foo
    // then entry proto_reg_handoff_foo

    // 注册协议
    // 执行了proto_register_protocol, 就这一句, 在显示过滤器中输入foo, 就显示绿色
    // 说明foo协议已经注册
    // 参数1是协议的完整名称
    // 参数2是协议的短名称, 在Decode as对话框中的协议名称列表中可以看到
    // 参数3是显示过滤器中的协议名称
    protocol_handle_foo = proto_register_protocol(
        PROTOCOL_FULL_NAME_FOO, 
        PROTOCOL_SHORT_NAME_FOO, 
        PROTOCOL_DISPLAY_FILTER_NAME_FOO);

    proto_register_field_array(protocol_handle_foo, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
}

void proto_reg_handoff_foo(void)
{
    // 建立解析器
    // 指定协议使用的解析器处理函数
    dissector_handle_foo = create_dissector_handle(dissect_proc_foo, protocol_handle_foo);

    // 绑定端口
    // 指定要处理哪个端口的载荷
    // dissector_add_uint("tcp.port", foo_TCP_PORT, dissector_handle_foo);

    // 注册了ICMP的回调处理后, wireshark不分析了, 只交给插件来处理ICMP协议
    dissector_add_uint("ip.proto", IP_PROTO_ICMP, dissector_handle_foo);
}

// wireshark工程应该是已经1字节对齐
typedef struct _tag_icmp_code {
    char type;
    char code;
}TAG_ICMP_CODE;

typedef struct _tag_icmp_code_desc {
    const char* desc;
    TAG_ICMP_CODE code;
}TAG_ICMP_CODE_DESC;

static TAG_ICMP_CODE_DESC icmp_code_desc_ary[] = {
    //Type Code description
    //0 0 echo reply (ping)
    {"echo reply (ping)", { 0, 0 }},

    //3 0 dest network unreachable
    {"dest network unreachable", { 3, 0 }},

    //3 1 dest host unreachable
    {"dest host unreachable", { 3, 1 }},

    //3 2 dest protocol unreachable
    { "dest protocol unreachable",{ 3, 2 } },

    //3 3 dest port unreachable
    { "dest port unreachable",{ 3, 3 } },

    //3 6 dest network unknown
    { "dest network unknown",{ 3, 6 } },

    //3 7 dest host unknown
    { "dest host unknown",{ 3, 7 } },

    //4 0 source quench (congestion control - not used)
    { "source quench (congestion control - not used)",{ 4, 0 } },

    //8 0 echo request (ping)
    { "echo request (ping)",{ 8, 0 } },

    //9 0 route advertisement
    { "route advertisement",{ 9, 0 } },

    //10 0 router discovery
    { "router discovery",{ 10, 0 } },

    //11 0 TTL expired
    { "TTL expired",{ 11, 0 } },

    //12 0 bad IP header
    { "bad IP header",{ 12, 0 } },

    {NULL, {-1, -1}},
};

// https://stackoverflow.com/questions/20247551/icmp-echo-checksum
// http://www.faqs.org/rfcs/rfc1071.html

static guint16 calc_check_sum(const guint8* data, int len)
{
    guint32 sum = 0;
    guint16 tmp = 0;
    int i = 0;

    do {
        if ((NULL == data) || (len <= 0) || (0 != (len % 8))) {
            break;
        }

        for (i = 0; i < len; i += 2) {
            tmp = ntohs(*(guint16*)(data + i)); // BE data
            if (0 == i) {
                sum = tmp;
                continue;
            } else if (2 == i) {
                // this is check sum, need skip it
                continue;
            }
            else {
                sum += tmp;
            }
        }

        // add carry bit !
        while (sum >> 16) {
            sum = (sum & 0xffff) + (sum >> 16);
        };

        sum = ~sum;
    } while (0);

    return (guint16)sum;
}

static int dissect_proc_foo(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, void *data _U_)
{
    bool b_pasre_ok = false;
    TAG_ICMP_CODE icmp_code;

    char sz_buf[4096] = { '\0' };
    proto_item* item = NULL;
    proto_item* item_tmp = NULL;
    proto_tree* sub_tree_foo = NULL;
    proto_tree* sub_tree_foo_sub1 = NULL;

    // guint reported_length = tvb_reported_length(tvb);
    guint captured_length = tvb_captured_length(tvb);
    guint data_offset = 0; // 要处理的数据开始偏移
    guint data_len_left = captured_length; // 要处理的数据长度
    const guint8* data_left = NULL; // 要处理的数据
    const guint8* data_org = NULL; // 要处理的原始数据
    int i = 0;
    bool was_find = false;
    guint16 check_sum = 0;
    guint16 check_sum_by_calc = 0;
    guint16 icmp_id = 0;
    guint16 sequence_number = 0;

    // set col.5 = "Protocol" 's content to 'foo'
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "ICMP");

    // set col.7 = "Info" 's content to empty, else will display by tcp info
    col_clear(pinfo->cinfo, COL_INFO);

    item = proto_tree_add_item(tree, protocol_handle_foo, tvb, 0, -1, ENC_NA);

    do {
        // 在节点下增加子树, 此时子树还没有显示出来
        sub_tree_foo = proto_item_add_subtree(item, ett_foo);
        if (NULL == sub_tree_foo) {
            break;
        }

        // data_len_left is the ICMP data len
        // data_left is the ICMP data
        data_org = tvb_get_ptr(tvb, 0, -1);
        data_left = data_org;

        // --------------------------------------------------------------------------------
        // read ICMP type and code
        // --------------------------------------------------------------------------------
        if (data_len_left < sizeof(TAG_ICMP_CODE)) {
            break;
        }

        memcpy(&icmp_code, data_left, sizeof(TAG_ICMP_CODE));
        data_left += sizeof(TAG_ICMP_CODE);
        data_len_left -= sizeof(TAG_ICMP_CODE);

        // show ICMP.type
        item_tmp = proto_tree_add_string_format_value(sub_tree_foo, hf_foo_message, tvb, offsetof(TAG_ICMP_CODE, type), sizeof(icmp_code.type),
            NULL, // 可选
            NULL,
            -1);

        proto_item_set_text(item_tmp, "ICMP.type = %d", icmp_code.type);

        // show ICMP.code
        item_tmp = proto_tree_add_string_format_value(sub_tree_foo, hf_foo_message, tvb, offsetof(TAG_ICMP_CODE, code), sizeof(icmp_code.code),
            NULL, // 可选
            NULL,
            -1);

        proto_item_set_text(item_tmp, "ICMP.code = %d", icmp_code.code);

        // --------------------------------------------------------------------------------
        // show ICMP desc
        // --------------------------------------------------------------------------------
        was_find = false;
        for (i = 0; i < (int)(sizeof(icmp_code_desc_ary) / sizeof(icmp_code_desc_ary[0])); i++) {
            if (NULL == icmp_code_desc_ary[i].desc) {
                break;
            }

            if ((icmp_code_desc_ary[i].code.type == icmp_code.type)
                && (icmp_code_desc_ary[i].code.code == icmp_code.code))
            {
                was_find = true;
                break;
            }
        }

        // show ICMP packet is valid or not
        item_tmp = proto_tree_add_string_format_value(sub_tree_foo, hf_foo_message, tvb, 0, sizeof(icmp_code),
            NULL, // 可选
            NULL,
            -1);

        if (!was_find) {
            // show error msg
            proto_item_set_text(item_tmp, "unknown ICMP type and code, please connect developer");
            break;
        }
        else {
            // show icmp desc for (icmp.type + icmp.code)
            proto_item_set_text(item_tmp, "this is valid ICMP packet : [%s]", icmp_code_desc_ary[i].desc);
        }

        // update data offset
        data_offset += sizeof(TAG_ICMP_CODE);

        // --------------------------------------------------------------------------------
        // get check sum
        // --------------------------------------------------------------------------------
        if (data_len_left < sizeof(check_sum)) {
            break;
        }

        check_sum = ntohs(*(guint16*)data_left); // BE data

        // calc check sum
        check_sum_by_calc = calc_check_sum((guint8*)data_org, captured_length);
        data_left += sizeof(check_sum);
        data_len_left -= sizeof(check_sum);

        // show checksum
        item_tmp = proto_tree_add_string_format_value(sub_tree_foo, hf_foo_message, tvb, data_offset, sizeof(check_sum),
            NULL, // 可选
            NULL,
            -1);

        proto_item_set_text(item_tmp, "check sum %s : [0x%X]", (check_sum_by_calc == check_sum) ? "ok" : "error", check_sum);

        // update data offset
        data_offset += sizeof(check_sum);

        // --------------------------------------------------------------------------------
        // read icmp id
        // --------------------------------------------------------------------------------
        if (data_len_left < sizeof(check_sum)) {
            break;
        }

        icmp_id = *(guint16*)data_left;
        data_left += sizeof(icmp_id);
        data_len_left -= sizeof(icmp_id);

        // show icmp_id
        item_tmp = proto_tree_add_string_format_value(sub_tree_foo, hf_foo_message, tvb, data_offset, sizeof(icmp_id),
            NULL, // 可选
            NULL,
            -1);

        proto_item_set_text(item_tmp, "ID : [0x%X]", icmp_id);

        // update data offset
        data_offset += sizeof(icmp_id);

        // --------------------------------------------------------------------------------
        // read sequence number
        // --------------------------------------------------------------------------------
        if (data_len_left < sizeof(sequence_number)) {
            break;
        }

        sequence_number = ntohs(*(guint16*)data_left); // data is BE
        data_left += sizeof(sequence_number);
        data_len_left -= sizeof(sequence_number);

        // show icmp_id
        item_tmp = proto_tree_add_string_format_value(sub_tree_foo, hf_foo_message, tvb, data_offset, sizeof(sequence_number),
            NULL, // 可选
            NULL,
            -1);

        proto_item_set_text(item_tmp, "sequence number : [0x%X]", sequence_number);

        // update data offset
        data_offset += sizeof(sequence_number);

        // --------------------------------------------------------------------------------
        // show data
        // --------------------------------------------------------------------------------
        // 加一个子树来展现数据长度和数据
        sub_tree_foo_sub1 = proto_tree_add_subtree(sub_tree_foo, tvb, data_offset, data_len_left, ett_foo_subtree_1, NULL, "data");
        item_tmp = proto_tree_add_string_format_value(sub_tree_foo_sub1, hf_foo_message, tvb, data_offset, data_len_left,
            NULL, // 可选
            NULL,
            -1);

        proto_item_set_text(item_tmp, "data len = %d(bytes)", data_len_left);
        if (data_len_left > 0) {
            item_tmp = proto_tree_add_string_format_value(sub_tree_foo_sub1, hf_foo_message, tvb, data_offset, data_len_left,
                NULL, // 可选
                NULL,
                -1);

            /*
            if is ping request or reply, show text data, else show binary data on data area
            { "echo request (ping)", { 8, 0 } },
            { "echo reply (ping)",{ 0, 0 } },
            */
            if (((8 == icmp_code.type) && (0 == icmp_code.code))
                || ((0 == icmp_code.type) && (0 == icmp_code.code)))
            {
                memcpy(sz_buf, data_left, data_len_left);
                sz_buf[data_len_left] = '\0';
                proto_item_set_text(item_tmp, "data = [%s]", sz_buf);
            }
            else {
                proto_item_set_text(item_tmp, "data maybe binary, please see it on data area");
            }
        }

        b_pasre_ok = true;
    } while (0);

    if (NULL != sub_tree_foo) {
        // show END message, parse ok or error
        item_tmp = proto_tree_add_string_format_value(sub_tree_foo, hf_foo_message, tvb, 0, -1,
            NULL, // 可选
            NULL,
            -1);

        proto_item_set_text(item_tmp, b_pasre_ok ? "nice, parse ok" : "error, please connect developer");
    }

    return captured_length; // return data length by process
}

猜你喜欢

转载自blog.csdn.net/LostSpeed/article/details/80964508