Wi-Fi va boshqa ko'plab qisqartmalar. Android ilovasida Wi-Fi tugunlari haqida shishib ketmasdan qanday qilib ma'lumotlarni olish mumkin

Bir kuni men Android ilovalaridan Wi-Fi tarmoqlarini skanerlashim va kirish nuqtalari haqida batafsil ma'lumot olishim kerak edi.

Bu erda biz bir nechta qiyinchiliklarga duch keldik: off.Android hujjatlari tasvirlangan ko'pgina sinflar eskirgan (API darajasi > 26), bu unda aks etmagan; hujjatlardagi ba'zi narsalarning tavsifi minimal (masalan, sinfning imkoniyatlar maydoni ScanResult yozish vaqtida deyarli hech narsa tasvirlanmagan, garchi u juda ko'p muhim ma'lumotlarni o'z ichiga oladi). Uchinchi qiyinchilik shundaki, siz Wi-Fi-ga birinchi marta yaqinlashganingizda, nazariyani o'qish va routerni localhost orqali sozlashdan tashqari, siz alohida tushunarli ko'rinadigan bir qator qisqartmalar bilan shug'ullanishingiz kerak. Ammo ularni qanday bog'lash va tuzish kerakligi aniq bo'lmasligi mumkin (hukm sub'ektivdir va oldingi tajribaga bog'liq).

Ushbu maqolada Android kodidan Wi-Fi muhiti haqida to'liq ma'lumotni NDK, buzg'unchiliksiz, faqat Android API yordamida qanday olish va uni qanday talqin qilishni tushunish muhokama qilinadi.

Keling, kechiktirmaylik va kod yozishni boshlaylik.

1. Loyiha yarating

Ushbu eslatma Android loyihasini bir necha marta yaratganlar uchun mo'ljallangan, shuning uchun biz ushbu elementning tafsilotlarini o'tkazib yuboramiz. Quyidagi kod Kotlin da taqdim etiladi, minSdkVersion=23.

2. Kirish ruxsatlari

Ilovadan Wi-Fi bilan ishlash uchun foydalanuvchidan bir nechta ruxsatlarni olishingiz kerak bo'ladi. Ga muvofiq hujjatlar, 8.0 dan keyin OS versiyalari bo'lgan qurilmalarda tarmoqni skanerlash uchun tarmoq muhiti holatini ko'rishdan tashqari, qurilmaning Wi-Fi moduli holatini o'zgartirish uchun kirish yoki koordinatalarga kirish (taxminan) kerak. yoki aniq). 9.0 versiyasidan boshlab siz foydalanuvchiga ikkalasini ham so'rashingiz kerak, shuningdek, foydalanuvchidan joylashuv xizmatlarini yoqishni aniq so'rashingiz kerak. Foydalanuvchiga bu Googlening injiqligi ekanligini va bizning unga josuslik qilish istagimiz emasligini jasorat bilan tushuntirishni unutmang :)

Shunday qilib, AndroidManifest.xml-ga biz quyidagilarni qo'shamiz:

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

Va joriy faoliyatga havolani o'z ichiga olgan kodda:

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. BroadcastReceiver yarating va Wi-Fi tarmoq muhitini skanerlash boʻyicha maʼlumotlarni yangilash tadbirlariga obuna boʻling.

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
}

Hujjatlardagi WiFiManager.startScan usuli API 28-versiyasidan beri bekor qilingan, lekin oʻchirilgan deb belgilangan. hidoyat foydalanishni taklif qiladi.

Hammasi bo'lib biz ob'ektlar ro'yxatini oldik ScanResult.

4. ScanResult-ga qarang va shartlarni tushuning

Keling, ushbu sinfning ba'zi sohalarini ko'rib chiqamiz va ular nimani anglatishini tavsiflaymiz:

SSID — Xizmatlar toʻplami identifikatori — tarmoq nomi

BSSID – Asosiy xizmatlar to‘plami identifikatori – tarmoq adapterining MAC manzili (Wi-Fi nuqtasi)

daraja — Qabul qilingan signal kuchi indikatori [dBm (ruscha dBm) — Desibel, mos yozuvlar quvvati 1 mVt.] — Qabul qilingan signal kuchining ko'rsatkichi. 0 dan -100 gacha bo'lgan qiymatni oladi, 0 dan qanchalik uzoq bo'lsa, Wi-Fi nuqtasidan qurilmangizga o'tish yo'lida signal kuchi shunchalik ko'p yo'qoladi. Batafsil ma'lumotni, masalan, saytida topishingiz mumkin Vikipediya. Bu erda men sizga Android sinfidan foydalanishni aytaman Wifi menejeri Siz tanlagan bosqichda signal darajasini a'lodan dahshatligacha shkala bo'yicha sozlashingiz mumkin:

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

