Einmal musste ich Wi-Fi-Netzwerkanwendungen von Android aus scannen und eine detaillierte Berechnung der Daten über Access Points erhalten.
Hier hatte ich mit mehreren Schwierigkeiten zu kämpfen: In der
Off-Dokumentation von Android wurden viele der beschriebenen Klassen veraltet (API-Level> 26), was sich darin nicht widerspiegelte; Die Beschreibung einiger Dinge in der Dokumentation ist minimal (zum Beispiel wurde das Funktionsfeld der
ScanResult- Klasse zum Zeitpunkt des Schreibens in keiner Weise beschrieben, obwohl es viele wichtige Daten enthält). Die dritte Schwierigkeit kann darin bestehen, dass Sie, wenn Sie sich zum ersten Mal dem WLAN nähern, das sich vom Lesen der Theorie und dem Einrichten des Routers für localhost unterscheidet, mit einer Reihe von Abkürzungen umgehen müssen, die separat verständlich erscheinen. Es ist jedoch möglicherweise nicht offensichtlich, wie sie in Beziehung gesetzt und strukturiert werden sollen (die Beurteilung ist subjektiv und hängt von früheren Erfahrungen ab).
In diesem Artikel wird erläutert, wie Sie umfassende Daten zu Wi-Fi-Umgebungen aus Android-Code ohne NDK und Hacks abrufen, jedoch nur mithilfe der Android-API, und wie Sie diese interpretieren.
Wir werden nicht ziehen und anfangen, Code zu schreiben.
1. Erstellen Sie ein Projekt
Der Hinweis richtet sich an Personen, die das Android-Projekt mehrmals erstellt haben. Daher lassen wir die Details dieses Elements weg. Der folgende Code wird in der Kotlin-Sprache minSdkVersion = 23 angezeigt.
2. Zugriffsberechtigungen
Um mit Wi-Fi in der Anwendung arbeiten zu können, müssen Sie vom Benutzer mehrere Berechtigungen einholen. Gemäß der
Dokumentation benötigen Sie zum Scannen des Netzwerks auf Geräten mit Betriebssystemversionen nach 8.0 zusätzlich zum Zugriff auf den Status der Netzwerkumgebung entweder Zugriff, um den Status des Wi-Fi-Moduls des Geräts zu ändern, oder Zugriff auf Koordinaten (ungefähr oder genau). Ab Version 9.0 müssen Sie den Benutzer für beides und das anfordern und den Benutzer ausdrücklich auffordern, den Ortungsdienst zu aktivieren. Vergessen Sie nicht, dem Nutzer galant zu erklären, dass dies eine Laune von Google ist und nicht unser Wunsch, ihn auszuspionieren :)
Insgesamt in AndroidManifest.xml hinzufügen:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Und im Code, der einen Link zur aktuellen Aktivität enthält:
import android.app.Activity import android.content.Context import android.location.LocationManager import androidx.core.app.ActivityCompat .... if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { ActivityCompat.requestPermissions( activity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CHANGE_WIFI_STATE), 1 ) makeEnableLocationServices(activity.applicationContext) } else { ActivityCompat.requestPermissions( activity, arrayOf(Manifest.permission.CHANGE_WIFI_STATE), 1 ) } fun makeEnableLocationServices(context: Context) {
3. Erstellen Sie BroadcastReceiver und abonnieren Sie Aktualisierungsereignisse beim Scannen der Wi-Fi-Netzwerkumgebung
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager val wifiScanReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false) if (success) { scanSuccess() } } } val intentFilter = IntentFilter() intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) context.registerReceiver(wifiScanReceiver, intentFilter) val success = wifiManager.startScan() if (!success) { } .... private fun scanSuccess() { val results: List<ScanResult> = wifiManager.scanResults }
Die WiFiManager.startScan-Methode in der Dokumentation ist in API-Version 28 als nicht verfügbar markiert, jedoch deaktiviert.
Anleitung schlägt vor, es zu verwenden.
Insgesamt haben wir eine Liste der
ScanResult- Objekte erhalten.
4. Wir schauen uns ScanResult an und verstehen die Begriffe
Schauen wir uns einige Felder dieser Klasse an und beschreiben, was sie bedeuten:
SSID - Service Set Identifier ist der Name des Netzwerks.
BSSID - Basic Service Set Identifier - MAC-Adresse des Netzwerkadapters (Wi-Fi-Punkt)
Pegel - Anzeige der Stärke des empfangenen Signals [dBm (russisch dBm) - Dezibel, Referenzleistung 1 mW.] - Anzeige des Pegels des empfangenen Signals. Es nimmt einen Wert von 0 bis -100 an. Je weiter von 0 entfernt, desto mehr Signalleistung geht auf dem Weg vom Wi-Fi-Punkt zu Ihrem Gerät verloren. Weitere Details finden Sie beispielsweise auf
Wikipedia . Hier
möchte ich Ihnen sagen, dass Sie mit dem
WifiManager der Android-Klasse den Signalpegel mit dem von Ihnen ausgewählten Schritt auf einer Skala von ausgezeichnet bis schrecklich kalibrieren können:
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager val numberOfLevels = 5 val level = WifiManager.calculateSignalLevel(level, numberOfLevels)
Frequenz - Die Frequenz des Wi-Fi-Punkts [Hz]. Neben der Frequenz selbst könnte Sie auch der sogenannte Kanal interessieren. Jeder Punkt hat seine eigene Sauberkeit. Zum Zeitpunkt des Schreibens beträgt der beliebteste Bereich von Wi-Fi-Punkten 2,4 GHz. Genauer gesagt überträgt der Punkt Informationen mit einer nummerierten Frequenz nahe der genannten auf Ihr Telefon. Die Anzahl der Kanäle und die Werte der entsprechenden Frequenzen sind
standardisiert . Dies geschieht so, dass nahegelegene Punkte mit unterschiedlichen Frequenzen arbeiten, wodurch sie sich nicht gegenseitig stören und die Geschwindigkeit und Qualität der Übertragung nicht gegenseitig verringern. Gleichzeitig arbeiten die Punkte nicht auf derselben Frequenz, sondern auf dem Frequenzbereich (
Parameter channelWidth), der als Kanalbreite bezeichnet wird. Das heißt, Punkte, die auf benachbarten Kanälen (und nicht nur auf benachbarten Kanälen, sondern sogar auf 3 von sich selbst) arbeiten, erzeugen Interferenzen miteinander. Dieser einfache Code kann für Sie nützlich sein, mit dem Sie die Kanalnummer anhand des Frequenzwerts für Punkte mit einer Frequenz von 2,4 und 5 GHz berechnen können:
val channel: Int get() { return if (frequency in 2412..2484) { (frequency - 2412) / 5 + 1 } else if (frequency in 5170..5825) { (frequency - 5170) / 5 + 34 } else { -1 } }
Fähigkeiten sind das interessanteste Feld für die Analyse, dessen Arbeit viel Zeit in Anspruch nahm. Hier werden die "Fähigkeiten" des Punktes in die Linie geschrieben. In diesem Fall können Details zur Interpretation der Zeichenfolge in der Dokumentation nicht gesucht werden. Hier sind einige Beispiele dafür, was in dieser Zeile liegen könnte:
[WPA-PSK-TKIP+CCMP][WPA2-PSK-TKIP+CCMP][WPS][ESS] [WPA2-PSK-CCMP][ESS] [WPA2-PSK-CCMP+TKIP][ESS] [WPA-PSK-CCMP+TKIP][WPA2-PSK-CCMP+TKIP][ESS] [ESS][WPS]
5. Abkürzungen und Parsim-Funktionen verstehen
Es ist erwähnenswert, dass die Klassen des Pakets android.net.wifi. * Verwendet das Linux-Dienstprogramm
wpa_supplicant unter der Haube, und die Ausgabe im Feld "Funktionen" ist eine Kopie des Felds "Flags" während des Scannens.
Wir werden nacheinander handeln. Betrachten Sie zunächst die Ausgabe eines Formats, in dem Elemente in den Klammern durch ein "-" - Zeichen getrennt sind:
[WPA-PSK-TKIP+CCMP] [WPA2-PSK-CCMP]
Der erste Wert beschreibt das sogenannte
Authentifizierungsmethode Das heißt, welche Abfolge von Aktionen vom Gerät und vom Zugriffspunkt ausgeführt werden muss, damit der Zugriffspunkt selbst verwendet werden kann und wie die Nutzdaten verschlüsselt werden. Zum Zeitpunkt des Schreibens dieses Beitrags sind die häufigsten Optionen WPA und WPA2, bei denen entweder jedes Gerät direkt oder über das sogenannte angeschlossen ist. Der RADIUS-Server (WPA-Enterprice) stellt ein Kennwort über einen verschlüsselten Kanal bereit. Höchstwahrscheinlich stellt der Zugangspunkt bei Ihnen zu Hause eine Verbindung gemäß diesem Schema her. Der Unterschied zwischen der zweiten und der ersten Version in einer robusteren Verschlüsselung: AES gegen unsicheres TKIP. WPA3, das komplexer und fortschrittlicher ist, wird ebenfalls schrittweise eingeführt. Theoretisch gibt es möglicherweise eine Option für die CCKM-Unternehmenslösung (Cisco Centralized Key Managment), die ich jedoch noch nie getroffen habe.
Der Zugriffspunkt kann für die Authentifizierung anhand der MAC-Adresse konfiguriert werden. Wenn der Access Point Daten mithilfe des veralteten WEP-Algorithmus bereitstellt, erfolgt praktisch keine Authentifizierung (der geheime Schlüssel ist hier der Verschlüsselungsschlüssel). Diese Optionen werden als SONSTIGES klassifiziert.
Es gibt auch eine bevorzugte Methode im öffentlichen WLAN mit versteckter Captive Portal-Erkennung - eine Anforderung zur Authentifizierung über einen Browser. Solche Zugangspunkte scheinen für den Scanner offen zu sein (was sie aus Sicht der physischen Verbindung sind). Daher klassifizieren wir sie als OFFEN.
Der zweite Wert kann als
Schlüsselverwaltungsalgorithmus festgelegt werden . Es ist ein Parameter der oben beschriebenen Authentifizierungsmethode. Spricht darüber, wie genau der Austausch von Verschlüsselungsschlüsseln erfolgt. Betrachten wir die möglichen Optionen. EAP - wird im oben genannten WPA-Unternehmenspreis verwendet und verwendet eine Datenbank, um die eingegebenen Authentifizierungsdaten zu überprüfen. SAE - wird in fortgeschrittenem WPA3 verwendet und ist widerstandsfähiger gegen Busting. PSK ist die häufigste Option. Dabei wird das Kennwort eingegeben und verschlüsselt übertragen. IEEE8021X - gemäß dem internationalen Standard (außer von der WPA-Familie unterstützt). OWE (Opportunistic Wireless Encryption) ist eine Erweiterung des IEEE 802.11-Standards für die Punkte, die wir als OPEN klassifiziert haben. OWE gewährleistet die Sicherheit von Daten, die über ein unsicheres Netzwerk übertragen werden, indem es diese verschlüsselt. Eine Variante ist auch möglich, wenn keine Zugriffsschlüssel vorhanden sind. Nennen wir diese Option NONE.
Der dritte Parameter ist der sogenannte
Verschlüsselungsmethode (Verschlüsselungsschemata) - Wie genau wird die Verschlüsselung zum Schutz der übertragenen Daten verwendet? Wir listen die Optionen auf. WEP - verwendet eine RC4-Stream-Verschlüsselung, der geheime Schlüssel ist ein Verschlüsselungsschlüssel, der in der Welt der modernen Kryptographie als inakzeptabel angesehen wird. TKIP - verwendet in WPA, CKIP - in WPA2. TKIP + CKIP - kann aus Gründen der Abwärtskompatibilität an WPA- und WPA2-fähigen Punkten angegeben werden.
Anstelle von drei Elementen finden Sie eine einzelne WEP-Markierung:
[WEP]
Wie oben erläutert, reicht dies aus, um den Algorithmus für die Verwendung von nicht vorhandenen Schlüsseln und die standardmäßig verwendete Verschlüsselungsmethode nicht anzugeben.
Betrachten Sie nun diese Klammer:
[ESS]
Dies ist
der Wi-Fi-Betriebsmodus oder die
Topologie von Wi-Fi-Netzwerken . Möglicherweise tritt der BSS-Modus (Basic Service Set) auf, wenn es einen Zugangspunkt gibt, über den verbundene Geräte kommunizieren. Kann in lokalen Netzwerken gefunden werden. In der Regel werden Zugriffspunkte benötigt, um Geräte aus verschiedenen lokalen Netzwerken zu verbinden. Sie sind daher Teil der ESS (Extended Service Sets). Der Typ der IBSSs (Independent Basic Service Sets) gibt an, dass das Gerät Teil eines Peer-to-Peer-Netzwerks ist.
Die WPS-Flagge kann auch fallen:
[WPS]
WPS (Wi-Fi Protected Setup) - ein Protokoll zur halbautomatischen Initialisierung eines Wi-Fi-Netzwerks. Zum Initialisieren gibt der Benutzer entweder ein 8-stelliges Passwort ein oder klemmt eine Taste am Router. Wenn Ihr Zugangspunkt vom ersten Typ ist und dieses Flag neben dem Namen Ihres Zugangspunkts hervorgehoben ist, wird dringend empfohlen, zum Admin-Bereich zu gehen und den Zugriff über WPS zu deaktivieren. Tatsache ist, dass eine 8-stellige PIN häufig an der MAC-Adresse erkannt oder in absehbarer Zeit aussortiert werden kann, was jemand, der nicht sauber ist, nutzen kann.
6. Erstellen Sie ein Parsing-Modell und eine Parsing-Funktion
Basierend auf dem, was wir oben herausgefunden haben, beschreiben wir mit Datenklassen, was passiert ist:
enum class AuthMethod { WPA3, WPA2, WPA,
Jetzt schreiben wir eine Funktion, die das Feld "Funktionen" analysiert:
private fun parseCapabilities(capabilitiesString: String): List < Capability > { val capabilities: List < Capability > = capabilitiesString .splitByBrackets() .filter { !it.isTopology() && !it.isWps() } .flatMap { parseCapability(it) } return if (!capabilities.isEmpty()) { capabilities } else { listOf(Capability(AuthMethod.OPEN, KeyManagementAlgorithm.NONE, CipherMethod.NONE)) } } private fun parseCapability(part: String): List < Capability > { if (part.contains("WEP")) { return listOf(Capability( AuthMethod.OTHER, KeyManagementAlgorithm.WEP, CipherMethod.WEP )) } val authScheme = when { part.contains("WPA3") - > AuthMethod.WPA3 part.contains("WPA2") - > AuthMethod.WPA2 part.contains("WPA") - > AuthMethod.WPA else - > null } val keyManagementAlgorithm = when { part.contains("OWE") - > KeyManagementAlgorithm.OWE part.contains("SAE") - > KeyManagementAlgorithm.SAE part.contains("IEEE802.1X") - > KeyManagementAlgorithm.IEEE8021X part.contains("EAP") - > KeyManagementAlgorithm.EAP part.contains("PSK") - > KeyManagementAlgorithm.PSK else - > null } val capabilities = ArrayList < Capability > () if (part.contains("TKIP") || part.contains("CCMP")) { if (part.contains("TKIP")) { capabilities.add(Capability( authScheme ? : AuthMethod.OPEN, keyManagementAlgorithm ? : KeyManagementAlgorithm.NONE, CipherMethod.TKIP )) } if (part.contains("CCMP")) { capabilities.add(Capability( authScheme ? : AuthMethod.OPEN, keyManagementAlgorithm ? : KeyManagementAlgorithm.NONE, CipherMethod.CCMP )) } } else if (authScheme != null || keyManagementAlgorithm != null) { capabilities.add(Capability( authScheme ? : AuthMethod.OPEN, keyManagementAlgorithm ? : KeyManagementAlgorithm.NONE, CipherMethod.NONE )) } return capabilities } private fun parseTopologyMode(capabilitiesString: String): TopologyMode ? { return capabilitiesString .splitByBrackets() .mapNotNull { when { it.contains("ESS") - > TopologyMode.ESS it.contains("BSS") - > TopologyMode.BSS it.contains("IBSS") - > TopologyMode.IBSS else - > null } } .firstOrNull() } private fun parseWPSAvailable(capabilitiesString: String): Boolean { return capabilitiesString .splitByBrackets() .any { it.isWps() } } private fun String.splitByBrackets(): List < String > { val m = Pattern.compile("\\[(.*?)\\]").matcher(this) val parts = ArrayList < String > () while (m.find()) { parts.add(m.group().replace("[", "").replace("]", "")) } return parts } private fun String.isTopology(): Boolean { return TopologyMode.values().any { this == it.name } } private fun String.isWps(): Boolean { return this == "WPS" }
8. Wir schauen uns das Ergebnis an
Ich werde das Netzwerk scannen und zeigen, was passiert ist. Anzeigen der Ergebnisse der einfachen Ausgabe über Log.d:
Capability of Home-Home [WPA2-PSK-CCMP][ESS][WPS] ... capabilities=[Capability(authScheme=WPA2, keyManagementAlgorithm=PSK, cipherMethod=CCMP)], topologyMode=ESS, availableWps=true
Die Frage der Verbindung zum Netzwerk über den Anwendungscode blieb unbeantwortet. Ich kann nur sagen, dass Sie zum Lesen der gespeicherten Betriebssystemkennwörter eines Mobilgeräts Root-Rechte und die Bereitschaft benötigen, das Dateisystem zu durchsuchen, um wpa_supplicant.conf zu lesen. Wenn die Anwendungslogik die Eingabe eines Kennworts von außen umfasst, kann die Verbindung über die Klasse
android.net.wifi.WifiManager hergestellt werden .
Vielen Dank an
Egor Ponomarev für wertvolle Ergänzungen.
Wenn Sie denken, dass Sie etwas hinzufügen oder reparieren müssen, schreiben Sie in die Kommentare :)