#!/usr/bin/env python
# -*- coding: utf-8 -*-

#For best results, run as root in a safe enviornment.
#This is experimental software.  Use at your own risk.
#This requires the scapy python library, the aircrack-ng suite, gpsd, and the gps python library.
#Read the original article for further documentation.

from scapy.all import *
import os, sys, time, datetime
from gps import *
#Insert some threading to run multiple functions at the same time.
from threading import Thread
#Some variables to use globally later.
menu = ""
interface = ""
hwaddress = ""
#Optional vender list to view the names of the hardware venders.
venderlist = []
#Our output file's name.
file_name = ""
#Lattitude/Longitude global variables.
lat = 0.0
lon = 0.0
#Menu display.  Title font uses a figlet font named Bloody.  Requires utf coding.
def displaymenu():
   global menu 
   menu = raw_input("\n\
To continue, type a number and then press enter:\n\
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\
Choose an option:\n\
1. Change into monitor mode with airmon-ng.\n\
2. Start gpsd and specify your gps device.\n\
3. Scan for all hardware addresses and write to file. ctrl-z to exit.\n\
4. Match hardware addresses from different file outputs.\n\
5. Scan for one or more specific hardware addresses from a file.\n\
6. Find probes and associated devices from a hw address.  This scans through your airodump-ng database.\n\
7. Create or update hardware vender file to identify most devices scanned.\n\
8. Stop monitor mode and return wifi to normal. \n\
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\
")


#---------------------definitions-----------------------    
def AddressScan(pkt) :
   global file_name
   global lat
   global lon
   splitstring = []
   f = open(file_name, "a")
   venderfound  = 0
   thetimeis = datetime.datetime.now()
   #This section looks for valid harddware addresses.  The length will be 17.  Then it looks through your hardware vender file
   #to figure out which type of device the address belongs to.  It also takes note of the date/time and gps coordinates.
   if pkt.addr1 not in clients and len(str(pkt.addr1)) == 17:
       clients.append(pkt.addr1)
       for line in venderlist:
           if len(line) > 2 and line[2] == ":":
               splitstring = line.split(',')
               if str(pkt.addr1)[:len(splitstring[0])] == splitstring[0].lower() and venderfound == 0:
                   f.write(str(pkt.addr1) + "," + splitstring[1].rstrip() + "," + str(lat) + "," + str(lon) + "," + str(thetimeis) + "\n")
                   print "Device Found: %s - %s,%s,%s,%s" % ((pkt.addr1), splitstring[1].rstrip(), str(lat), str(lon),str(thetimeis))
                   venderfound = 1
       if venderfound == 1:
           venderfound = 0
       else:
           f.write(str(pkt.addr1) + ",unknown," + str(lat) + "," + str(lon) + "," + str(thetimeis) + "\n")
           print "Device Found: %s,unknown,%s,%s,%s" % ((pkt.addr1), str(lat), str(lon), str(thetimeis))

   if pkt.addr2 not in clients and len(str(pkt.addr2)) == 17:
       clients.append(pkt.addr2)
       for line in venderlist:
           if len(line) > 2 and line[2] == ":":
               splitstring = line.split(',')
               if str(pkt.addr2)[:len(splitstring[0])] == splitstring[0].lower() and venderfound == 0:
                   f.write(str(pkt.addr2) + "," + splitstring[1].rstrip() + "," + str(lat) + "," + str(lon) + "," + str(thetimeis) + "\n")
                   print "Device Found: %s - %s,%s,%s,%s" % ((pkt.addr2), splitstring[1].rstrip(), str(lat), str(lon), str(thetimeis))
                   venderfound = 1
       if venderfound == 1:
           venderfound = 0
       else:
           f.write(str(pkt.addr2) + ",unknown," + str(lat) + "," + str(lon) + "," + str(thetimeis) + "\n")
           print "Device Found: %s,unknown,%s,%s,%s" % ((pkt.addr2), str(lat), str(lon), str(thetimeis))

   if pkt.addr3 not in clients and len(str(pkt.addr3)) == 17:
       clients.append(pkt.addr3)
       for line in venderlist:
           if len(line) > 2 and line[2] == ":":
               splitstring = line.split(',')
               if str(pkt.addr3)[:len(splitstring[0])] == splitstring[0].lower() and venderfound == 0:
                   f.write(str(pkt.addr3) + "," + splitstring[1].rstrip() + "," + str(lat) + "," + str(lon) + "," + str(thetimeis) + "\n")
                   print "Device Found: %s - %s,%s,%s,%s" % ((pkt.addr3), splitstring[1].rstrip(), str(lat), str(lon), str(thetimeis))
                   venderfound = 1
       if venderfound == 1:
           venderfound = 0
       else:
           f.write(str(pkt.addr3) + ",unknown," + str(lat) + "," + str(lon) + "," + str(thetimeis) + "\n")
           print "Device Found: %s,unknown,%s,%s,%s" % ((pkt.addr3), str(lat), str(lon), str(thetimeis))