chastota — Wi-Fi nuqtasining ish chastotasi [Hz]. Chastotaning o'ziga qo'shimcha ravishda, sizni kanal deb ataladigan narsa qiziqtirishi mumkin. Har bir nuqta o'zining operatsion tozaligiga ega. Yozish vaqtida Wi-Fi nuqtalarining eng mashhur diapazoni 2.4 GGts ni tashkil qiladi. Ammo, aniqrog'i, nuqta ma'lumotni telefoningizga nomlanganiga yaqin raqamlangan chastotada uzatadi. Kanallar soni va mos keladigan chastotalar standartlashtirilgan. Bu yaqin atrofdagi nuqtalar turli chastotalarda ishlashi va shu bilan bir-biriga aralashmasligi va uzatish tezligi va sifatini o'zaro pasaytirmasligi uchun amalga oshiriladi. Bunday holda, nuqtalar bir chastotada emas, balki bir qator chastotalar oralig'ida ishlaydi (parametr kanal kengligi), kanal kengligi deb ataladi. Ya'ni, qo'shni (va nafaqat qo'shni, balki o'zidan 3 ta) kanallarda ishlaydigan nuqtalar bir-biriga xalaqit beradi. Siz ushbu oddiy kodni foydali deb topishingiz mumkin, bu sizga 2.4 va 5 Gts chastotali nuqtalar uchun chastota qiymatidan kanal raqamini hisoblash imkonini beradi:


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

Qobiliyatlari - tahlil qilish uchun eng qiziqarli soha, u bilan ishlash juda ko'p vaqtni talab qiladi. Bu erda nuqtaning "imkoniyatlari" qatorda yoziladi. Bunday holda, siz hujjatlarda string talqini tafsilotlarini izlashingiz shart emas. Bu qatorda bo'lishi mumkin bo'lgan ba'zi misollar:

[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. Qisqartmalar va tahlil qilish imkoniyatlarini tushunish

Shuni aytib o'tish joizki, android.net.wifi.* paketining sinflari Linux yordam dasturi tomonidan himoya ostida qo'llaniladi. wpa_supplicant va imkoniyatlar maydonidagi chiqish natijasi skanerlashda bayroqlar maydonining nusxasidir.

Biz izchil harakat qilamiz. Keling, avval qavs ichidagi elementlar “-” belgisi bilan ajratilgan formatning chiqishini ko'rib chiqaylik:

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

Birinchi ma'no deb atalmishni tasvirlaydi. autentifikatsiya usuli. Ya'ni, kirish nuqtasi o'zidan foydalanishga ruxsat berishi va foydali yukni qanday shifrlashi uchun qurilma va kirish nuqtasi qanday harakatlar ketma-ketligini bajarishi kerak. Ushbu xabarni yozish paytida eng keng tarqalgan variantlar WPA va WPA2 bo'lib, ularda har bir qurilma to'g'ridan-to'g'ri yoki so'zda ulangan. RADIUS serveri (WPA-Enterprice) shifrlangan kanal orqali parol beradi. Ehtimol, sizning uyingizdagi kirish nuqtasi ushbu sxema bo'yicha ulanishni ta'minlaydi. Ikkinchi versiya va birinchi versiya o'rtasidagi farq shundaki, u kuchliroq shifrga ega: xavfsiz bo'lmagan TKIPga nisbatan AES. Bundan murakkabroq va ilg'or bo'lgan WPA3 ham asta-sekin joriy etilmoqda. Nazariy jihatdan, CCKM (Cisco markazlashtirilgan kalitlarni boshqarish) korxona yechimi bilan variant bo'lishi mumkin, lekin men uni hech qachon uchratmaganman.

Kirish nuqtasi MAC manzili bo'yicha autentifikatsiya qilish uchun tuzilgan bo'lishi mumkin. Yoki, agar kirish nuqtasi eskirgan WEP algoritmidan foydalangan holda ma'lumotlarni taqdim etsa, unda aslida autentifikatsiya yo'q (bu erda maxfiy kalit shifrlash kalitidir). Biz bunday variantlarni BOSHQA deb tasniflaymiz.
Yashirin Captive Portal Detection bilan umumiy Wi-Fi tarmog'ida mashhur bo'lgan usul ham mavjud - brauzer orqali autentifikatsiya so'rovi. Bunday kirish nuqtalari skanerga ochiq ko'rinadi (ular jismoniy ulanish nuqtai nazaridan). Shuning uchun biz ularni Ochiq deb tasniflaymiz.

Ikkinchi qiymatni quyidagicha belgilash mumkin kalitlarni boshqarish algoritmi. Bu yuqorida tavsiflangan autentifikatsiya usulining parametridir. Shifrlash kalitlari qanday almashtirilishi haqida gapiradi. Keling, mumkin bo'lgan variantlarni ko'rib chiqaylik. EAP - qayd etilgan WPA-Enterprice-da qo'llaniladi, kiritilgan autentifikatsiya ma'lumotlarini tekshirish uchun ma'lumotlar bazasidan foydalanadi. SAE - ilg'or WPA3 da qo'llaniladi, qo'pol kuchga nisbatan ancha chidamli. PSK - eng keng tarqalgan variant, parolni kiritish va uni shifrlangan shaklda uzatishni o'z ichiga oladi. IEEE8021X - xalqaro standartga muvofiq (WPA oilasi tomonidan qo'llab-quvvatlanadiganidan farq qiladi). OWE (Opportunistik simsiz shifrlash) biz OPEN deb tasniflagan nuqtalar uchun IEEE 802.11 standartining kengaytmasi hisoblanadi. OWE shifrlash orqali himoyalanmagan tarmoq orqali uzatiladigan ma'lumotlar xavfsizligini ta'minlaydi. Kirish kalitlari bo'lmaganda ham variant mumkin, keling, bu variantni NONE deb ataymiz.

