Wi-Fi a vill aner Ofkierzungen. Wéi kritt een Daten iwwer Wi-Fi Noden an enger Android Applikatioun ouni ze geschwollen

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: off.Android Dokumentatioun vill vun de beschriwwene Klassen goufen ofgeschaaft (API Niveau> 26), wat net an et reflektéiert gouf; d'Beschreiwung vun e puer Saachen an der Dokumentatioun ass minimal (zum Beispill d'Kapazitéitsfeld vun der Klass ScanResultat beim Schreiwen gëtt bal näischt beschriwwen, obwuel et vill wichteg Donnéeën enthält). Déi drëtt Schwieregkeet kann an der Tatsaach leien datt wann Dir fir d'éischt un Wi-Fi kënnt, ausser d'Theorie liesen an de Router iwwer localhost opstellen, musst Dir mat enger Rei vun Ofkierzungen këmmeren, déi individuell verständlech schéngen. Awer et ass vläicht net offensichtlech wéi se se bezéien an strukturéieren (Uerteel ass subjektiv an hänkt vu fréierer Erfahrung of).

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 Dokumentatioun, fir d'Netzwierk op Apparater mat OS Versiounen no 8.0 ze scannen, zousätzlech zum Zougang zum Zoustand vun der Netzwierkëmfeld ze gesinn, braucht Dir entweder Zougang fir den Zoustand vum Wi-Fi Modul vum Apparat z'änneren, oder Zougang zu Koordinaten (ongeféier oder genau). Vun der Versioun 9.0 un, musst Dir de Benotzer fir béid froen, an och explizit de Benotzer ufroen fir Standuertservicer unzeschalten. Vergiesst net dem Benotzer galant z'erklären datt dëst dem Google säi Geck ass, an net eise Wonsch op hien ze spionéieren :)

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. guide proposéiert et ze benotzen.

Am Ganze krute mir eng Lëscht vun Objeten ScanResultat.

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 Wikipedia. Hei wäert ech Iech soen datt Dir d'Android Klass benotzt WifiManager Dir kënnt de Signalniveau op enger Skala vun exzellent bis schrecklech kalibréieren an deem Schrëtt deen Dir wielt:

        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 standardiséiert. Dëst gëtt gemaach fir datt d'nächst Punkte mat verschiddene Frequenzen funktionnéieren, doduerch net matenee stéieren an net géigesäiteg d'Geschwindegkeet an d'Qualitéit vun der Iwwerdroung reduzéieren. An dësem Fall funktionnéieren d'Punkte net op enger Frequenz, mee iwwer eng Frequenzbereich (Parameter channelWidth), genannt Kanalbreet. Dat ass, Punkten, déi op ugrenzend (an net nëmmen ugrenzend, awer och 3 vu sech selwer) Kanäl operéieren, stéieren sech mateneen. Dir kënnt dësen einfache Code nëtzlech fannen, deen Iech erlaabt d'Kanalnummer aus dem Frequenzwäert fir Punkte mat enger Frequenz vun 2.4 a 5 Ghz ze berechnen:


    /* по частоте определяем номер канала */
    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 wpa_supplicant an d'Ausgabresultat am Fäegkeetefeld ass eng Kopie vum Fändelenfeld beim Scannen.

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 android.net.wifi.WifiManager.

Spass Egor Ponomarev fir wäertvoll Ergänzunge.

Wann Dir mengt datt eppes muss bäigefüügt oder korrigéiert ginn, da schreift an de Kommentarer :)

Source: will.com

Setzt e Commentaire