Wi-Fi és sok más rövidítés. Hogyan szerezhet be adatokat a Wi-Fi csomópontokról egy Android-alkalmazásban anélkül, hogy megduzzadna

Egy nap meg kellett vizsgálnom a Wi-Fi hálózatokat Android-alkalmazásokból, és részletes adatokat kellett szereznem a hozzáférési pontokról.

Itt több nehézséggel kellett szembenéznünk: kikapcsolva.Android dokumentáció a leírt osztályok közül sok elavulttá vált (API szint > 26), ami nem tükröződött benne; a dokumentációban néhány dolog leírása minimális (például az osztály képességmezője ScanResult a cikk írásakor szinte semmi nincs leírva, bár sok fontos adatot tartalmaz). A harmadik nehézség abban rejlik, hogy amikor először kerül a Wi-Fi közelébe, az elmélet elolvasása és a router localhost-on keresztüli beállítása mellett számos, külön-külön is érthetőnek tűnő rövidítéssel kell megküzdenie. De lehet, hogy nem egyértelmű, hogyan viszonyítsuk és strukturáljuk őket (az ítélet szubjektív és a korábbi tapasztalatoktól függ).

Ez a cikk azt tárgyalja, hogyan lehet átfogó adatokat szerezni a Wi-Fi környezetről Android-kódból NDK, hack nélkül, de csak az Android API használatával, és megérti, hogyan értelmezhető.

Ne késlekedjünk, és kezdjük el a kódírást.

1. Hozzon létre egy projektet

Ez a megjegyzés azoknak szól, akik egynél többször készítettek Android-projektet, ezért ennek a tételnek a részleteit figyelmen kívül hagyjuk. Az alábbi kód Kotlinban jelenik meg, minSdkVersion=23.

2. Hozzáférési engedélyek

Ha az alkalmazásból Wi-Fi-vel szeretne dolgozni, több engedélyt kell beszereznie a felhasználótól. Vminek megfelelően dokumentáció, a 8.0 utáni operációs rendszer verziójú eszközök hálózatának szkenneléséhez a hálózati környezet állapotának megtekintésén túlmenően hozzáférésre van szüksége az eszköz Wi-Fi moduljának állapotának módosításához, vagy hozzáférésre van szüksége a koordinátákhoz (kb. vagy pontos). A 9.0-s verziótól kezdődően mindkettőre felszólítania kell a felhasználót, és kifejezetten kérnie kell a felhasználót a helyszolgáltatások bekapcsolására. Ne felejtsd el gálánsan elmagyarázni a felhasználónak, hogy ez a Google szeszélye, és nem a mi vágyunk, hogy kémkedjünk utána :)

Tehát az AndroidManifest.xml fájlhoz hozzáadjuk:

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

Az aktuális tevékenységre mutató hivatkozást tartalmazó kódban pedig:

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. Hozzon létre egy BroadcastReceivert, és iratkozzon fel a Wi-Fi hálózati környezet vizsgálatával kapcsolatos adatfrissítési eseményekre

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
}

A dokumentációban szereplő WiFiManager.startScan metódus az API 28-as verziója óta meg van jelölve, de ki van kapcsolva. útmutató használatát javasolja.

Összességében kaptunk egy listát az objektumokról ScanResult.

4. Tekintse meg a ScanResultot, és értse meg a feltételeket

Nézzük meg ennek az osztálynak néhány mezőjét, és írjuk le, mit jelentenek:

SSID — A Service Set Identifier a hálózat neve

BSSID – Basic Service Set Identifier – A hálózati adapter MAC-címe (Wi-Fi pont)

szint — Fogadott jelerősség jelzője [dBm (orosz dBm) — Decibel, referenciateljesítmény 1 mW.] — A vett jelerősség jelzője. 0 és -100 közötti értéket vesz fel, minél távolabb 0-tól, annál több jel veszett el a Wi-Fi-ponttól az eszközig tartó úton. További részletek találhatók például a címen Wikipedia. Itt elmondom, hogy az Android osztály használatával WifiManager a jelszintet a kiválótól a borzalmasig terjedő skálán kalibrálhatja a választott lépésben:

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

