Skip to content

Instantly share code, notes, and snippets.

@ScottHelme
Last active December 13, 2018 02:59
Show Gist options
  • Save ScottHelme/5c6869e17c3e9c8b2034dc8fc13e180b to your computer and use it in GitHub Desktop.
Save ScottHelme/5c6869e17c3e9c8b2034dc8fc13e180b to your computer and use it in GitHub Desktop.
Convert XML files from Kismet into KML for Google Maps or Fusion Tables
#!/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: 13.06.2011
import os
import time
import zipfile
import xml.parsers.expat
import optparse
class WirelessNetwork:
def __init__(self,type,firsttime,lasttime):
self.type=type
self.firsttime=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="""
<Placemark><styleUrl>#%s</styleUrl>%s
<Point><coordinates>%s,%s</coordinates></Point>
<description><![CDATA[
SSID: %s<br />
MAC: %s<br />
Manuf: %s<br />
Type: %s<br />
Channel: %s<br />
Encryption: <FONT color=%s>%s</FONT><br />
Last time: %s<br />
GPS: %s,%s]]></description></Placemark>
"""
KML_FOLDER = """
<Folder>
<name>%s: %s APs</name>
<Style id="%s"><IconStyle><scale>0.5</scale>
<Icon>")
<href>http://files.salecker.org/netxml2kml/images/%s.gif</href>
</Icon></IconStyle></Style>
%s
</Folder>
"""
class netxml:
def __init__(self):
self.networks={}
self.outputname=""
self.target=None
self.disable_names = False
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 <outputname>.kml")
parser.add_option("--kmz", dest="kmz", action="store_true",
help="Create a KMZ file for Google Earth <outputname>.kmz")
parser.add_option("--disable-names", dest="names", action="store_true",
help="Disable names in KML/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 ""
if options.names is True:
self.disable_names = True
# 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):
"""<name attr="">
"""
#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):
"""</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):
"""<self.parser["laststart"]>data</self.parser["laststart"]>
"""
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"]=="manuf":
self.parser["wn"].manuf=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"]=="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("<kml xmlns='http://earth.google.com/kml/2.1'>\r\n")
target.add("<Document>\r\n")
target.add("<name>netxml2kml</name>\r\n")
target.add("<open>1</open>")
count={"WPA":0,"WEP":0,"None":0,"Other":0}
folders, route = 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(self.output_kml_route(route))
target.add("\r\n</Document>\r\n</kml>")
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"}
route = {}
for net in self.networks:
wn=self.networks[net]
if len(wn.gps)==0:
continue
essid = wn.get_from_ssid('essid').replace("<","&lt;").replace(">","&gt;").replace("&","&amp;")
if not self.disable_names:
name = "<name>%s</name>" % essid
else:
name = ""
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,name,wn.gps['avg-lon'],wn.gps['avg-lat'],
essid,wn.bssid,wn.manuf,wn.type,
wn.channel,colors[crypt],encryption,wn.lasttime,
wn.gps['avg-lat'],wn.gps['avg-lon'],
))
count[crypt]+=1
sec_first = int(time.mktime(time.strptime(wn.firsttime)))
sec_last = int(time.mktime(time.strptime(wn.lasttime)))
if sec_last - sec_first < 300:
route[sec_last] = (wn.gps['avg-lat'],wn.gps['avg-lon'])
return folders, route
def output_kml_route(self, route):
output = []
num = 1
last_second = 0
output.append("<Folder><name>Routes</name>")
for second in sorted(route):
lat, lon = route[second]
if second - last_second > 1800:
if len(output) > 1:
output.append("</coordinates></LineString><name>Route %s (end %s)</name></Placemark>\n" % (num, time.strftime("%a %b %d %H:%M:%S %Y", time.gmtime(last_second))))
num += 1
output.append("<Placemark><Style><LineStyle><color>7f00ff00</color><width>3</width></LineStyle></Style><LineString><coordinates>\n")
last_second = second
output.append("%s,%s \n" % (lon, lat))
output.append("</coordinates></LineString><name>Route %s (end %s)</name></Placemark>\n" % (num, time.strftime("%a %b %d %H:%M:%S %Y", time.gmtime(last_second))))
output.append("</Folder>")
return "".join(output)
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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment