#!/bin/perl -w

use strict;

# Liste der bekannten MAC-Adressen
#
# Schema:
# 01:23:45:67:89:0a,Klarname
#
# Buchstaben duerfen gross geschrieben werden
#
use constant MACLIST => "maclist.csv";

if( not -e MACLIST ) {
	die "Could not find list of known MACs '".MACLIST."'\n";
}

# Liste der Accesspoints auf die gelauscht werden soll
my @aplist = (
	'68:15:90:41:89:62',
	'E0:3F:49:F2:7C:20',
	
);

sub printHelp {
	print '
Usage: wifinder.pl <monitoring-interface>

                   Interface must be in monitoring mode, run: airmon-ng start <wifi-interface>

                   -h, --help            	prints this help
                   --offline capture.pcap	reads input from .pcap-File (for test purpose)


';

}

my $interface;
my $oDumpEvent;
if( ( $#ARGV >= 0 ) && ( $ARGV[0] ne ''  ) ) {
	if( $ARGV[0] eq "-h" || $ARGV[0] eq "--help" ) {
		printHelp();
	} elsif( $ARGV[0] eq "--offline" ) {
		if( defined( $ARGV[1] ) && -e $ARGV[1] ) {
			use Net::Frame::Dump::Offline;
			$oDumpEvent = Net::Frame::Dump::Offline->new( file => $ARGV[1] );
		} else {
			die( "Cannot find .pcap-File!\n" );
		}
	} else {
		use Net::Frame::Dump::Online2;
		# Netzwerk-Interface (muss sich im Monitoring-Modus befinden)
		$interface = $ARGV[0];

		$oDumpEvent = Net::Frame::Dump::Online2->new(
			dev    => $interface,
			filter => APFilter( @aplist ),
		) or die "Could not open network interface for monitoring!\n$!";
	}
} else {
	printHelp();
	exit 1;
}

sub getPkgData {
	my $data = shift;

	my $devMAC;
	my $offset;
	my $name = "Unknown";

	# Pakettyp ist das 18. Byte
	my $pkgtype = unpack( "C1", substr( $data, 18, 1 ) );

	SWITCH:{
		$pkgtype == 0x00 && do {

			$name = "Assossiation Request";
			# ab 28. Byte -> Source-MAC
			$offset = 28;
		};
		$pkgtype == 0x10 && do {

			$name = "Assossiation Response";
			# ab 22. Byte -> Destination-MAC
			$offset = 22;
		};
		$pkgtype == 0x0c && do {

			$name = "Deauth";
			# ab 22. Byte -> Destination-MAC
			$offset = 22;
		};
		$pkgtype == 0xb0 && do {

			$name = "Authentication (SEQ ";

			# ab 22. Byte -> Destination-MAC
			$offset = 22;
			# Authentication SEQ 44. Byte
			# entsprechend Source oder Dest Mac nehmen
			if( unpack( "C1", substr( $data, 44, 1 ) ) == 0x01 ) {
				# ab 28. Byte -> Source-MAC
				$offset = 28;
				$name .= "1)";
			} else {
				$name .= "2)";
			}
		};
	}

	if( $name eq "Unknown" ) {
		# Bei unbekanntem Paket "leere" MAC erzeugen
		$devMAC = "00:00:00:00:00:00";
		$pkgtype = -1;
	} else {
		foreach my $digit ( unpack( "C6", substr( $data, $offset, 6 ) ) ) {
			$devMAC .= sprintf( "%02x:", $digit );
		}
		# letzten : entfernen
		chop $devMAC;
	}

	return( {
		name => $name,
		type => $pkgtype,
		devMAC => $devMAC
	} );
}

sub MACtoUser {
	my $MACstring = shift;

	open( my $data, '<', MACLIST ) or die "Could not open '".MACLIST."' $!\n";
	while( my $line = <$data> ) {
		chomp $line;
 
		my @fields = split "," , $line;
		
		if( defined $fields[0] && lc( $fields[0] ) eq $MACstring ) {
			return( $fields[1]. " ($MACstring)" );
		}
	}
	close $data;

	return( "Unknown ($MACstring)" );
}

sub APFilter {
	my $apstr = "(type mgt subtype assoc-req or type mgt subtype assoc-resp or type mgt subtype auth or type mgt subtype deauth) and (";

	while( my $AP = shift ) {
		$apstr .= " wlan host $AP or";
	}
	
	# letztes 'or' entfernen
	chop $apstr;
	chop $apstr;
	$apstr .= ")";

	return( $apstr );
}




$oDumpEvent->start;

while( 1 ) {
	if( my $f = $oDumpEvent->next ) {
		my $raw            = $f->{raw};
		my $timestamp      = $f->{timestamp};

		print localtime()." | New Pkg found: ";
		
		my $pkgdata = getPkgData( $raw );
		print $pkgdata->{name};
		print "\nFrom: ".MACtoUser( $pkgdata->{devMAC} );
		
		print "\n\n";
	} else {
		sleep 1;
	}
}

$oDumpEvent->stop;