frekvencia — a Wi-Fi pont működési frekvenciája [Hz]. Magán a frekvencián kívül az úgynevezett csatorna is érdekelhet. Minden pontnak megvan a maga működési tisztasága. A cikk írásakor a Wi-Fi pontok legnépszerűbb tartománya 2.4 GHz. De pontosabban a pont a megnevezett frekvenciához közeli számozott frekvencián továbbítja az információkat a telefonjára. A csatornák száma és a megfelelő frekvenciák szabványosított. Ez azért történik, hogy a közeli pontok különböző frekvenciákon működjenek, ezáltal ne zavarják egymást, és kölcsönösen ne csökkentsék az átvitel sebességét és minőségét. Ebben az esetben a pontok nem egy frekvencián működnek, hanem egy frekvenciatartományban (paraméter csatornaszélesség), az úgynevezett csatornaszélesség. Vagyis a szomszédos (és nem csak szomszédos, hanem akár 3 önmagukból is) csatornákon működő pontok zavarják egymást. Hasznosnak találhatja ezt az egyszerű kódot, amely lehetővé teszi a csatornaszám kiszámítását a frekvenciaértékből a 2.4 és 5 Ghz frekvenciájú pontoknál:


    /* по частоте определяем номер канала */
    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épességek - a legérdekesebb elemzési terület, amely sok időt igényelt. Itt a pont „képességei” vannak beírva a sorban. Ebben az esetben nem kell a karakterlánc-értelmezés részleteit keresni a dokumentációban. Íme néhány példa arra, hogy mi lehet ebben a sorban:

