Hack 38. Map Your Wardriving Expeditions
Found Wi-Fi nearby? Put it on a map!
My interest in Wi-Fi is what got me started with GIS. I had been following the early 802.11 devices and was psyched to read about new modern and cheaply made wireless equipment in the newsand then actually see them on the shelves of various stores. Businesses of all sizes and millions of households started buying and installing these devices all over the place. I wondered where these things were being installed, and just how many were around.
Not long afterwards, I learned that there were people who would go out in their cars with GPSs and laptops, recording the locations of these wireless signals. These wardrivers, I learned, weren't breaking into these networks, but were comparing findings such as the funny names for some of the networks and the locations of the increasingly popular local wireless hotspots. I figured I had to try this out, and after about five minutes, I was hooked.
I was still curious about what my findings looked like. I had to figure out not only how to plot them, but also how to plot them on a map and on the Web. After a while, and with another curious person, Eric Blevins, we put together http://WiFiMaps.com.
WiFiMaps.com is a web-based geographic map of where Wi-Fi has been installed. The locations are updated by the users, who upload their wardriving scans of various areas. In turn, the site uses TIGER, Mapserver, and a host of other open source and otherwise free tools to plot street-level maps of Wi-Fi installations for those who wonder.
You too can do the same thingand not just the wondering part, but also the where part. We'll do some wardriving and use a PHP script to parse the data found, and then plot the Wi-Fi spots on Google Maps.
4.11.1. The Hack
For collecting Wi-Fi data, I generally use Netstumbler, available from http://www.netstumbler.com, which is popular among wardrivers. For free 'nix operating systems, Kismet (http://www.kismetwireless.net/) is the tool of choice, while on Mac OS X, there's both MacStumbler (http://www.macstumbler.com/) and KisMAC (http://kismac.binaervarianz.de). In this hack, we'll be exporting data from these programs in the WiScan log format, which all four programs should happily generate. WiScan is a tab-delimited text file with its own characteristics, and perhaps there are other wardriving packages that also export to this format.
So, go out for a wardrive! If you are in a densely populated area, even a 20-minute wardrive can produce thousands of points. Be safe! Don't be tempted to access any of the networks you find while wardriving unless you have permission from the owner, or you are at a public hotspot. Also, pay attention to the road while collecting datanot your laptop.
4.11.2. The Code
We have four components to mapping the data you've just collected: the HTML form, the plain and simple Google Maps code, the wacky PHP parser bits, and some interesting local data to plot. You'll need access to and permission for a web server with PHP, and to be familiar enough with those to get the code situated.
Let's start with the HTML upload form, which will allow us to send our WiScan log to the server to be mapped. Call this file index.html:
Wi-Fi Data on Google Maps
Send this file:
This is quite simple, and I may have even not included some extra functionality. This seemed to work okay, however. Then we can go to the Google Maps default JavaScript setup described in Chapter 2. I decided to add the large zoom control and the map type control, so you can switch between street maps and satellite imagery.
Map.addControl(new GLargeMapControl()); Map.addControl(new GMapTypeControl());
Next is the magical PHP code. We'll keep some sanity here by paring things down by MAC address and doing an average on the various points. This will give us a pseudocenter for placing our markers. We also use a spatial average to generate the starting map. Where you would normally put the following line in your JavaScript:
map.centerAndZoom(new GPoint(-122.141944, 37.441944), 4);
… replace it instead with the PHP code shown here:
$unique) { $divlat = $unique["lat"] / $unique["cnt"]; $divlng = $unique["lng"] / $unique["cnt"]; echo "var point = new GPoint({$divlng}, {$divlat}); "; echo "var marker = new GMarker(point); map.addOverlay(marker);"; } } ?>
Once you have these scripts in place, you will need to create a temporary directory with appropriate write permissions. You should then be able to call up your PHP script, upload a WiScan file, and have the results displayed on a Google Map, which might look like Figure 4-25.
Figure 4-25. Some results from a wardriving expedition
4.11.3. Hacking the Hack
If you're using Kismet, you can have it generate logfiles in its custom XML format and use a combination of JavaScript and XSLT to translate the log entries directly into HTML for the info window, without the need for PHP. You can see an example of this online at http://mappinghacks.com/projects/gmaps/kismet.html. Since you can view source on that page to see its inner workings, we'll just cover the juicy bits. Here's a somewhat simplified snippet of a Kismet XML log:
linksys 00:11:22:33:44:55 6 None 6.2069 -75.5629 6.2078 -75.5618
Each wireless-network element in the log contains details about each network seen, including a gps-info element that describes the area in which the network was detected. We can use an XSLT stylesheet to turn each of these entries into HTML:
Channel
Encryption:
The stylesheet identifies a log entry by looking elements in the log called wireless-network, and then outputs an HTML div element, using the values of the SSID, BSSID, channel, and encryption elements from inside each log entry. The HTML that would result from the XSLT transformation of the example log entry using this stylesheet would look something like this:
linksys
00:11:22:33:44:55
Channel 6
Encryption: None
On our map page, we fetch the Kismet log using GXmlHttp and feed it to a function called mapFeed( ). This function uses the JavaScript DOM API to find all the wireless-network entries and then looks inside each one to find the minimum latitude and longitude of the network's coverage area. For each network node with geographic information, a GPoint object is created with its coordinates, which is passed to another function, addMarker( ), to create a marker on the map for that node.
function mapFeed (xml) { var items = xml.documentElement .getElementsByTagName("wireless-network"); map.clearOverlays( ); for (var i = 0; i < items.length; i++) { var gpsinfo = items[i].getElementsByTagName("gps-info"); if (gpsinfo.length == 0) continue; var lon = gpsinfo[0].getElementsByTagName("min-lon")[0] .childNodes[0].nodeValue; var lat = gpsinfo[0].getElementsByTagName("min-lat")[0] .childNodes[0].nodeValue; var x = parseFloat(lon); var y = parseFloat(lat); if (x && x != 90 && y && y != 180) { var point = new GPoint(x, y); var marker = addMarker(point, items[i]); map.addOverlay(marker); } } }
The addMarker( ) function is almost painfully simple:
function addMarker (point, xml) { var marker = new GMarker(point); GEvent.addListener(marker, "click", function ( ) { marker.openInfoWindowXslt( xml, "kismet2marker.xsl" ); }); return marker; }
The supplied point is used to create a new GMarker, and then a click event is added to that marker, and the marker is returned. Later, when a user clicks on the marker, the event handler calls marker.openInfoWindowXslt( ), which fetches our XSLT stylesheet from the server, applies it to the DOM element containing the log entry for that wireless node, and then takes the resulting HTML and sticks it into a new info window over the marker. The result is quite elegant, because instead of having to assemble the HTML layout of the info window in JavaScript (which can get quite messy), we can use an external XSLT stylesheet to cleanly separate our logic and our presentation. If we want to add other data from the logfiles to the info window, we can do so by updating the XSLT without ever having to touch the JavaScript again.
The JavaScript in this example could stand some improvement. For example, we could fetch the maximum lat/long for each node and average them with the minimum lat/long for a more accurate position. We could also display a different marker based on the contents of the encryption field, so that we could see, at a glance, which networks were open and which were closed. We could add some logic to track the maximum and minimum coordinates of our logfile and set the map center and zoom level appropriately [Hack #58]. Finally, as wardriving logs tend to pick up lots of points quickly, we might want to look into using "Show Lots of StuffQuickly" [Hack #59] as a means of speeding up the map display.
Drew from Zhrodague