def scancommand(pkt) :
   global file_name
   global hwaddressfile
   global lat
   global lon
   global systemcommand
   f = open(file_name, "a")
   if pkt.addr1 in clients:
       thetimeis = datetime.datetime.now()
       print "Device Detected: %s, %s, %s, %s, %s" % ((pkt.addr1), clients[pkt.addr1], str(lat), str(lon), str(thetimeis))
       f.write(str(pkt.addr1) + "," + clients[pkt.addr1] + "," + str(lat) + "," + str(lon) + "," + str(thetimeis) + "\n")
       if systemcommand != "":
           os.system(systemcommand)
   if pkt.addr2 in clients:
       thetimeis = datetime.datetime.now()
       print "Device Detected: %s, %s, %s, %s, %s" % ((pkt.addr2), clients[pkt.addr2], str(lat), str(lon), str(thetimeis))    
       f.write(str(pkt.addr2) + "," + clients[pkt.addr2] + "," + str(lat) + "," + str(lon) + "," + str(thetimeis) + "\n")
       if systemcommand != "":
           os.system(systemcommand)

   if pkt.addr3 in clients:
       thetimeis = datetime.datetime.now()
       print "Device Detected: %s, %s, %s, %s, %s" % ((pkt.addr3), clients[pkt.addr3], str(lat), str(lon), str(thetimeis))
       f.write(str(pkt.addr3) + "," + clients[pkt.addr3] + "," + str(lat) + "," + str(lon) + "," + str(thetimeis) + "\n")
       if systemcommand != "":
           os.system(systemcommand)
   f.close()

def channelhop():
   channel = 1
   while channel < 14:
       os.system("iw dev %s set channel %d" % (interface, channel))
       time.sleep(.01)
       channel = channel + 1
           
       if channel == 13:
           channel = 1

#This feature requires you to set up gpsd on your system.  Also requires the python module gps.
def gpsfunct():
   global lat
   global lon
   gpsd = gps(mode=WATCH_ENABLE|WATCH_NEWSTYLE)
   while True:
       report = gpsd.next()
       if report['class'] == 'TPV':
           lat = getattr(report,'lat',0.0)
           lon = getattr(report,'lon',0.0)

def airodumpdatabase():
       #Run airodump and save all the data. we can refer to this data later.  This line uses the -K 1 option to run airodump-ng in the
       #background.  If this option isn't used airodump-ng seems to override the output.  This will keep on running even after the
       #python script is closed.  You may want to close it manually when you're finished.
       os.system('airodump-ng -K 1 -w' + "aird-db/" + str(datetime.datetime.now()).replace(" ","") + ' --output-format csv ' + interface)