Uchinchi parametr deb ataladi. shifrlash sxemalari — uzatilayotgan maʼlumotlarni himoya qilish uchun shifr qanday aniq qoʻllaniladi. Keling, variantlarni sanab o'tamiz. WEP - RC4 oqim shifridan foydalanadi, maxfiy kalit zamonaviy kriptografiya olamida nomaqbul deb hisoblanadigan shifrlash kalitidir. TKIP - WPA-da, CKIP - WPA2-da ishlatiladi. TKIP+CKIP - orqaga qarab muvofiqligi uchun WPA va WPA2 ga qodir bo'lgan nuqtalarda ko'rsatilishi mumkin.

Uch element o'rniga siz yolg'iz WEP belgisini topishingiz mumkin:

[WEP]

Yuqorida muhokama qilganimizdek, bu mavjud bo'lmagan kalitlardan foydalanish algoritmini va sukut bo'yicha bir xil bo'lgan shifrlash usulini belgilamaslik uchun etarli.

Endi bu qavsni ko'rib chiqing:

[ESS]

bu Wi-Fi ish rejimi yoki Wi-Fi tarmoq topologiyasi. Siz BSS (Asosiy xizmat ko'rsatish to'plami) rejimiga duch kelishingiz mumkin - ulangan qurilmalar aloqa qiladigan bitta kirish nuqtasi mavjud bo'lganda. Mahalliy tarmoqlarda topish mumkin. Qoidaga ko'ra, kirish nuqtalari turli xil mahalliy tarmoqlardan qurilmalarni ulash uchun kerak, shuning uchun ular kengaytirilgan xizmatlar to'plami - ESS tarkibiga kiradi. IBSS (Mustaqil asosiy xizmatlar to'plami) turi qurilmaning Peer-to-Peer tarmog'ining bir qismi ekanligini ko'rsatadi.

Siz WPS bayrog'ini ham ko'rishingiz mumkin:

[WPS]

WPS (Wi-Fi Protected Setup) - Wi-Fi tarmog'ini yarim avtomatik ishga tushirish protokoli. Ishga tushirish uchun foydalanuvchi 8 ta belgidan iborat parolni kiritadi yoki routerdagi tugmani bosadi. Agar sizning kirish nuqtangiz birinchi turdagi bo'lsa va ushbu belgi kirish nuqtangiz nomining yonida paydo bo'lsa, sizga administrator paneliga o'tish va WPS kirishni o'chirish tavsiya etiladi. Gap shundaki, ko'pincha 8 xonali PIN-kodni MAC-manzil orqali topish mumkin yoki uni yaqin vaqt ichida hal qilish mumkin, bundan kimdir insofsiz foydalanishi mumkin.

6. Model va tahlil qilish funksiyasini yaratish

Yuqorida bilib olganimizga asoslanib, biz ma'lumotlar sinflari yordamida nima sodir bo'lganini tasvirlaymiz:

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

Endi imkoniyatlar maydonini tahlil qiladigan funksiya yozamiz:


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. Natijaga qarang

Men tarmoqni skanerlayman va sizga nima topganimni ko'rsataman. Log.d orqali oddiy chiqish natijalari ko'rsatilgan:

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

Ilova kodidan tarmoqqa ulanish masalasi o'rganilmagan holda qoldi. Men shuni aytamanki, mobil qurilmaning operatsion tizimidan saqlangan parollarni o'qish uchun sizga ildiz huquqlari va wpa_supplicant.conf o'qish uchun fayl tizimi orqali o'tishga tayyor bo'lishingiz kerak. Agar dastur mantig'i tashqaridan parol kiritishni talab qilsa, ulanish sinf orqali amalga oshirilishi mumkin android.net.wifi.WifiManager.

Rahmat Egor Ponomarev qimmatli qo'shimchalar uchun.

Agar biror narsani qo'shish yoki tuzatish kerak deb hisoblasangiz, izohlarda yozing :)

Manba: www.habr.com

a Izoh qo'shish