Wi-Fi è parechje altre abbreviazioni. Cumu uttene dati nantu à i nodi Wi-Fi in una applicazione Android senza gonfià

Un ghjornu avia bisognu di scansà e rete Wi-Fi da l'applicazioni Android è uttene dati detallati nantu à i punti d'accessu.

Quì avemu avutu affruntà parechje difficultà: off.Documentazione Android assai di e classi descritte sò diventate deprecated (livellu API> 26), chì ùn era micca riflessu in questu; a descrizzione di alcune cose in a documentazione hè minima (per esempiu, u campu di capacità di a classe ScanResult à u mumentu di a scrittura, quasi nunda hè descrittu, ancu s'ellu cuntene assai dati impurtanti). A terza difficultà pò esse in u fattu chì quandu avete prima avvicinatu à u Wi-Fi, oltre à leghje a teoria è a stallazione di u router via localhost, avete da trattà cù una quantità di abbreviazioni chì parenu capiscenu individualmente. Ma ùn pò esse micca ovviamente cumu per rilancià è strutturate (u ghjudiziu hè subjectivu è dipende di l'esperienza previa).

Stu articulu discute quantu à ottene dati cumpleta circa l 'ambienti Wi-Fi da codice Android senza NDK, pirate, ma solu cù l' API Android è capisce quantu à interpretà lu.

Ùn ritardemu micca è cuminciamu à scrive codice.

1. Crea un prughjettu

Questa nota hè destinata à quelli chì anu creatu un prughjettu Android più di una volta, cusì omettemu i dettagli di questu articulu. U codice sottu serà prisentatu in Kotlin, minSdkVersion=23.

2. Access permissions

Per travaglià cù Wi-Fi da l'applicazione, avete bisognu di ottene parechji permessi da l'utilizatore. In cunfurmità cù documentazione, Per scansà a reta nantu à i dispositi cù versioni OS dopu à 8.0, in più di l'accessu à vede u statu di l'ambiente di a reta, avete bisognu di accede à cambià u statu di u modulu Wi-Fi di u dispusitivu, o accessu à coordenate (apprussimativu). o esatta). Partendu da a versione 9.0, duvete dumandà à l'utilizatore per i dui, è ancu dumandà esplicitamente à l'utilizatore per attivà i servizii di locu. Ùn vi scurdate di spiegà galantemente à l'utilizatori chì questu hè u capricciu di Google, è micca a nostra brama di spiarà ellu :)

Allora, in AndroidManifest.xml aghjunghjemu:

    <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"/>

È in u codice chì cuntene un ligame à l'attività attuale:

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. Crea un BroadcastReceiver è abbonate à l'eventi di l'aghjurnamentu di dati nantu à scanning l'ambiente di a rete Wi-Fi

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
}

U mètudu WiFiManager.startScan in a documentazione hè marcatu cum'è depricatu da a versione API 28, ma off. avvià suggerisce l'usu.

In tuttu, avemu ricevutu una lista di l'uggetti ScanResult.

4. Fighjate à ScanResult è capisce i termini

Fighjemu certi campi di sta classa è discrìvemu ciò chì significanu:

SSID — Service Set Identifier hè u nome di a reta

BSSID - Basic Service Set Identifier - Indirizzu MAC di l'adattatore di rete (puntu Wi-Fi)

livellu - Ricivutu Signal Strength Indicator [dBm (Russian dBm) - Decibel, putenza di riferimentu 1 mW.] - Un indicatore di a forza di u signale ricevutu. Piglia un valore da 0 à -100, u più luntanu da 0, u più putere di signale hè stata persa in a strada da u puntu Wi-Fi à u vostru dispositivu. Più dettagli ponu esse truvati, per esempiu, à Wikipedia. Quì vi dicu chì utilizendu a classe Android WifiManager pudete calibre u livellu di u signale in una scala da eccellente à terribili in u passu chì sceglite:

        val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
        val numberOfLevels = 5
        val level = WifiManager.calculateSignalLevel(level, numberOfLevels)