#---------------------menu------------------------------ 
while True:
   displaymenu()
   if menu == "1":
       os.system("clear")
       #Assumes the user has iwconfig.  Shows available interfaces.
       os.system("iwconfig")
       #User inputs preferred wireless interface
       interface = raw_input("Please enter your wireless interface: (ex. wlan0)\n")
       #Device is turned off and then put into monitor mode
       os.system("ip link set dev " + interface + " down")
       os.system("airmon-ng start " + interface)
       #If you type in your interface name incorrectly you should restart.  The other options will assume you succesfully entered monitor mode.
       interface = interface + "mon"
   if menu == "2":
       gpsdevice = raw_input("Please enter your gps device. (ex. /dev/ttyUSB0)\n")
       os.system("gpsd " + gpsdevice + " -F /var/run/gpsd.sock")
   if menu == "3":
       #Start GPS function so that can load while prompts are entered.
       Thread(target = gpsfunct).start()
       clients = []
       clients.append("ff:ff:ff:ff:ff:ff")
       #User inputs interface if string is empty.
       if interface == "":
           os.system("clear")
           os.system("iwconfig")
           interface = raw_input("Please enter your wireless interface: (ex. wlan0mon)\n")
       if os.path.exists("hwvenderlist"):
           venderfile = "hwvenderlist"
       else:
           venderfile = raw_input("Please enter the name of the file with hardware venders, or leave this blank.\n")
       if venderfile != "":
           vf = open(venderfile,"r")
           for line in vf:
               venderlist.append(line)
           vf.close()
       blacklistfile = raw_input("Enter the name of your blacklist file or leave this blank and press enter.\n")
       if blacklistfile != "":
           bl = open(blacklistfile,"r")
           for line in bl:
               #Truncate the line to 17 characters.
               clients.append(line[:17])
           bl.close()
       file_name = raw_input("Please name the output file.\n")
       if file_name == "":
           file_name = "wt-option3-default-output-" + str(datetime.datetime.now())
       #Checks for airodump-database directory.  Creates it if it doesn't exist.  We can use these files later.
       if os.path.exists("aird-db") == False:
           os.system("mkdir aird-db")
       
       #Press ctrl c OR ctrl Z to stop scripts
       #Runs our channel hopper, address scanner, and airodump-ng database.
       Thread(target = airodumpdatabase).start()
       Thread(target = channelhop).start()
       Thread(target = sniff(iface=interface, prn = AddressScan)).start()
   if menu == "4":
       list1 = []
       list2 = []
       file1 = raw_input("Enter the name of your first output file.\n")
       file2 = raw_input("Enter the name of your second output file.\n")
       savedfile = raw_input("If you would like to save the matches to a file enter a file name.\n")
       f1 = open(file1,"r")
       f2 = open(file2,"r")
       #Check to see if the user wants to save a file.  Otherwise you'll get an error.
       if savedfile != "":
           nf = open(savedfile,"a")
       for line in f1:
           list1.append(line.lower()[:17])
       f1.close()
       for line in f2:
           list2.append(line.lower()[:17])
       f2.close()
       for line in set(list1).intersection(list2):
           print line
           if savedfile != "":
               nf.write(line)
       raw_input("Press enter to return to menu.")
       os.system("clear")
   if menu == "5":
       Thread(target = gpsfunct).start()
       if interface == "":
           os.system("clear")
           os.system("iwconfig")
           interface = raw_input("Please enter your wireless interface: (ex. wlan0mon)\n")
       clients = {}
       hwaddressfile = raw_input("Please enter the filename that contains the addresses you would like to scan for\n")
       file_name = raw_input("Enter the name of the file to output successful scan info.  (date/time GPS)\n")
       if file_name == "":
           file_name = "wt-option5-default-output-" + str(datetime.datetime.now())
       systemcommand = raw_input("Enter a shell command to run on a successful scan. (ex. vlc ring.wav)\n")
       hwf = open(hwaddressfile,"r")
       splitstring = []
       for line in hwf:
           splitstring = line.split(",")
           if len(splitstring) > 1:
               clients.update({splitstring[0][:17].lower() : splitstring[1].rstrip()})
           else:
               clients.update({splitstring[0][:17].lower() : "no name"})
       Thread(target = channelhop).start()
       Thread(target = sniff(iface=interface, prn = scancommand)).start()
       hwf.close()
   if menu == "6":
       splitstring = []
       airdfile = []
       airddb = os.listdir("aird-db")
       clientmac = raw_input("Please enter the mac address of the client.\n")
       clientmac = clientmac.upper()
       for line in airddb:
           ad = open("aird-db/" + line,"r")
           #We start scanning from the bottom.  The first line we need is len(airdfile)-2.
           linenum = 2
           for line in ad:
               airdfile.append(line)
           #Checks for a colon on the 3rd character of the line.  If it's there it should be a client.
           while airdfile[len(airdfile)-linenum][2] == ":":
               splitstring = airdfile[len(airdfile)-linenum].split(',')
               #Prints out the associated client.
               if splitstring[0] == clientmac:
                   print "Associated AP:"
                   print splitstring[5]
               if splitstring[0] == clientmac and len(splitstring[6]) != 2:
                   for probe in range(len(splitstring) - 6):
                       print "Probe:"
                       print splitstring[6 + probe]
               linenum = linenum + 1
           ad.close()
       raw_input("Press enter to return to menu.")
       os.system("clear")
   
   if menu == "7":
       #We start out with the wireshark manuf file.  That has all the info we need.  It just has to be modified.
       os.system("wget -O hwvenderlist-tempfile-delete https://raw.githubusercontent.com/wireshark/wireshark/master/manuf")
       #Get rid of all the commas.  We need to turn this into a csv file of sorts.
       os.system("sed -i 's/,//g' hwvenderlist-tempfile-delete")
       #Replace the first tab on each line with a comma.  This should separate all the hardware addresses.
       os.system("sed -i 's/\\t/,/' hwvenderlist-tempfile-delete")
       #Truncate the "netmasks" after the specified number of bits.
       os.system("sed -i 's/0:00\\/36//' hwvenderlist-tempfile-delete")
       os.system("sed -i 's/0:00:00\\/28//' hwvenderlist-tempfile-delete")
       splitstring = []
       ieeereg = ""
       #We need to move all the IeeeRegi addresses to the bottom.  Some are redundant after modifying the netmasks.
       with open("hwvenderlist-tempfile-delete", "r") as fdownload:
           with open("hwvenderlist", "w") as output:
               output.write("# This file has been modified for use with wifitrack.  Sorry for any confusion.\n")
               for line in fdownload:
                   splitstring = line.split(",")
                   if len(splitstring) > 1 and splitstring[1][:8] == "IeeeRegi":
                       ieeereg = ieeereg + line
                   else:
                       output.write(line)
               output.write(ieeereg)
       fdownload.close()
       output.close()
       os.system("rm hwvenderlist-tempfile-delete")
       
   if menu == "8":
       #User inputs preferred wireless interface.
       if interface == "":
           os.system("iwconfig")
           interface = raw_input("Please enter your wireless interface: (ex. wlan0mon)\n")
       #Device is turned off and then put into monitor mode.
       os.system("airmon-ng stop " + interface)



