[JAVA] 如何编写协议解码函数

7595 0
nannet 2011-2-16 05:29:06 | 显示全部楼层 |阅读模式
信息来源:www.chinakacher.net
这是介绍几个简单的协议解码函数,是参考NetPacket模块中的相关函数修改的。如Windows下通常使用winpcap库抓包,使用模块是Net:cap和Net:capUtils,其中Net:cap是Perl的winpcap库接口,Net:capUtils里映射了一些Net:cap中常用的函数,提供了更方便些的接口。写协议分析程序一般步骤是:

1、打开设备;
2、循环抓包,对每个包进行逐层解码,在每层解码后进行相关字段的过滤;
3、最后将满足过滤条件的包输出;
4、加上对中断信号的处理,各种统计Counter等其他特性;

举例来说,打开Eth0设备,循环抓包,最初获得eth frame,用eth_decode函数处理,获得帧的src_mac、dest_mac、type和data字段,其中data字段里是上层协议数据。然后根据type字段指示的上层协议类型,对data字段的值应用相应的解码函数,如type是“0x0800”,则表明这个以太帧里面是IP包-对应就是以太帧的data字段,用ip_decode函数处理这个字段,继而获得这个IP包中的各个字段,同理它的data字段就是IP层的上层协议包的内容,又IP包中的proto字段指示四层协议的类型,如IP的proto字段是6,则表明四层是TCP协议,那么就对IP包data字段应用tcp_decode函数进行解析,依此类推。

综上,每层协议头中必定定义了某个字段(如eth.type,ip.proto,ppp.code, etc.)指示如何解析该层携带的上层数据包(data)。具体解码做法就是参考协议的RFC文档,使用unpack逐层解码、计算生成每层相关字段。当编写某个协议的解码函数时,可以参考下面几个decode函数,其中关键是unpack的使用,因为是解析网络中的数据包,当然使用网络字节顺序来unpack,常用下面几个字符:

N - 32bits 网络字节顺序
n - 16bits 网络字节顺序
C - 8bits 网络字节顺序
a - 任意二进制

对于非整字节的字段(如4bits和13bits等),按C或n取得整字节后移位获得,如IP Header的结构是:

    0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |Version|  IHL  |Type of Service|          Total Length         | <-CCn
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |         Identification        |Flags|      Fragment Offset    | <-nn
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Time to Live |    Protocol   |         Header Checksum       | <-CCn
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                       Source Address                          | <-N
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Destination Address                        | <-N
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    | <-a*
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                    Example Internet Datagram Header

参考rfc791.txt,对应的解码函数可以写成:

sub ip_decode {
    my $packet = shift;
    my $ippkt_ref = {};
    my @fields = qw/tmp tos len id foffset ttl proto cksum src_ip dest_ip options/;
    my @values = unpack('CCnnnCCnNNa*' , $packet);

    map { $ippkt_ref->{$fields[$_]} = $values[$_] } (0..$#fields);

    #Extract bit fields
    $ippkt_ref->{ver} = ($ippkt_ref->{tmp} & 0xf0) >> 4;
    $ippkt_ref->{hlen} = $ippkt_ref->{tmp} & 0x0f;
    $ippkt_ref->{flags} = $ippkt_ref->{foffset} >> 13;
    $ippkt_ref->{foffset} = ($ippkt_ref->{foffset} & 0x1fff) << 3;

    #Decode variable length header options and remaining data in field
    #Option length is number of 32 bits words
    my $olen = ($ippkt_ref->{hlen} - 5) ? ($ippkt_ref->{hlen} - 5) * 4 : 0;

    ($ippkt_ref->{options}, $ippkt_ref->{data}) = unpack('a'.$olen.'a*', $ippkt_ref->{options});

    #Convert 32 bit ip addresses to dotted quad notation
    $ippkt_ref->{src_ip} = to_dotquad($ippkt_ref->{src_ip});
    $ippkt_ref->{dest_ip} = to_dotquad($ippkt_ref->{dest_ip});

    undef $ippkt_ref->{tmp};
    return $ippkt_ref;
}

--Packet Decoders eXamples--

#Packets decoders
sub eth_decode {
    my $packet = shift;
    my $ethframe_ref = {};
    my @fields = qw/sm_lo sm_hi dm_lo dm_hi type data/;
    my @values = unpack('NnNnna*', $packet);
    map { $ethframe_ref->{$fields[$_]} = $values[$_] } (0..$#fields);

    #Convert MAC addresses to hex string to avoid representation problems
    $ethframe_ref->{src_mac} = sprintf("%08x%04x", $ethframe_ref->{sm_hi}, $ethframe_ref->{sm_lo});
    $ethframe_ref->{dest_mac}= sprintf("%08x%04x", $ethframe_ref->{dm_hi}, $ethframe_ref->{dm_lo});

    undef $ethframe_ref->{$_} foreach(qw/sm_lo sm_hi dm_lo dm_hi/);
    return $ethframe_ref;
}

sub arp_decode {
    my $packet = shift;
    my $arppkt_ref = {};
    my @fields = qw/htype proto hlen plen opcode sha spa tha tpa data/;
    my @values = (unpack('nnCCnH12H8H12H8', $packet), undef);

    map { $arppkt_ref->{$fields[$_]} = $values[$_] } (0..$#fields);

    return $arppkt_ref;
}

sub ppp_decode {
    my $packet = shift;
    my $ppppkt_ref = {};
    my @fields = qw/code id length data/;
    my @values = unpack('ccna*', $packet);

    map { $ppppkt_ref->{$fields[$_]} = $values[$_] } (0..$#fields);

    if($ppppkt_ref->{code} =~ m/[1-4]/) {
        $ppppkt_ref->{type} = unpack('c', $ppppkt_ref->{data});

        my @fields;

        @fields = qw/type length MRU data/ if($ppppkt_ref->{type} == PPP_TYPE_MRU);
        @fields = qw/type length auth data/ if($ppppkt_ref->{type} == PPP_TYPE_AUTHPROTO);
        @fields = qw/type length quality data/ if($ppppkt_ref->{type} == PPP_TYPE_QUALPROTO);
        @fields = qw/type length magic data/ if($ppppkt_ref->{type} == PPP_TYPE_MAGICNUM);

        my @values = unpack('ccna*', $ppppkt_ref->{data});

        map { $ppppkt_ref->{$fields[$_]} = $values[$_] } (0..$#fields);
    }

    if($ppppkt_ref->{code} == PPP_CODE_ECHOREQ or $ppppkt_ref->{code} == PPP_CODE_ECHOREPLY) {
        my @fields = qw/magic data/;
        my @values = unpack('Na*', $ppppkt_ref->{data});

        map { $ppppkt_ref->{$fields[$_]} = $values[$_] } (0..$#fields);
    }

    return $ppppkt_ref;
}

sub ip_decode {
    my $packet = shift;
    my $ippkt_ref = {};
    my @fields = qw/tmp tos len id foffset ttl proto cksum src_ip dest_ip options/;
    my @values = unpack('CCnnnCCnNNa*' , $packet);

    map { $ippkt_ref->{$fields[$_]} = $values[$_] } (0..$#fields);

    #Extract bit fields
    $ippkt_ref->{ver} = ($ippkt_ref->{tmp} & 0xf0) >> 4;
    $ippkt_ref->{hlen} = $ippkt_ref->{tmp} & 0x0f;
    $ippkt_ref->{flags} = $ippkt_ref->{foffset} >> 13;
    $ippkt_ref->{foffset} = ($ippkt_ref->{foffset} & 0x1fff) << 3;

    #Decode variable length header options and remaining data in field
    #Option length is number of 32 bits words
    my $olen = ($ippkt_ref->{hlen} - 5) ? ($ippkt_ref->{hlen} - 5) * 4 : 0;

    ($ippkt_ref->{options}, $ippkt_ref->{data}) = unpack('a'.$olen.'a*', $ippkt_ref->{options});

    #Convert 32 bit ip addresses to dotted quad notation
    $ippkt_ref->{src_ip} = to_dotquad($ippkt_ref->{src_ip});
    $ippkt_ref->{dest_ip} = to_dotquad($ippkt_ref->{dest_ip});

    undef $ippkt_ref->{tmp};
    return $ippkt_ref;
}

sub icmp_decode {
    my $packet = shift;
    my $icmppkt_ref = {};
    my @fields = qw/type code cksum data/;
    my @values = unpack('CCna*', $packet);

    map { $icmppkt_ref->{$fields[$_]} = $values[$_] } (0..$#fields);

    return $icmppkt_ref;
}

sub tcp_decode {
    my $packet = shift;
    my $tcppkt_ref = {};
    my @fields = qw/src_port dest_port seqnum acknum tmp winsize cksum urg options/;
    my @values = unpack("nnNNnnnna*", $packet);

    map { $tcppkt_ref->{$fields[$_]} = $values[$_] } (0..$#fields);

    #Extract flags
    $tcppkt_ref->{hlen} = ($tcppkt_ref->{tmp} & 0xf000) >> 12;
    $tcppkt_ref->{reserved} = ($tcppkt_ref->{tmp} & 0x0f00) >> 8;
    $tcppkt_ref->{flags} = $tcppkt_ref->{tmp} & 0x00ff;

    #Decode variable length header and remaining data
    my $olen = ($tcppkt_ref->{hlen} - 5) ? ($tcppkt_ref->{hlen} - 5) * 4 : 0;

    ($tcppkt_ref->{options}, $tcppkt_ref->{data}) = unpack('a'.$olen.'a*', $tcppkt_ref->{options});

    undef $tcppkt_ref->{tmp};
    return $tcppkt_ref;
}

sub udp_decode {
    my $packet = shift;
    my $udppkt_ref = {};
    my @fields = qw/src_port dest_port len cksum data/;
    my @values = unpack("nnnna*", $packet);

    map { $udppkt_ref->{$fields[$_]} = $values[$_] } (0..$#fields);

    return $udppkt_ref;
}
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

nannet

初入联盟

关注
  • 主题
  • 粉丝
  • 关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行