[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. A rövidítések és az elemzési képességek megértése

Érdemes megemlíteni, hogy az android.net.wifi.* csomag osztályait egy Linux segédprogram használja a motorháztető alatt. wpa_supplicant és a képességek mezőben a kimeneti eredmény a zászlók mező másolata a szkenneléskor.

Következetesen fogunk cselekedni. Először nézzük meg egy olyan formátum kimenetét, amelyben a zárójelben lévő elemeket „-“ jel választja el egymástól:

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

Az első jelentés leírja az ún. hitelesítési módszer. Vagyis milyen műveletsort kell végrehajtania az eszköznek és a hozzáférési pontnak ahhoz, hogy a hozzáférési pont engedélyezze a használatát, és hogyan kell titkosítani a hasznos adatot. A bejegyzés írásakor a legelterjedtebb opciók a WPA és a WPA2, amelyekben vagy minden csatlakoztatott eszköz közvetlenül vagy az ún. A RADIUS-kiszolgáló (WPA-Enterprice) titkosított csatornán keresztül biztosítja a jelszót. Valószínűleg az otthoni hozzáférési pont biztosítja a kapcsolatot ennek a sémának megfelelően. A különbség a második és az első között az, hogy erősebb titkosítással rendelkezik: AES a nem biztonságos TKIP-hez képest. Fokozatosan bevezetésre kerül a WPA3 is, amely összetettebb és fejlettebb. Elméletileg a CCKM (Cisco Centralized Key Management) vállalati megoldással lehet egy lehetőség, de én még nem találkoztam vele.

Lehetséges, hogy a hozzáférési pontot úgy konfigurálták, hogy MAC-cím alapján hitelesítsen. Vagy ha a hozzáférési pont az elavult WEP-algoritmus használatával szolgáltat adatokat, akkor valójában nincs hitelesítés (a titkos kulcs itt a titkosítási kulcs). Az ilyen opciókat az EGYÉB kategóriába soroljuk.
Van egy olyan módszer is, amely népszerű a nyilvános wi-fi-ben a rejtett Captive Portal Detection funkcióval - egy böngészőn keresztüli hitelesítési kérés. Az ilyen hozzáférési pontok nyitottnak tűnnek a szkenner számára (ami a fizikai kapcsolat szempontjából azok). Ezért NYITOTT kategóriába soroljuk őket.

A második érték jelölhető kulcskezelési algoritmus. Ez a fent leírt hitelesítési módszer paramétere. Pontosan arról beszél, hogyan történik a titkosítási kulcsok cseréje. Tekintsük a lehetséges lehetőségeket. EAP - az említett WPA-Enterprice-ben használatos, adatbázist használ a bevitt hitelesítési adatok ellenőrzésére. SAE - fejlett WPA3-ban használják, jobban ellenáll a nyers erőnek. A PSK - a leggyakoribb lehetőség, amely magában foglalja a jelszó megadását és titkosított formában történő továbbítását. IEEE8021X - nemzetközi szabvány szerint (eltér a WPA család által támogatotttól). Az OWE (Opportunistic Wireless Encryption) az IEEE 802.11 szabvány kiterjesztése azokra a pontokra, amelyeket NYITOTT kategóriába soroltunk. Az OWE titkosítással biztosítja a nem biztonságos hálózaton továbbított adatok biztonságát. Olyan opció is lehetséges, amikor nincsenek hozzáférési kulcsok, nevezzük ezt az opciót NONE-nak.

A harmadik paraméter az ún. titkosítási sémák — pontosan hogyan használják a titkosítást a továbbított adatok védelmére. Soroljuk fel a lehetőségeket. WEP - RC4 adatfolyam titkosítót használ, a titkos kulcs a titkosítási kulcs, amely elfogadhatatlannak számít a modern kriptográfia világában. TKIP - WPA-ban használatos, CKIP - WPA2-ben. TKIP+CKIP - a visszamenőleges kompatibilitás érdekében WPA-ra és WPA2-re képes pontokban adható meg.

Három elem helyett magányos WEP-jelölést találhat:

[WEP]

Ahogy fentebb tárgyaltuk, ez elég ahhoz, hogy ne adja meg a kulcshasználati algoritmust, amely nem létezik, és a titkosítási módszert, amely alapértelmezés szerint ugyanaz.

Most nézzük ezt a zárójelet:

[ESS]

Ezt Wi-Fi üzemmód vagy Wi-Fi hálózati topológia. Találkozhat BSS (Basic Service Set) móddal – ha van egy hozzáférési pont, amelyen keresztül a csatlakoztatott eszközök kommunikálnak. Megtalálható a helyi hálózatokon. Általános szabály, hogy a különböző helyi hálózatokból származó eszközök csatlakoztatásához hozzáférési pontokra van szükség, ezért ezek az Extended Service Sets (ESS) részét képezik. Az IBSS-ek (Independent Basic Service Sets) típus azt jelzi, hogy az eszköz egy Peer-to-Peer hálózat része.

Láthatja a WPS zászlót is:

[WPS]

A WPS (Wi-Fi Protected Setup) a Wi-Fi hálózat félautomata inicializálására szolgáló protokoll. Az inicializáláshoz a felhasználó beír egy 8 karakteres jelszót, vagy megnyom egy gombot az útválasztón. Ha a hozzáférési pont az első típusú, és ez a jelölőnégyzet megjelenik a hozzáférési pont neve mellett, akkor erősen ajánlott az adminisztrációs panel megnyitása és a WPS-hozzáférés letiltása. Az tény, hogy a 8 számjegyű PIN-kód sokszor a MAC-cím alapján kideríthető, vagy belátható időn belül rendbe hozható, amit valaki tisztességtelenül kihasználhat.

6. Hozzon létre egy modellt és elemző függvényt

A fent megtudottak alapján adatosztályok segítségével írjuk le a történteket:

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

Most írjunk egy függvényt, amely elemzi a képességmezőt:


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. Lásd az eredményt

Átkutatom a hálózatot, és megmutatom, mit találtam. Megjelennek a Log.d-n keresztüli egyszerű kimenet eredményei:

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

Vizsgálatlan maradt az alkalmazáskódból a hálózathoz való csatlakozás kérdése. Csak annyit mondok, hogy a mentett jelszavak mobileszköz operációs rendszeréből való olvasásához root jogokra van szükség, és készen kell állnia a fájlrendszerben való turkálásra a wpa_supplicant.conf olvasásához. Ha az alkalmazás logikája megköveteli a jelszó beírását kívülről, a kapcsolat az osztályon keresztül is létrejöhet android.net.wifi.WifiManager.

Köszönöm Egor Ponomarev értékes kiegészítésekért.

Ha úgy gondolod, hogy valamit kiegészíteni vagy javítani kell, írd meg kommentben :)

Forrás: will.com

Hozzászólás