<?php

/* 
    Kismet to kml (Google Earth) log file converter alias "Kismet Earth"
    Released by Philippe Niquille, philippe at niquille dot com, www.niquille.com
        
    ******************************
    *   version 0.1 - 24.9.2005
    ******************************
        
    features
    --------
    parses Kismet log files (.xml and .gps) to generate a google earth kml file
    - draws WarDrive Path as tesselated line
    - adds discovered access points to specific location on the map, parses meta information (mac, channel, packets, clients, etc.)
    - draws 3D polygons instead of simple AP images. The shape should represent the signal area as far as every corner has been
      captured correctly. The height depends on the channel. The color is yellow (open), green (closed) or blue when open and cloaked.
        
    requirements
    ------------
    PHP5 because of simple_xml functions
        
    changelog
    ---------
    v0.1 - initial release
        
    todo/wishlist
    -------------
    - parse default password list, vendor tags (MAC's) and add to description
    - save as kmZ
    - merge multiple log files
    - get better icons
    - better polygons...
*/

//execution time
$mtime explode(" ",microtime());
$mtime $mtime[1] + $mtime[0];
$starttime $mtime;

if(isset(
$argv[1]))//get filenames
{
    
$xml_file $argv[1].'.xml';
    
$gps_file $argv[1].'.gps';
}else
    exit(
"No argument specified\n");
    
if(
file_exists($xml_file) && file_exists($gps_file))
{
    
$xml simplexml_load_file($xml_file); //load kismet log
    
$gps simplexml_load_file($gps_file); //load corresponding detailed gps log
}else
    exit(
"Files couldn't be opened: ".$xml_file.", ".$gps_file."\n");


$array object2array($xml);
$array2 object2array($gps);

/*******************
*   get first xml tag's attributes
********************/

$meta['session-duration'] = strtotime($xml['end-time'])-strtotime($xml['start-time']); //get duration (unix timestamp, in seconds)
$meta['session-duration'] = floor($meta['session-duration']/3600).' h, '.floor($meta['session-duration']/60).' min'//format

$meta['net-count'] = count($array['wireless-network']);
$meta['parsed'] = $meta['net-count'];

/*******************
*   loop trough kismet wireless nets - types: infrastructure, probe
********************/