frequency | - frequenza operativa di u puntu Wi-Fi [Hz]. In più di a freccia stessu, pudete esse interessatu in u chjamatu canali. Ogni puntu hà a so purezza operativa. À u mumentu di a scrittura, a gamma più famosa di punti Wi-Fi hè 2.4 GHz. Ma, per esse più precisu, u puntu trasmette infurmazione à u vostru telefunu à una freccia numerata vicinu à quellu chjamatu. Numero di canali è frequenze currispundenti standardizatu. Questu hè fattu per chì i punti vicini operanu à frequenze diverse, per quessa ùn interferiscenu micca l'un l'altru è ùn si riducenu mutualmente a velocità è a qualità di trasmissione. In questu casu, i punti ùn operanu micca à una frequenza, ma in una gamma di frequenze (parametru ChannelWidth), chjamatu a larghezza di u canali. Questu hè, i punti chì operanu nantu à i canali adiacenti (è micca solu adiacenti, ma ancu 3 da elli stessi) i canali interferiscenu l'un l'altru. Pudete truvà stu còdice simplice utile, chì vi permette di calculà u numeru di canali da u valore di freccia per i punti cù una freccia di 2.4 è 5 Ghz:


    /* по частоте определяем номер канала */
    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
            }
        }

capacità - u campu più interessante per l'analisi, u travagliu cù quale hà bisognu di assai tempu. Quì i "capacità" di u puntu sò scritti in a linea. In questu casu, ùn avete micca bisognu di circà i dettagli di l'interpretazione di stringa in a documentazione. Eccu alcuni esempi di ciò chì puderia esse in questa linea:

[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. Capisce l'abbreviazioni è e capacità di parsing

Hè da nutà chì e classi di u pacchettu android.net.wifi.* sò usati sottu u cappucciu da una utilità Linux. wpa_supplicant è u risultatu di output in u campu di capacità hè una copia di u campu di bandiere quandu scanning.

Agiremu in modu coerente. Cunsideremu prima l'output di un furmatu in quale elementi in parentesi sò separati da un signu "-":

[WPA-PSK-TKIP+CCMP]
[WPA2-PSK-CCMP]

U primu significatu descrive u cusì chjamatu. metudu di autentificazione. Questu hè, chì sequenza d'azzioni deve esse realizatu u dispusitivu è u puntu d'accessu per chì u puntu d'accessu si permette d'utilizà è cumu per criptà a carica. A l'ora di scrive stu post, l'opzioni più cumuni sò WPA è WPA2, in quale sia ogni dispusitivu cunnessu direttamente o attraversu u cusì chjamatu. U servitore RADIUS (WPA-Enterprice) furnisce a password nantu à un canale criptatu. Probabilmente, u puntu d'accessu in a vostra casa furnisce una cunnessione secondu stu schema. A diferenza trà a seconda versione è a prima hè chì hà un cifru più forte: AES versus TKIP insecure. WPA3, chì hè più cumplessu è avanzatu, hè ancu introduttu gradualmente. Teoricamente, ci pò esse una opzione cù a soluzione intrapresa CCKM (Cisco Centralized Key Management), ma ùn aghju mai scontru.

U puntu d'accessu pò esse cunfiguratu per autentificà per l'indirizzu MAC. Or, se u puntu d'accessu furnisce dati cù l'algoritmu WEP anticu, allora ùn ci hè micca autentificazione (a chjave secreta quì hè a chjave di criptografia). Classificamu tali opzioni cum'è OTHER.
Ci hè ancu un metudu chì hè populari in u wi-fi publicu cù a Rilevazione di Portal Captive nascosta - una dumanda di autentificazione attraversu un navigatore. Tali punti d'accessu pareanu à u scanner cum'è aperti (chì sò da u puntu di vista di a cunnessione fisica). Dunque, li classificàmu cum'è OPEN.

U sicondu valore pò esse indicatu cum'è algoritmu di gestione chjave. Questu hè un paràmetru di u metudu di autentificazione descrittu sopra. Parla esattamente cumu si scambianu e chjave di criptografia. Cunsideremu l'opzioni pussibuli. EAP - utilizatu in u WPA-Enterprice citatu, usa una basa di dati per verificà i dati di autentificazione inseriti. SAE - utilizatu in WPA3 avanzatu, più resistente à a forza bruta. PSK - l'opzione più cumuna, implica l'ingressu di una password è a trasmissione in forma criptata. IEEE8021X - secondu un standard internaziunale (differente da quellu supportatu da a famiglia WPA). OWE (Opportunistic Wireless Encryption) hè una estensione di u standard IEEE 802.11 per i punti chì avemu classificatu cum'è OPEN. OWE assicura a securità di e dati trasmessi nantu à una rete micca assicurata criptendu. Una opzione hè ancu pussibule quandu ùn ci sò micca chjave d'accessu, chjamemu questa opzione NONE.

U terzu paràmetru hè u cusì chjamatu. schemi di criptografia - cumu esattamente u cifru hè utilizatu per prutege i dati trasmessi. Elenchemu l'opzioni. WEP - usa un cifru di flussu RC4, a chjave secreta hè a chjave di criptografia, chì hè cunsideratu inacceptable in u mondu di a criptografia muderna. TKIP - utilizatu in WPA, CKIP - in WPA2. TKIP + CKIP - pò esse specificatu in punti capaci di WPA è WPA2 per a cumpatibilità inversa.

Invece di trè elementi, pudete truvà una marca WEP solitaria:

[WEP]

Cumu avemu discututu sopra, questu hè abbastanza per micca specificà l'algoritmu per l'usu di e chjave, chì ùn esiste micca, è u metudu di criptografia, chì hè u listessu per automaticamente.

Avà cunsiderà sta parentesi:

[ESS]

issu Modu operativu Wi-Fi o Topulugia di a rete Wi-Fi. Pudete scuntrà u modu BSS (Set di Serviziu Bascu) - quandu ci hè un puntu d'accessu attraversu quale i dispositi cunnessi cumunicanu. Pò esse truvatu nantu à e rete lucali. Comu regula, i punti d'accessu sò necessarii per cunnette i dispositi da e diverse rete lucali, per quessa, sò parti di Sets di Serviziu Estensi - ESS. U tipu IBSSs (Sets di Serviziu Basic Indipendenti) indica chì u dispusitivu hè parti di una reta Peer-to-Peer.

Pudete ancu vede a bandiera WPS:

[WPS]

WPS (Wi-Fi Protected Setup) hè un protokollu per l'inizializazione semi-automatica di una rete Wi-Fi. Per inizializà, l'utilizatore entra in una password di 8 caratteri o pressu un buttone nantu à u router. Se u vostru puntu d'accessu hè di u primu tipu è sta casella di spunta cumparisce accantu à u nome di u vostru puntu d'accessu, vi cunsigliu assai di andà à u pannellu di amministrazione è disattivà l'accessu WPS. U fattu hè chì spessu u PIN di 8 cifre pò esse scupertu da l'indirizzu MAC, o pò esse risoltu in un tempu prevedibile, chì qualcunu pò apprufittà dishonestly.

6. Crea un mudellu è funzione parsing

Basatu nantu à ciò chì avemu scupertu sopra, descriveremu ciò chì hè accadutu cù e classi di dati:

/* схема аутентификации */
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.
}

Avà scrivemu una funzione chì analizà u campu di capacità:


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. Vede u risultatu

Scanneraghju a reta è vi mustraraghju ciò chì aghju trovu. I risultati di l'output simplici via Log.d sò mostrati:

Capability of Home-Home [WPA2-PSK-CCMP][ESS][WPS]
...
capabilities=[Capability(authScheme=WPA2, keyManagementAlgorithm=PSK, cipherMethod=CCMP)], topologyMode=ESS, availableWps=true

U prublema di cunnessione à a reta da u codice di l'applicazione ùn hè micca esaminatu. Diceraghju solu chì per leghje e password salvate da u SO di un dispositivu mobile, avete bisognu di diritti di root è una vuluntà di rummage through the file system per leghje wpa_supplicant.conf. Se a logica di l'applicazione richiede l'inserimentu di una password da l'esternu, a cunnessione pò esse fatta attraversu a classe android.net.wifi.WifiManager.

Спасибо Egor Ponomarev per aggiunte preziose.

Se pensate chì qualcosa deve esse aghjuntu o currettu, scrivite in i cumenti :)

Source: www.habr.com

Add a comment