Born to be proud
11/25
2017

perl解析pcap文件

关于Pcap文件解析的方法网上有很多相关文章,Pcap文件的格式以及使用Wireshark查看Pcap文件的方法也都有详细教程。然而利用Perl脚本语言对Pcap文件进行解析的相关代码和文章并没有。本文介绍了如何使用Perl脚本语言对Pcap文件进行解析。

模块安装

Net::Pcap 模块封装了用于解析Pcap文件的一些方法,首先介绍如何使用CPAN安装 Net::Pcap模块。

//CPAN相关命令
//获得帮助  
cpan>h  

//列出CPAN上所有模块的列表  
cpan>m  

//安装模块  
cpan>install Net::Pcap 

//退出  
cpan>q  

 

读取Pcap文件

pcap_loop($pcap, $count, \&callback, $user_data)
从pcap读取count个包,并且调用使用参数user_data调取callback函数。如果count为负,则循环读取直到错误发生。

my $pcap = pcap_open_offline($fileDir, \$err) || die "failed to open pcap file $pcap!";
while(1)
{
    $ret = pcap_loop($pcap, -1, \&load_pcap, $this);
    if($ret == -1)
    {
        print "error on processing packets in pcap file: $@!\n";
        die "error on processing packets in pcap file: $@!\n";
        last;
    }
    elsif($ret == 0 || $ret == -2)
    {
        print "end of pcap processing: $@!\n";
        last;
    }
}

解析数据包

网络数据包格式如下图所示:
image

unpack (packformat, formatstr) 函数将以机器格式存贮的值转化成Perl中值。packformat代表解析格式,formatstr为要解析的对象。

通过判断各个位置上的值从而确定包的类型。通过记录连接的状态,确定TCP三次握手的建立与解除。具体代码如下:

