Introduction
Wi-Fi triangulation is a method to geolocate a device by analyzing the signal strengths and MAC addresses (BSSIDs) of nearby Wi-Fi access points. It uses trilateration to estimate a device's position based on the relative distances to multiple access points, derived from signal strength data. This article explains the process of implementing Wi-Fi triangulation on a Windows system using the netsh
command-line tool, the Unwired Labs Location API for geolocation, and the Nominatim API for reverse geocoding. We'll break down each step, from scanning networks to converting coordinates into a human-readable address, and address common pitfalls in the process.
Prerequisites
To execute this process, you'll need:
- A Windows 10 or 11 system (we're focusing on Windows-specific commands).
- Administrative access to run network commands.
- An API key from Unwired Labs (unwiredlabs.com) for Wi-Fi geolocation.
- A tool like
curl
(available for Windows) to make API requests. - An email address for Nominatim API usage, per their policy.
- An active internet connection for API calls.
Step 1: Collecting Wi-Fi Data with netsh
Wi-Fi access points broadcast beacon frames containing their SSID (network name), BSSID (MAC address), and other metadata. Signal strength, reported as a percentage or in dBm, indicates how close the device is to the access point. We need to collect this data for all visible networks. On Windows, we use the netsh
utility to scan for Wi-Fi networks:
netsh wlan show networks mode=bssid
netsh
: Network Shell, a Windows utility for network management.wlan
: Targets the wireless LAN subsystem.show networks
: Lists all visible Wi-Fi networks.mode=bssid
: Includes detailed info like BSSID and signal strength.
Example Output
Interface name : Wi-Fi
There are 3 networks currently visible.
SSID 1 : CyberNet-2G
Network type : Infrastructure
Authentication : WPA2-Personal
Encryption : CCMP
BSSID 1 : aa:bb:cc:dd:ee:ff
Signal : 72%
Radio type : 802.11n
Band : 2.4 GHz
Channel : 6
SSID 2 : CyberNet-5G
Network type : Infrastructure
Authentication : WPA2-Personal
Encryption : CCMP
BSSID 1 : aa:bb:cc:dd:ee:fe
Signal : 65%
Radio type : 802.11ac
Band : 5 GHz
Channel : 48
SSID 3 : NeonGrid
Network type : Infrastructure
Authentication : WPA2-Personal
Encryption : CCMP
BSSID 1 : 11:22:33:44:55:66
Signal : 40%
Radio type : 802.11n
Band : 2.4 GHz
Channel : 1
Key data extracted:
- SSID:
CyberNet-2G
, BSSID:aa:bb:cc:dd:ee:ff
, Signal: 72% - SSID:
CyberNet-5G
, BSSID:aa:bb:cc:dd:ee:fe
, Signal: 65% - SSID:
NeonGrid
, BSSID:11:22:33:44:55:66
, Signal: 40%
Challenge: Incomplete Scans
Windows caches network lists and may only show the connected network if a fresh scan isn’t triggered. This issue is well-documented here:
https://superuser.com/questions/1446576/netsh-wlan-show-networks-only-shows-the-currently-connected-wifi-network
- Identify the interface:
netsh wlan show interfaces
- Disable the interface:
netsh interface set interface "Wi-Fi" admin=disable
- Re-enable it:
netsh interface set interface "Wi-Fi" admin=enable
- Wait 5 seconds, then re-run the scan command.
Post-Processing Data
The output is a text string that needs parsing to extract SSID, BSSID, and signal strength. For the API, we need to:
- Format the BSSID as
xx-xx-xx-xx-xx-xx
(replace colons with hyphens). - Convert signal strength from percentage to dBm using:
signal_dbm = -100 + (signal_percentage * 0.7)
Examples:
- 72% → -49.6 dBm
- 65% → -54.5 dBm
- 40% → -72.0 dBm
Processed data:
- SSID:
CyberNet-2G
, BSSID:aa-bb-cc-dd-ee-ff
, Signal: -49.6 dBm - SSID:
CyberNet-5G
, BSSID:aa-bb-cc-dd-ee-fe
, Signal: -54.5 dBm - SSID:
NeonGrid
, BSSID:11-22-33-44-55-66
, Signal: -72.0 dBm
Step 2: Geolocating with Unwired Labs API
The Unwired Labs API geolocates devices using Wi-Fi data. Endpoint:
https://us1.unwiredlabs.com/v2/process.php
Example JSON payload:
{
"token": "pk.1234567890abcdef1234567890abcdef",
"wifi": [
{ "bssid": "aa-bb-cc-dd-ee-ff", "signal": -49.6 },
{ "bssid": "aa-bb-cc-dd-ee-fe", "signal": -54.5 },
{ "bssid": "11-22-33-44-55-66", "signal": -72.0 }
],
"address": 1
}
Send it using curl:
curl -X POST -H "Content-Type: application/json" -d @payload.json https://us1.unwiredlabs.com/v2/process.php
Expected response:
{
"status": "ok",
"lat": 40.7128,
"lon": -74.0060,
"accuracy": 50
}
Failure response:
{
"status": "error",
"message": "No matches found",
"balance": 100
}
Step 3: Reverse Geocoding with Nominatim
Endpoint:
https://nominatim.openstreetmap.org/reverse?lat={latitude}&lon={longitude}&format=json
Example request:
curl -H "User-Agent: WiFiTriangulation/1.0 ([email protected])" "https://nominatim.openstreetmap.org/reverse?lat=40.7128&lon=-74.0060&format=json"
Example response:
{
"display_name": "New York, New York County, New York, United States",
"address": {
"city": "New York",
"county": "New York County",
"state": "New York",
"country": "United States"
}
}
Map link:
https://www.openstreetmap.org/?mlat=40.7128&mlon=-74.0060#map=15/40.7128/-74.0060
Final thought
Wi-Fi triangulation isn’t locked to just one tool or API. We used Unwired Labs here mainly because it's free and simple to work with, but there are plenty of other services out there that do the same thing. You can swap in different APIs, use custom tools, and even automate the whole process if you want. It’s pretty lightweight, and once you get the basics down, it’s easy to build on or tweak for your own use cases.