foreach($array['wireless-network'] as $key => $network)
{

    if(
    (
$network['gps-info']['min-lat'] == '90.000000' && $network['gps-info']['min-lon'] == '180.000000'//remove nets with no gps coordinates, no sense to display them on a map
    
|| $network['BSSID'] == '00:00:00:00:00:00'
    
|| $xml->{'wireless-network'}[$key]['type'] != 'infrastructure'//remove zero bssid nets, just junk
    
{
        if(
$xml->{'wireless-network'}[$key]['type'] == 'probe'//count probes
            
$meta['probe-count']++;
        
        
$meta['net-count']--; //remove non infrastructure from total net count, exit foreach
        
continue;
       
    }elseif(!isset(
$lookatnet)) //get the first usable net to set LookAt coordinates
        
$lookatnet $key;
        
    
$kml .= "<Placemark>\n<name>";
    if(empty(
$network['SSID'])) $kml .= "no ssid";
    else 
$kml .= $network['SSID'];
    
$kml .= "</name>\n";
    
    
/*******************
    *   build description html
    ********************/
    
$seenfor strtotime($xml->{'wireless-network'}[$key]['last-time'])-strtotime($xml->{'wireless-network'}[$key]['first-time']);
    
    
$kml .= "\t<description><![CDATA[seen for ".floor($seenfor/60)." min (".floor($seenfor)." sec)<br>"//get duration
    
$kml .= "first seen: ".$xml->{'wireless-network'}[$key]['first-time']."<br>last seen: ".$xml->{'wireless-network'}[$key]['last-time']."<br><hr>\n";
    
$kml .= "BSSID: ".$network['BSSID']."<br>\nchannel: ".$network['channel']."<br>\n";
  
    if(
$xml->{'wireless-network'}[$key]['wep'] == 'true'//net encrypton on?
    
{
        if(
$network['encryption'] == 'WEP')
            
$kml .= "<font color=\"green\">encryption: WEP</font>";
            
        elseif(
is_array($network['encryption']))
        {
            
$kml .= "<font color=\"green\">encryption: ";

            foreach(
$network['encryption'] as $enc)
            {
               
$kml .= $enc." ";
            }
            
$kml .= "</font>";
        }
    }else
        
$kml .= "<font color=\"red\">no encryption</font>";
    
    
$kml .= "<br>\ncloaked: ".$xml->{'wireless-network'}['cloaked'];
    
$kml .= "<br>\nmax rate: ".$xml->{'wireless-network'}['maxrate'];
    
    if(isset(
$network['ip-address']))
        
$kml .= "<br>IP range: ".$network['ip-address']['ip-range'];
    
    
$kml .= "<hr>\n<b>GPS coordinates</b><br><br>\nfirst-seen: ".$network['gps-info']['min-lat'].", ".$network['gps-info']['min-lon'].", ".$network['gps-info']['min-alt'];
    
$kml .= "\n<br>last-seen: ".$network['gps-info']['max-lat'].", ".$network['gps-info']['max-lon'].", ".$network['gps-info']['max-alt'];
    
$kml .= "<hr>\n<b>captured packets</b><br><br>\n";
    
    foreach(
$network['packets'] as $tag => $value//list the packets section (IV's, LLC, Data, etc.)
    
{
        
$kml .= $tag.": ".$value."<br>\n";
    }
    
$kml .= "total datasize captured: ".$network['datasize']."\n";
    
    
/*******************
    *   parse clients, if existant - types: fromds, tods
    ********************/

    
if(isset($network['wireless-client']))
    {
    
$kml .="<hr>\n<b>captured attached clients</b><br><br>\n";
    
$kml .= "number of clients: ".count($network['wireless-client'])."\n<br><ul>";
        foreach(
$network['wireless-client'] as $client//list all attached and captured clients
        
{
        
            
$kml .= "<li>MAC: ".$client['client-mac']."<br>IP: ";
            if(isset(
$client['client-ip-address'])) //IP discovered?
                
$kml .= $client['client-ip-address'];
            else
               
$kml .= "none discovered";
           
$kml .= "</li>\n";
        }
    }
    
    
$kml .= "</ul>]]>\n</description>\n\t<View>\n\t<longitude>".$network['gps-info']['min-lon']."</longitude>\n";
    
$kml .= "\t<latitude>".$network['gps-info']['min-lat']."</latitude>\n</View>\n";
    
    
$kml .= "\t<visibility>1</visibility>\n\t<styleUrl>root://styleMaps#default?iconId=0x307</styleUrl>\n";
    
    if(
$xml->{'wireless-network'}[$key]['wep'] == 'true'//net encrypton on?
    
{
        
$kml .= "\t<Style>\t<icon>http://www.niquille.com/wp-content/node_closed.png</icon></Style>";
        
$meta['wep-count']++;
    }else
        
$kml .= "\t<Style>\t<icon>http://www.niquille.com/wp-content/node_open.png</icon></Style>";

    
$kml .= "\t<Point>\n\t<coordinates>".$network['gps-info']['min-lon'].", ".$network['gps-info']['min-lat'].", ".$network['gps-info']['min-alt']."</coordinates>\n</Point>\n</Placemark>\n";
    
    
//add gps coordinates to path source
    
$line .= $network['gps-info']['min-lon'].", ".$network['gps-info']['min-lat'].", ".$network['gps-info']['min-alt']." ";
    
    
/*******************
    *   assemble polygon placemark
    ********************/
    
if($network['channel'] != '0')
    {
    
    
$poly .= "<Placemark>\n<name>".$network['BSSID']."</name>\n<visibility>0</visibility>\n<open>0</open>\n";
    
$poly .= "<Style>\n\t<LineStyle>\n\t<width>1.5</width></LineStyle>\n\t<PolyStyle>";
    
    if(
$xml->{'wireless-network'}[$key]['wep'] == 'true'//set color 
        
$poly .= "<color>8f00ff00</color>\n"//green, closed
    
elseif($meta[$key]['cloaked'] == 'true')
        
$poly .= "<color>7dff0000</color>\n"//blue, cloaked
    
else
        
$poly .= "<color>7d00ffff</color>\n"//yellow, open
    
    
$poly .= "</PolyStyle>\n</Style>\n<Polygon>\n<extrude>1</extrude>\n<tessellate>0</tessellate>\n";
    
$poly .= "<altitudeMode>relativeToGround</altitudeMode>\n<outerBoundaryIs>\n<LinearRing>\n<extrude>0</extrude>";
    
$poly .= "<tessellate>0</tessellate>\n<altitudeMode>clampToGround</altitudeMode>\n<coordinates>";

    
$poly_alt $network['channel']*10;
    
    unset(
$firstcoordinate);
    for(
$i=0;$i<count($array2['gps-point']);$i++) //loop trough .gps file, get every coordinate at specific time for polygon drawing
    
{
        if(
$network['BSSID'] == $gps->{'gps-point'}[$i]['bssid'])
        {
            if(!isset(
$firstcoordinate))
                
$firstcoordinate $i;
                
        
//not just duplicate rows?
           /*if($gps->{'gps-point'}[$i-1]['lat'] != $gps->{'gps-point'}[$i]['lat']
           && $gps->{'gps-point'}[$i-1]['lon'] != $gps->{'gps-point'}[$i]['lon'])*/

                
$poly .= $gps->{'gps-point'}[$i]['lon'].", ".$gps->{'gps-point'}[$i]['lat'].", ".$poly_alt." ";
        }
    }
    
    
$poly .= $gps->{'gps-point'}[$firstcoordinate]['lon'].", ".$gps->{'gps-point'}[$firstcoordinate]['lat'].", ".$poly_alt." "//finish polygon at first coordinate
    
$poly .= "</coordinates>\n</LinearRing>\n</outerBoundaryIs>\n</Polygon>\n</Placemark>";
    
    }
//end foreach

/*******************
*   assemble final KML xml file
********************/

$meta['wep-count'] = ($meta['wep-count']/$meta['net-count'])*100//encrypted percentage of total nets

$kml_final "<?xml version='1.0' encoding='UTF-8'?>\n<kml xmlns='http://earth.google.com/kml/2.0'>\n<Folder>\n<name>";
$kml_final .= $meta['session-duration'].', '.$meta['net-count'].' nets, '.$meta['probe-count'].' probes ('.$meta['parsed'].' total parsed) - '.date('d.m.Y',strtotime($xml['start-time']));
$kml_final .= "</name>\n<visibility>1</visibility><description>encrypted: ".round($meta['wep-count'],2)."%</description>\n";

//fly to first network
//range: zoom level, tilt: view angle
$kml_final .= "<LookAt>\n\t<longitude>".$array['wireless-network'][$lookatnet]['gps-info']['min-lon']."</longitude>\n\t<latitude>".$array['wireless-network'][$lookatnet]['gps-info']['min-lat']."</latitude>";
$kml_final .= "<range>1000</range><tilt>54</tilt><heading>-35</heading></LookAt>";

// output the WarDrive Path in a separate placemark
$kml_final .= "\n<Placemark>\n<name>WarSession Path</name>\n<description></description>\n<visibility>0</visibility>\n";
$kml_final .= "\t<Style>\n\t<geomColor>BB0000FF</geomColor>\n<geomScale>3</geomScale>\n</Style>\n\t<LineString>";
$kml_final .= "\n\t<tessellate>1</tessellate>\n\t<coordinates>$line</coordinates>\n</LineString>\n</Placemark>\n"//tesselate adjusts to terrain

//output 3D visualized polygons
$kml_final .= "<Folder>\n<name>3D visualized view</name><open>0</open>\n";
$kml_final .= $poly;
$kml_final .= "</Folder>\n";

$kml_final .= $kml;

$kml_final .= "</Folder>\n</kml>";

//$kml_final = simplexml_load_string($kml_final); //check kml syntax (xml), fails because of <br>, <li> in description tag
//$kml_final->asXML('new.kml');

writeXML($argv[1].".kml",$kml_final);

//execution time
$mtime explode(" ",microtime());
$mtime $mtime[1] + $mtime[0];
$endtime $mtime;
$totaltime = ($endtime $starttime);
echo 
"Parsed in ".round($totaltime,2)." seconds\n";


function 
writeXML($filename$kml)
{
   
//open xml file for writing
   
$handle fopen($filename,'w+');

   
//write header to xml file
  // fwrite($handle, header("Content-type: text/xml"));
  
   
fwrite($handle$kml);
   
   
fclose($handle);
}


/*******************
*   pulled from php.net
********************/
function object2array($object)
{
   
$return NULL;
      
   if(
is_array($object))
   {
       foreach(
$object as $key => $value)
           
$return[$key] = object2array($value);
   }
   else
   {
       
$var get_object_vars($object);
          
       if(
$var)
       {
           foreach(
$var as $key => $value)
               
$return[$key] = ($key && !$value) ? NULL object2array($value);
       }
       else return 
$object;
   }

   return 
$return;
}

?>