Enges Daags brauch ech Wi-Fi Netzwierker vun Android Uwendungen ze scannen an detailléiert Donnéeën iwwer Zougangspunkten ze kréien.
Hei hu mir e puer Schwieregkeete konfrontéiert:
Dësen Artikel diskutéiert wéi Dir iwwergräifend Donnéeën iwwer d'Wi-Fi Ëmfeld vum Android Code kritt ouni NDK, Hacks, awer nëmmen d'Android API ze benotzen an ze verstoen wéi et interpretéiert gëtt.
Loosst eis net verzögeren an ufänken Code ze schreiwen.
1. Schafen engem Projet
Dës Notiz ass fir déi geduecht, déi en Android-Projet méi wéi eemol erstallt hunn, sou datt mir d'Detailer vun dësem Artikel ausgoen. De Code hei ënnen gëtt a Kotlin presentéiert, minSdkVersion = 23.
2. Zougang Permissiounen
Fir mat Wi-Fi aus der Applikatioun ze schaffen, musst Dir e puer Permissiounen vum Benotzer kréien. Am Aklang mat
Also, an AndroidManifest.xml addéiere mer:
<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"/>
An am Code deen e Link op déi aktuell Aktivitéit 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) {
// TODO: перед вызовом этой функции надо рассказать пользователю, зачем Вам доступ к местоположению
val lm: LocationManager =
context.applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val gpsEnabled: Boolean = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
val networkEnabled: Boolean = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (!gpsEnabled && !networkEnabled) {
context.startActivity(Intent(ACTION_LOCATION_SOURCE_SETTINGS));
}
}
3. Erstellt e BroadcastReceiver an abonnéiert Iech op Datenaktualiséierungsevenementer iwwer d'Scannen vum Wi-Fi Netzwierk Ëmfeld
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
}
D'WiFiManager.startScan Method an der Dokumentatioun ass markéiert als ofgeschaaft zënter der API Versioun 28, awer aus.
Am Ganze krute mir eng Lëscht vun Objeten
4. Kuckt ScanResult a verstitt d'Konditioune
Loosst eis e puer Felder vun dëser Klass kucken a beschreiwen wat se bedeiten:
SSID - Service Set Identifier ass den Numm vum Netzwierk
BSSID - Basis Service Set Identifier - MAC Adress vum Netzwierkadapter (Wi-Fi Punkt)
Niveau - Empfangs Signal Stäerkt Indikator [dBm (Russesch dBm) - Decibel, Referenz Muecht 1 mW.] - En Indikator vun der kritt Signal Kraaft. Huelt e Wäert vun 0 bis -100, wat méi wäit vun 0 ass, wat méi Signalkraaft am Wee vum Wi-Fi Punkt op Ärem Apparat verluer ass. Méi Detailer fannt Dir, zum Beispill, um
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val numberOfLevels = 5
val level = WifiManager.calculateSignalLevel(level, numberOfLevels)
Frequenz - Operatiounsfrequenz vum Wi-Fi Punkt [Hz]. Nieft der Frequenz selwer, kënnt Dir am sougenannte Kanal interesséiert sinn. All Punkt huet seng eege Betribssystemer Rengheet. Zu der Zäit vum Schreiwen ass déi populärste Gamme vu Wi-Fi Punkten 2.4 GHz. Awer, fir méi präzis ze sinn, iwwerdréit de Punkt Informatioun un Ären Telefon mat enger nummeréierter Frequenz no bei der genannter. Zuel vu Kanäl an entspriechend Frequenzen
/* по частоте определяем номер канала */
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
}
}
Kënnen - de stäerkste interessant Terrain fir Analyse, Aarbecht mat deem verlaangt vill Zäit. Hei sinn d'"Fäegkeeten" vum Punkt an der Linn geschriwwen. An dësem Fall musst Dir net no Detailer vun der Stringinterpretatioun an der Dokumentatioun kucken. Hei sinn e puer Beispiller vu wat an dëser Linn kéint sinn:
[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. Versteesdemech Ofkierzungen an Parsing Kënnen
Et ass derwäert ze ernimmen datt d'Klassen vum android.net.wifi.* Package ënner der Hood vun engem Linux Utility benotzt ginn
Mir wäerte konsequent handelen. Loosst eis als éischt d'Ausgab vun engem Format betruechten, an deem Elementer bannent Klammeren duerch en "-" Zeechen getrennt sinn:
[WPA-PSK-TKIP+CCMP]
[WPA2-PSK-CCMP]
Déi éischt Bedeitung beschreift de sougenannte. Authentifikatioun Method. Dat ass, wéi eng Sequenz vun Aktiounen muss den Apparat an den Zougangspunkt ausféieren, fir datt den Zougangspunkt sech erlaabt ze benotzen a wéi d'Notzlaascht verschlësselt gëtt. Zu der Zäit vun dësem Post schreiwen, sinn déi meescht üblech Optiounen WPA an WPA2, an deenen entweder all verbonne Apparat direkt oder duerch de sougenannte. De RADIUS Server (WPA-Enterprice) bitt d'Passwuert iwwer e verschlësselte Kanal. Wahrscheinlech gëtt den Zougangspunkt an Ärem Heem eng Verbindung no dësem Schema. Den Ënnerscheed tëscht der zweeter Versioun an der éischter ass datt et e méi staarke Chiffer huet: AES versus den onsécheren TKIP. WPA3, wat méi komplex a fortgeschratt ass, gëtt och lues a lues agefouert. Theoretesch kann et eng Optioun mat der Enterprice-Léisung CCKM (Cisco Centralized Key Management) sinn, awer ech hunn et ni fonnt.
Den Zougangspunkt ka konfiguréiert sinn fir duerch MAC Adress ze authentifizéieren. Oder, wann den Zougangspunkt Daten mat dem alen WEP Algorithmus ubitt, da gëtt et tatsächlech keng Authentifikatioun (de geheime Schlëssel hei ass de Verschlësselungsschlëssel). Mir klassifizéieren esou Optiounen als ANER.
Et gëtt och eng Method déi populär ass am ëffentleche Wi-Fi mat verstoppte Captive Portal Detection - eng Authentifikatiounsufro duerch e Browser. Esou Zougangspunkte schéngen dem Scanner als oppen (wat se aus der Siicht vun der kierperlecher Verbindung sinn). Dofir klasséiere mir se als OPEN.
Den zweete Wäert kann als bezeechent ginn Schlëssel Gestioun Algorithmus. Dëst ass e Parameter vun der Authentifikatiounsmethod hei uewen beschriwwen. Schwätzt iwwer genau wéi d'Verschlësselungsschlësselen ausgetauscht ginn. Loosst eis déi méiglech Optiounen betruechten. EAP - benotzt am ernimmten WPA-Enterprice, benotzt eng Datebank fir déi aginn Authentifikatiounsdaten z'iwwerpréiwen. SAE - benotzt an fortgeschratt WPA3, méi resistent géint brute Kraaft. PSK - déi meescht üblech Optioun, ëmfaasst Passwuert aginn an verschlësselte Form iwwerdroen. IEEE8021X - no engem internationale Standard (ënnerscheed vun deem vun der WPA Famill ënnerstëtzt). OWE (Opportunistic Wireless Encryption) ass eng Erweiderung vum IEEE 802.11 Standard fir Punkten déi mir als OPEN klasséiert hunn. OWE garantéiert d'Sécherheet vun den Daten, déi iwwer en net geséchert Netzwierk iwwerdroe ginn andeems se se verschlësselen. Eng Optioun ass och méiglech wann et keng Zougangsschlëssel gëtt, loosst eis dës Optioun KENG nennen.
Déi drëtt Parameter ass de sougenannte. Verschlësselungsschemaen - wéi genau de Chiffer benotzt gëtt fir déi iwwerdroen Donnéeën ze schützen. Loosst eis d'Optiounen oplëschten. WEP - benotzt en RC4 Stream Chiffer, de geheime Schlëssel ass de Verschlësselungsschlëssel, deen an der Welt vun der moderner Kryptografie als inakzeptabel ugesi gëtt. TKIP - benotzt an WPA, CKIP - an WPA2. TKIP + CKIP - kann a Punkte spezifizéiert ginn, déi fäeg sinn WPA a WPA2 fir Réckkompatibilitéit.
Amplaz vun dräi Elementer, kënnt Dir eng einsam WEP Mark fannen:
[WEP]
Wéi mir uewe diskutéiert hunn, ass dat genuch fir den Algorithmus net ze spezifizéieren fir Schlësselen ze benotzen, déi net existéieren, an d'Verschlësselungsmethod, déi par défaut d'selwecht ass.
Betruecht elo dës Klammer:
[ESS]
dëser Wi-Fi Betribssystemer Modus oder Wi-Fi Netzwierk Topologie. Dir kënnt BSS (Basic Service Set) Modus begéinen - wann et een Zougangspunkt ass, duerch deen verbonne Geräter kommunizéieren. Kann op lokalen Netzwierker fonnt ginn. Als Regel sinn Zougangspunkte gebraucht fir Geräter aus verschiddene lokalen Netzwierker ze verbannen, sou datt se Deel vun Extended Service Sets - ESS sinn. D'IBSSs (Independent Basic Service Sets) Typ weist datt den Apparat Deel vun engem Peer-to-Peer-Netz ass.
Dir kënnt och de WPS Fändel gesinn:
[WPS]
WPS (Wi-Fi Protected Setup) ass e Protokoll fir semi-automatesch Initialiséierung vun engem Wi-Fi Netzwierk. Fir ze initialiséieren, gitt de Benotzer entweder en 8-Charakter Passwuert oder dréckt e Knäppchen um Router. Wann Ären Zougangspunkt vum éischten Typ ass an dës Checkbox nieft dem Numm vun Ärem Zougangspunkt erschéngt, sidd Dir staark recommandéiert op d'Administratiounspanel ze goen an den WPS Zougang auszeschalten. D'Tatsaach ass datt dacks den 8-Zifferen PIN duerch d'MAC Adress erausfonnt ka ginn, oder et kann an enger absehbarer Zäit zortéiert ginn, vun deem een onéierlech profitéiere kann.
6. Schafen engem Modell an Parsing Funktioun
Baséierend op deem wat mir uewen erausfonnt hunn, wäerte mir beschreiwen wat geschitt ass mat Datenklassen:
/* схема аутентификации */
enum class AuthMethod {
WPA3,
WPA2,
WPA, // Wi-Fi Protected Access
OTHER, // включает в себя Shared Key Authentication и др. использующие mac-address-based и WEP
CCKM, // Cisco
OPEN // Open Authentication. Может быть со скрытым Captive Portal Detection - запрос аутентификации через браузер
}
/* алгоритм ввода ключей */
enum class KeyManagementAlgorithm {
IEEE8021X, // по стандарту
EAP, // Extensible Authentication Protocol, расширяемый протокол аутентификации
PSK, // Pre-Shared Key — каждый узел вводит пароль для доступа к сети
WEP, // в WEP пароль является ключом шифрования (No auth key)
SAE, // Simultaneous Authentication of Equals - может быть в WPA3
OWE, // Opportunistic Wireless Encryption - в роутерах новых поколений, публичных сетях типа OPEN
NONE // может быть без шифрования в OPEN, OTHER
}
/* метод шифрования */
enum class CipherMethod {
WEP, // Wired Equivalent Privacy, Аналог шифрования трафика в проводных сетях
TKIP, // Temporal Key Integrity Protocol
CCMP, // Counter Mode with Cipher Block Chaining Message Authentication Code Protocol,
// протокол блочного шифрования с кодом аутентичности сообщения и режимом сцепления блоков и счетчика
// на основе AES
NONE // может быть без шифрования в OPEN, OTHER
}
/* набор методов шифрования и протоколов, по которым может работать точка */
data class Capability(
var authScheme: AuthMethod? = null,
var keyManagementAlgorithm: KeyManagementAlgorithm? = null,
var cipherMethod: CipherMethod? = null
)
/* Режим работы WiFi (или топология сетей WiFi) */
enum class TopologyMode {
IBSS, // Эпизодическая сеть (Ad-Hoc или IBSS – Independent Basic Service Set).
BSS, // Основная зона обслуживания Basic Service Set (BSS) или Infrastructure Mode.
ESS // Расширенная зона обслуживания ESS – Extended Service Set.
}
Loosst eis elo eng Funktioun schreiwen déi d'Kapazitéitsfeld parséiert:
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. Gesinn d'Resultat
Ech scannen d'Netz a weisen Iech wat ech fonnt hunn. Gewise sinn d'Resultater vum einfachen Output iwwer Log.d:
Capability of Home-Home [WPA2-PSK-CCMP][ESS][WPS]
...
capabilities=[Capability(authScheme=WPA2, keyManagementAlgorithm=PSK, cipherMethod=CCMP)], topologyMode=ESS, availableWps=true
D'Thema vun der Verbindung mam Netz vum Applikatiounscode blouf net ënnersicht. Ech wäert nëmme soen datt fir gespäichert Passwierder aus dem OS vun engem mobilen Apparat ze liesen, braucht Dir Rootrechter an e Wëllen duerch de Dateiesystem ze rudderen fir wpa_supplicant.conf ze liesen. Wann d'Applikatiounslogik erfuerdert datt Dir e Passwuert vu baussen agitt, kann d'Verbindung duerch d'Klass gemaach ginn
Spass
Wann Dir mengt datt eppes muss bäigefüügt oder korrigéiert ginn, da schreift an de Kommentarer :)
Source: will.com