Displaying Kismet Data in Google Earth
Swathe — Sat, 2010-06-26 21:20
DISCLAIMER: The Author in no way advocates the cracking of WiFi encryption or connecting to private networks without permission. Mapping of wireless access points does not violate any laws, though I advise people to verify this in their particular location.
Kismet is a readily available application that in it's most basic form allow you to scan your surrounding area for WiFi access points. More information on Kismet and it's function can be found at http://www.kismetwireless.net/
What makes Kismet really interesting is that when used to scan on a GPS enable device, it will also log GPS co-ordinates of every access point it detects. This data, once converted, can be opened using Google earth to overlay your detected access points onto Google's satellite imagery. The Data can include:
1. SSID
2. BSSID/MAC
3. Type
4. Channel
5. Encryption
6. GPS Coordinates
I have tested this on my N900 and can confirm that it does indeed work. Accuracy can be affected by the speed of your travel whilst scanning and GPS signal. In order to view this data there are a couple of steps.
1. Kismet will create a log file with the .netxml extension. This is the file that contains all the data we need for Google Earth.
2. I use a python script written by Patrick Salecker to convert my logfile. Copy the text from the code below into your favourite text editor and save it as netxml2kml.py
#!/usr/bin/env python
# coding=utf-8
#
# Converts netxml files from Kismet Newcore into KML or KMZ files for Google Earth
#
# Author: Patrick Salecker
# URL: http://www.salecker.org/software/netxml2kml/en
# Last modified: 20.12.2009
import os
import time
import zipfile
import xml.parsers.expat
import optparse
class WirelessNetwork:
def __init__(self,type,firsttime,lasttime):
self.type=type
self.fisttime=firsttime
self.lasttime=lasttime
self.bssid=""
self.manuf=""
self.ssid=[]
self.freqmhz={}
self.maxrate=0
self.maxseenrate=0
self.packets={}
self.snr={} # Signal-to-noise ratio
self.datasize=0
self.channel=0
self.carrier=""
self.bsstimestamp=0
self.gps={}
self.ipaddress={}
def get_from_ssid(self,key):
result=[]
for ssid in self.ssid:
if key in ssid and ssid[key]!="":
if type(ssid[key])!=type({}):
return ssid[key]
else:
for bla in ssid[key]:
if bla not in result:
result+=[bla,]
if len(result)>0:
return result
else:
return ""
def update(self,new):
"""Update a network
Compare a existing network with a new and update the existing
"""
if len(self.gps)==0 and len(new.gps)>0:
self.gps=new.gps
return True
KML_PLACEMARK="""
#%s
%s,%s
MAC: %s
Type: %s
Channel: %s
Encryption: %s
Last time: %s]]>
"""
KML_FOLDER = """
%s: %s APs
0.5
")
http://bla.wsf23.net/img/wd/%s.gif
%s
"""
class netxml:
def __init__(self):
self.networks={}
self.outputname=""
self.target=None
def main(self):
usage=self.main_usage()
parser = optparse.OptionParser(usage)
parser.add_option("-o", dest="outputname",
help="Filename without extension")
parser.add_option("--kml", dest="kml", action="store_true",
help="Create a KML file for Google Earth .kml")
parser.add_option("--kmz", dest="kmz", action="store_true",
help="Create a KMZ file for Google Earth .kmz")
(options, args) = parser.parse_args()
# Input
if len(args)>0:
for filename in args:
if os.path.isdir(filename):
self.parse_dir(filename)
elif os.path.isfile(filename):
self.parse(filename)
else:
print "Invalid name: %s"%filename
if options.outputname==None:
print "Output name not defined, try '-h'"
else:
self.outputname=options.outputname
print "Outputfile: %s.*" % self.outputname
print ""
# Output
if len(self.networks)>0:
if self.outputname!="":
if options.kml is True:
self.output_kml()
if options.kmz is True:
self.output_kml(kmz=True)
else:
print "No networks"
def main_usage(self):
return """
python netxml [options] [file1] [file2] [dir1] [dir2] [...]
./netxml [options] [dir1] [dir2] [file1] [file2] [...]
Example:
python netxml.py --kmz --kml -o today somefile.netxml /mydir"""
def parse_dir(self,parsedir):
"""Parse all files in a directory
"""
print "Parse .netxml files in Directory:",parsedir
starttime=time.time()
files=0
if not parsedir.endswith(os.sep):
parsedir+=os.sep
for filename in os.listdir(parsedir):
if os.path.splitext(filename)[1]==".netxml":
self.parse(parsedir + filename)
files+=1
print "Directory done, %s sec, %s files" % (
round(time.time()-starttime,2),files)
def parse(self,filename):
"""Parse a netxml file generated by Kismet Newcore
"""
self.parser={
"update":0,
"new":0,
"laststart":"",
"parents":[],
"wn":None,
"ssid":None
}
p = xml.parsers.expat.ParserCreate()
p.buffer_text=True #avoid chunked data
p.returns_unicode=False #disabled Unicode support is much faster
p.StartElementHandler = self.parse_start_element
p.EndElementHandler = self.parse_end_element
p.CharacterDataHandler = self.parse_char_data
if os.path.isfile(filename):
p.ParseFile(open(filename))
else:
print "Parser: filename is not a file:" % filename
print "Parser: %s, %s new, %s old" % (
filename,self.parser["new"],self.parser["update"])
def parse_start_element(self,name, attrs):
"""
"""
#print 'Start element:', name, attrs
if name=="wireless-network":
self.parser["wn"]=WirelessNetwork(
attrs["type"],
attrs["first-time"],
attrs["last-time"])
elif name=="essid" and 'cloaked' in attrs:
self.parser["ssid"]['cloaked']=attrs['cloaked']
elif name=="SSID":
self.parser["ssid"]={"encryption":{}}
self.parser["parents"].insert(0,self.parser["laststart"])
self.parser["laststart"]=name
def parse_end_element(self,name):
"""
"""
#print 'End element:', name
if name=="wireless-network":
if self.parser["wn"].bssid in self.networks:
self.networks[self.parser["wn"].bssid].update(self.parser["wn"])
self.parser["update"]+=1
else:
self.networks[self.parser["wn"].bssid]=self.parser["wn"]
self.parser["new"]+=1
elif name=="SSID":
if len(self.parser["ssid"])>0 and "type" in self.parser["ssid"]:
if self.parser["parents"][0]=="wireless-network":
self.parser["wn"].ssid.append(self.parser["ssid"])
del self.parser["ssid"]
self.parser["laststart"]=self.parser["parents"].pop(0)
def parse_char_data(self,data):
"""data
"""
if data.strip()=="":
return
if self.parser["parents"][0]=="SSID":
if self.parser["laststart"]=="encryption":
self.parser["ssid"]["encryption"][data]=True
elif self.parser["laststart"] in("type","ssid","essid","max-rate","packets","beaconrate","info"):
self.parser["ssid"][self.parser["laststart"]]=data
elif self.parser["parents"][1]=="wireless-network":
if self.parser["parents"][0]=="gps-info":
self.parser["wn"].gps[self.parser["laststart"]]=float(data)
"""elif self.parser["parents"][0]=="packets":
self.parser["wn"].packets[self.parser["laststart"]]=data
elif self.parser["parents"][0]=="snr-info":
self.parser["wn"].snr[self.parser["laststart"]]=data
elif self.parser["parents"][0]=="ip-address":
self.parser["wn"].ipaddress[self.parser["laststart"]]=data"""
elif self.parser["parents"][0]=="wireless-network":
if self.parser["laststart"]=="BSSID":
self.parser["wn"].bssid=data
elif self.parser["laststart"]=="channel":
self.parser["wn"].channel=int(data)
"""elif self.parser["laststart"]=="freqmhz":
self.parser["wn"].freqmhz[data]=True
elif self.parser["laststart"]=="carrier":
self.parser["wn"].carrier=data
elif self.parser["laststart"]=="maxseenrate":
self.parser["wn"].maxseenrate=data
elif self.parser["laststart"]=="manuf":
self.parser["wn"].manuf=data
elif self.parser["laststart"]=="bsstimestamp":
self.parser["wn"].bsstimestamp=data
elif self.parser["laststart"]=="datasize":
self.parser["wn"].datasize=data"""
def output_kml(self,kmz=False):
"""Output KML for Google Earth
"""
print "%s export..." % ("KML" if not kmz else "KMZ")
#starttime=time.time()
if kmz is True:
target=CreateKMZ(self.outputname)
else:
target=CreateKML(self.outputname)
target.add("<?xml version='1.0' encoding='UTF-8'?>\r\n")
target.add("\r\n")
target.add("\r\n")
target.add("netxml2kml\r\n")
target.add("1")
count={"WPA":0,"WEP":0,"None":0,"Other":0}
folders=self.output_kml_fill_folders(count)
for crypt in ("WPA","WEP","None","Other"):
if crypt=="WPA":
pic="WPA"
elif crypt=="WEP":
pic="WEP"
else:
pic="Open"
target.add(KML_FOLDER %(
crypt,
count[crypt],
crypt,
pic,
"".join(folders[crypt])
))
print "%s\t%s" % (crypt,count[crypt])
target.add("\r\n\r\n")
target.close()
print "Done. %s networks" % sum(count.values())
#round(time.time()-starttime,2)
def output_kml_fill_folders(self,count):
folders={"WPA":[],"WEP":[],"None":[],"Other":[]}
colors={"WPA":"red","WEP":"orange","None":"green","Other":"grey"}
for net in self.networks:
wn=self.networks[net]
if len(wn.gps)==0:
continue
encryption=wn.get_from_ssid('encryption')
crypt=self.categorize_encryption(encryption)
if len(encryption)!=0:
encryption.sort(reverse=True)
encryption=" ".join(encryption)
folders[crypt].append(KML_PLACEMARK %(
crypt,wn.gps['avg-lon'],wn.gps['avg-lat'],
wn.get_from_ssid('essid'),wn.bssid,wn.type,
wn.channel,colors[crypt],encryption,wn.lasttime
))
count[crypt]+=1
return folders
def categorize_encryption(self,encryption):
for c in encryption:
if c.startswith("WPA"):
return "WPA"
if "WEP" in encryption:
return "WEP"
elif "None" in encryption:
return "None"
else:
return "Other"
class CreateKML:
"""Write the KML data direct into a file
"""
def __init__(self,outputname):
self.file=open("%s.kml" % outputname, 'w')
def add(self,data):
self.file.write(data)
def close(self):
self.file.close()
class CreateKMZ:
"""Store the KML data in a list and write it into a zipfile in close()
"""
def __init__(self,outputname):
self.data=[]
self.zip=zipfile.ZipFile("%s.kmz" % outputname, "w")
def add(self,data):
self.data.append(data)
def close(self):
zinfo = zipfile.ZipInfo("netxml2kml.kml")
zinfo.compress_type = zipfile.ZIP_DEFLATED
self.zip.writestr(zinfo,"".join(self.data))
self.zip.close()
if __name__ == "__main__":
converter=netxml()
converter.main()
3.Make the script executable by navigating to the folder in which you have stored the script and in the terminal use the command:
sudo chmod -x netxml2kml.py
4. Now to make things simple, I copy our log file into the same directory as the Python script. You can then enter the following command:
python netxml.py --kmz --kml -o FILENAME somefile.netxml /mydir
This will create a FILENAME.kmz and a FILENAME.kml file in the specified directory. Either of these files can be loaded into Google Eart by choosing Open from the file menu.
KMZ files can be a combination of multiple KML files, and this article will get updated in the future once I am happy with a solution for doing so.
Further information on the Python script used can be located at Patrick Saleckers website at http://www.salecker.org/software/netxml2kml/en
Patrick also supplies a sample .netxml file for you to try out with his script.
Feel free to contact me in IRC or via the mailing list for any questions.
Enjoy!