foreach my $event (sort(keys(%{$packets})))
    {
        print "testlog: a new packet begin----------------------- \n";

        my $packet = $packets->{$event}->{pkt};
        my $proto = unpack("H*", substr($packet, 12, 2));
        if($proto eq "0800") #ipv4
        {
            $count ++;
            my $off = 14;
            my $iphl = (unpack("C", substr($packet, $off, 1)) & 0x0f); # header length  , <<2 is the actual length
            my $ip_payload_len = unpack("n", substr($packet, $off + 2, 2)); #total length
            #print "testlog: packet total length: ". $ip_payload_len ."\n";
            $proto = unpack("C", substr($packet, $off + 9, 1)); #net proto

            my $sip = inet_ntoa(substr($packet, $off + 12, 4)); #source ip addr
            my $dip = inet_ntoa(substr($packet, $off + 16, 4)); #dest ip addr

            # chop the padding bytes off
            $packet = substr($packet, 0, $off + $ip_payload_len); #a full packet

            if($iphl > 5)
            {print "Warning: ip packet with ip option\n";}

            my $ipPayloadLen = unpack("n", substr($packet, $off + 2, 2)) - ($iphl << 2); #total length - header length 

            # skip ipv4 header
            $off += ($iphl << 2);
            if($proto == 6)        # tcp session
            {
                my $tcp_hdrl = unpack("C", substr($packet, $off + 12, 1)) >> 2; # tcp header length , <<2 is the actual length
                my $tcp_flag = unpack("C", substr($packet, $off + 13, 1)); #tcp flag
                #print $tcp_flag . "\n";
                my $sport = unpack("n", substr($packet, $off, 2));    #source port
                my $dport = unpack("n", substr($packet, $off + 2, 2));    #dest port

                if($ipPayloadLen - $tcp_hdrl < 0) # eq 0
                {
                    print "invalid tcp packet: $proto packet not supported!\n";    
                    last;
                }

                my $c = "$sip:$sport<->$dip:$dport";
                my $s = "$dip:$dport<->$sip:$sport";

                # recode session state
                if(!defined($session))
                {
                    print "testlog: session will be defined! \n";
                    $session = {
                        id => $c,
                        cstate => "closed",
                        sip => $sip,
                        dip => $dip,
                        sport => $sport,
                        dport => $dport
                    };
                }

                if($tcp_flag & 0x04)
                {
                    print "Warning: tcp session with RST, need mannual checking!Stop session analyzing: $frameID!\n";
                    $session->{cstate} = "closed";
                }
                print "testlog: session state: ". $session->{cstate} ."\n";
                if($session->{cstate} eq "closed")    # open session
                {
                    if(($tcp_flag & 0x02))        # syn open sesstion
                    {
                        $session->{cstate} = "syn_sent";
                    }
                    else
                    {
                        if(length(substr($packet, $off + $tcp_hdrl)) > 0)
                        {print "Warning: tcp session disordered on closed, need mannual checking!Stop session analyzing: $sip : $sport <-> $dip : $dport \n";}
                    }

                }
                elsif($session->{cstate} eq "syn_sent")
                {
                    #print ($tcp_flag);
                    #print "\n";
                    if(($tcp_flag & 0x12)==0x12){
                        print "ok";
                    };
                    print "\n";
                    if(($tcp_flag & 0x02) && ($sip eq $session->{sip}) && ($sport eq $session->{sport})) # syn from client retransmission
                    {print "tcp retransmission detected!\n";}
                    elsif(($tcp_flag & 0x12) && ($sip eq $session->{dip}) && ($sport eq $session->{dport}))    # syn_ack from server
                    {$session->{cstate} = "syn_rcvd";}
                    else
                    {
                        print "Warning: tcp session disordered on syn_sent, need mannual checking!Stop session analyzing!\n";
                    }

                }
                elsif($session->{cstate} eq "syn_rcvd")
                {
                    if(($tcp_flag & 0x10) && ($sip eq $session->{sip}) && ($sport eq $session->{sport}))  # ack from client
                    {
                        $session->{cstate} = "connected";         # won't check ack
                        print(FD "CONNECT twoarm 1 0 tcp $dip $dport $sip $sport\n\n");
                    }
                    else
                    {print "Warning: none ack packet on syn_rcvd!\n";}
                }
                elsif($session->{cstate} eq "connected")
                {
                    #print "testlog: $sip ---- $session->{sip} ----- $session->{dip}";
                    if($sip eq $session->{sip} && ($sport eq $session->{sport}))        # from client
                    {
                        if($tcp_flag & 0x01)
                        {
                            $session->{FIN} += 1;
                            $session->{FINFrom} = 1; # this is the second FIN, disconnect begin from server
                        }
                        else
                        {
                            if(length(substr($packet, $off + $tcp_hdrl)) > 0) #TCP payload is not empty
                            {
                                $str_temp = unpack('H*',substr($packet, $off + $tcp_hdrl));
                                my @str_temp2 = split(//, $str_temp);
                                my $str_temp3;
                                for($i=0; $i<@str_temp2; $i=$i+2){
                                    $str_temp3 = $str_temp3 . "\\x" . $str_temp2[$i] . $str_temp2[$i+1];
                                }
                                print "$str_temp3 \n";
                                print(FD "SEND 0 #\"$str_temp3\" NOW\n\n");
                            }
                        }
                    }
                    elsif($sip eq $session->{dip} && ($sport eq $session->{dport}))
                    {
                        if($tcp_flag & 0x01)
                        {
                            $session->{FIN} += 1;
                            $session->{FINFrom} = 0; # this is the second FIN, disconnect begin from client
                        }
                        else
                        {
                            if(length(substr($packet, $off + $tcp_hdrl)) > 0)
                            {
                                $str_temp = unpack('H*',substr($packet, $off + $tcp_hdrl));
                                my @str_temp2 = split(//, $str_temp);
                                my $str_temp3;
                                for($i=0; $i<@str_temp2; $i=$i+2){
                                    $str_temp3 = $str_temp3 . "\\x" . $str_temp2[$i] . $str_temp2[$i+1];
                                }
                                print "$str_temp3 \n";
                                print(FD "SEND 1 #\"$str_temp3\" NOW\n\n");
                            }
                        }
                    }
                    else
                    {
                        print "invalid packet on connected tcp session\n";
                    }
                }
                if($session->{cstate} eq "connected" && $session->{FIN} == 2)
                {
                    $FINFrom = $session->{FINFrom};
                    print(FD "DISCONNECT $FINFrom\n");
                    $session->{cstate} = "closed";
                    delete($session->{FIN});
                }
            }
        }
    }

相关文章

https://metacpan.org/pod/Net::Pcap
http://blog.csdn.net/eroswang/arti
http://blog.sina.com.cn/s/blog_4936c31d010115hj.htmlcle/details/2032564