#!/usr/local/bin/perl


# File: ppcap.pl


use Net::Pcap qw(pcap_open_offline pcap_loop);
use Net::Packet;
use NetPacket::Ethernet qw(:ALL);
use NetPacket::IP qw(:ALL);
use NetPacket::TCP;

use Config::Auto;
use Data::Dumper;

# ##########################################################################
# Read and process config info.  This code simply reads in various settings
# from a flat text file.  It makes it easier to change the way the code 
# runs without actually having to edit the code.  We might not even use 
# all of the vars, but we'll read them all just in case.
# ##########################################################################

     my $startTime = time;

     my $config = Config::Auto::parse("./ppcap.conf");

     my $debug = $config->{debugLevel};
     my $capFile = $config->{defaultCapFile};
     my $synExpected = $config->{synExpected};
     my $senderAddr = $config->{senderAddress};
     my $receiverAddr = $config->{receiverAddress};


# ##########################################################################
# If the user specified a pcap capture file on the command line, then
# we'll use it, otherwise, we'll use the one specified as the default
# in the conf file. 
# ##########################################################################

     if ($ARGV[0]) {
        $capFile = $ARGV[0];
     }

# ##########################################################################
# Now open the pcap file and process each line.
# ##########################################################################

     my $pcap = pcap_open_offline($capFile,\$error_msg);

     print "###############################################################\n";
     print "Opened $capFile\n";
     print "###############################################################\n";

     my $packetCount = 1;

     pcap_loop($pcap, -1, \&process_packet, "user data");

     print "###############################################################\n";

     my $endTime = time;

     my $totalTime = $endTime - $startTime;

     print "Analysis completed in $totalTime seconds\n";

exit;

# ##########################################################################
#
# Subroutine: process_packet
#
# As with any Net::Pcap program, this is the routine that does all the 
# work.  It will be called once for each packet in the trace.  
#
# Valid NetPacket::TCP fields (with sample values) are:
#                 'hlen' => 5,
#                 'urg' => 0,
#                 '_parent' => undef,
#                 'src_port' => 2182,
#                 'flags' => 24,
#                 'options' => '',
#                 'seqnum' => 722539488,
#                 'data' => 'somedataastextstring',
#                 'cksum' => 7802,
#                 'winsize' => 64402,
#                 'acknum' => 1583632711,
#                 '_frame' => '<whole frame as raw hex data>',
#                 'dest_port' => 3025,
#                 'reserved' => 0
#
# Valid NetPacket::IP fields (with sample values) are:
#                 'len' => 54,
#                 'dest_ip' => '192.168.23.2',
#                 'options' => '',
#                 'ttl' => 128,
#                 'src_ip' => '192.168.23.129',
#                 'tos' => 0,
#                 'id' => 296,
#                 '_parent' => undef,
#                 'hlen' => 5,
#                 'proto' => 17,
#                 'foffset' => 0,
#                 'flags' => 0,
#                 'ver' => 4,
#                 'cksum' => 35259,
#                 'data' => 'somedataastextstring',
#                 '_frame' => '<whole frame as raw hex data>'
#

sub process_packet {

     my ($user_data, $header, $raw_pkt) = @_;

     my $IPinfo = NetPacket::IP->decode(eth_strip($raw_pkt));

     my $TCPinfo = NetPacket::TCP->decode(
              NetPacket::IP::strip(NetPacket::Ethernet::strip($raw_pkt)));

     # ######################################################################
     # Now we have a packet in both IP and TCP versions (see above for 'field'
     # names.
     # ######################################################################

     if (($IPinfo->{'proto'} == IP_PROTO_TCP) && $debug) {
             print "Packet $packetCount Looks like a TCP packet\n";
     }

     my $dataLength = length($TCPinfo->{'data'});
     my $sourceAddr = $IPinfo->{'src_ip'};
     my $destAddr = $IPinfo->{'dest_ip'};
     my $sourcePort = $TCPinfo->{'src_port'};
     my $destPort = $TCPinfo->{'dest_port'};

     if ($TCPinfo->{'data'} =~ /\x4d\x5a/) {
	     print "Possible program (MZ Signature) at packet $packetCount, packet from $sourceAddr to $destAddr\n";
             if ($debug) {
                 dumpData($TCPinfo->{'data'},$dataLength);
             }
     }	     

     if ($TCPinfo->{'data'} =~ /This program cannot be run in DOS mode/) {
	     print "Found program (DOS Stub) at packet $packetCount, packet from $sourceAddr to $destAddr\n";
             if ($debug) {
                 dumpData($TCPinfo->{'data'},$dataLength);
             }
     }	     

     if ($TCPinfo->{'data'} =~ /jar/i) {
	     print "Found possible java applet at packet $packetCount, packet from $sourceAddr to $destAddr\n";
             if ($debug) {
                 dumpData($TCPinfo->{'data'},$dataLength);
             }
     }	     


     $packetCount++;

}

# ##########################################################################
#
# Subroutine: dumpData
#
# ##########################################################################

sub dumpData {
   
      my ($data, $dataLength) = @_;

      my $packetPointer = 0;

      while ($packetPointer < $dataLength) {
        
           my $dataSegment = substr($data, $packetPointer, 16);

           printf ("#     %.4X : ", $packetPointer);

           my $i = 0;

           print map ($i++ % 16 ? "$_ " : "    $_ ",
                      unpack ('H2 ' x 16,$dataSegment)
                     );

           $dataSegment =~ s/[\x00-\x1f\x7f-\xff]/./g;

           print "     $dataSegment\n";
       
           $packetPointer += 16;

       }

       print "\n"

}



