Wi-Fi жана башка көптөгөн кыскартуулар. Android тиркемесинде Wi-Fi түйүндөрү жөнүндө маалыматтарды шишип кетпестен кантип алса болот

Бир күнү мен Android тиркемелеринен Wi-Fi тармактарын сканерлеп, кирүү чекиттери жөнүндө толук маалыматтарды алышым керек болчу.

Бул жерде биз бир нече кыйынчылыктарга дуушар болдук: off.Android документтери сүрөттөлгөн класстардын көбү эскирди (API деңгээли > 26), ал анда чагылдырылган эмес; документтердеги кээ бир нерселердин сүрөттөлүшү минималдуу (мисалы, класстын мүмкүнчүлүктөрү талаасы ScanResult жазуу учурунда, дээрлик эч нерсе сүрөттөлгөн эмес, бирок ал көп маанилүү маалыматтарды камтыйт). Үчүнчү кыйынчылык Wi-Fi'га биринчи жолу жакындаганда, теорияны окуп, роутерди localhost аркылуу орнотуудан башка, сиз өзүнчө түшүнүктүү болуп көрүнгөн бир катар кыскартуулар менен күрөшүүгө туура келиши мүмкүн. Бирок аларды кантип байланыштыруу жана структуралаштыруу ачык-айкын болбошу мүмкүн (сот субъективдүү жана мурунку тажрыйбага көз каранды).

Бул макалада Android кодунан Wi-Fi чөйрөсү жөнүндө толук маалыматты NDKсыз, бузуксуз, бирок Android API аркылуу гана алуу жана аны кантип чечмелөө керектиги талкууланат.

Кечикпей, код жазууну баштайлы.

1. Долбоор түзүү

Бул эскертүү Android долбоорун бир нече жолу түзгөндөр үчүн арналган, ошондуктан биз бул нерсенин чоо-жайын өткөрүп жиберебиз. Төмөндөгү код Котлинде көрсөтүлөт, minSdkVersion=23.

2. Кирүү уруксаттары

Тиркемеден Wi-Fi менен иштөө үчүн колдонуучудан бир нече уруксаттарды алышыңыз керек болот. Менен ылайык документтер, 8.0ден кийин OS версиялары бар түзмөктөрдө тармакты сканерлөө үчүн, тармак чөйрөсүнүн абалын көрүү мүмкүнчүлүгүнөн тышкары, түзмөктүн Wi-Fi модулунун абалын өзгөртүүгө же координаттарга (болжолдуу) жетүү керек. же так). 9.0 версиясынан баштап, сиз колдонуучуга экөөнү тең сунуштап, ошондой эле колдонуучудан жайгашкан жерди аныктоо кызматтарын күйгүзүүнү ачык суранышыңыз керек. Колдонуучуга бул Google'дун каалоосу экенин жана биздин аны аңдыгыбыз келбей турганын чечкиндүү түрдө түшүндүрүүнү унутпаңыз :)

Ошентип, AndroidManifest.xml ичинде биз төмөнкүлөрдү кошобуз:

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

Ал эми учурдагы аракетке шилтемени камтыган коддо:

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 түзүңүз жана 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
}

Документтеги WiFiManager.startScan ыкмасы API 28 версиясынан бери эскирилген деп белгиленген, бирок өчүрүлгөн. жетектөө колдонууну сунуш кылат.

Бардыгы болуп объекттердин тизмесин алдык ScanResult.

4. ScanResult программасын карап, терминдерди түшүнүңүз

Келгиле, бул класстын айрым тармактарын карап көрөлү жана алар эмнени билдирерин сүрөттөп көрөлү:

SSID — Кызмат топтомун идентификатору — тармактын аты

BSSID – Негизги тейлөө топтомунун идентификатору – тармак адаптеринин MAC дареги (Wi-Fi түйүнү)

деңгээл — Кабыл алынган сигнал күчүнүн көрсөткүчү [дБм (орусча дБм) — Децибел, эталондук кубаттуулук 1 мВт.] — Кабыл алынган сигналдын күчүнүн көрсөткүчү. 0дөн -100гө чейинки маанини алат, 0дөн канчалык алыс болсо, Wi-Fi түйүнүнөн түзмөгүңүзгө чейинки жолдо ошончолук сигнал күчү жоголот. Көбүрөөк маалымат, мисалы, табууга болот Wikipedia. Бул жерде мен сизге Android классын колдонуп айтып берем WifiManager сиз тандаган кадамда сигналдын деңгээлин эң сонундан коркунучтууга чейин калибрлей аласыз:

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

жыштыгы — Wi-Fi чекитинин иштөө жыштыгы [Гц]. Жыштыктан тышкары сизди канал деп аталган нерсе кызыктырышы мүмкүн. Ар бир чекиттин өзүнүн иштөө тазалыгы бар. Жазуу учурунда Wi-Fi чекиттеринин эң популярдуу диапазону 2.4 ГГц. Бирок, тагыраак айтканда, чекит телефонуңузга маалыматты аталганга жакын номерлүү жыштык менен өткөрүп берет. Каналдардын саны жана тиешелүү жыштыктар стандартташтырылган. Бул жакын жердеги пункттар ар кандай жыштыктарда иштеши үчүн, ошону менен бири-бирине тоскоол болбошу жана берүүнүн ылдамдыгын жана сапатын өз ара төмөндөтпөшү үчүн жасалат. Бул учурда чекиттер бир жыштыкта ​​эмес, бир катар жыштык диапазонунда иштейт (параметр channelWidth), каналдын туурасы деп аталат. Башкача айтканда, чектеш (жана чектеш гана эмес, ал тургай өзүнөн 3 да) каналдарда иштеген чекиттер бири-бирине тоскоол болот. Сиз 2.4 жана 5 ГГц жыштыктагы чекиттер үчүн жыштыктын маанисинен каналдын номерин эсептөөгө мүмкүндүк берген бул жөнөкөй кодду пайдалуу деп тапсаңыз болот:


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

мүмкүнчүлүктөрү - талдоо үчүн эң кызыктуу тармак, аны менен иштөө көп убакытты талап кылган. Бул жерде чекиттин "мүмкүнчүлүктөрү" сапта жазылган. Бул учурда, сиз документациядан сап чечмелөөнүн чоо-жайын издөөнүн кажети жок. Бул жерде бул сапта болушу мүмкүн болгон кээ бир мисалдар:

[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. Аббревиатураларды түшүнүү жана талдоо мүмкүнчүлүктөрү

Белгилей кетсек, android.net.wifi.* пакетинин класстары Linux утилитасы тарабынан капчыктын астында колдонулат. wpa_supplicant жана мүмкүнчүлүктөр талаасындагы чыгаруу натыйжасы сканерлөөдө желектер талаасынын көчүрмөсү болуп саналат.

Биз ырааттуу аракеттенебиз. Адегенде кашаанын ичиндеги элементтер “-” белгиси менен бөлүнгөн форматтын чыгышын карап көрөлү:

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

Биринчи мааниси деп аталганды сүрөттөйт. аутентификация ыкмасы. Башкача айтканда, кирүү чекитинин өзүн колдонууга жана пайдалуу жүктү кантип шифрлөөсүнө уруксат берүү үчүн аппарат жана кирүү чекити кандай аракеттерди аткарышы керек. Бул постту жазып жатканда, эң кеңири таралган варианттар - бул WPA жана WPA2, анда ар бир туташкан түзмөк түздөн-түз же деп аталган аркылуу. RADIUS сервери (WPA-Enterprice) шифрленген канал аркылуу сырсөздү камсыз кылат. Кыязы, сиздин үйүңүздөгү кирүү чекити ушул схемага ылайык байланышты камсыздайт. Экинчи версия менен биринчисинин айырмасы, анын күчтүү шифри бар: кооптуу TKIPке каршы AES. Татаал жана өнүккөн WPA3 да акырындык менен киргизилүүдө. Теориялык жактан алганда, CCKM (Cisco борборлоштурулган ачкычтарды башкаруу) ишкана чечими менен вариант болушу мүмкүн, бирок мен аны эч качан жолуктурган эмесмин.

Мүмкүн кирүү чекити MAC дареги боюнча аныктыгын текшерүү үчүн конфигурацияланган болушу мүмкүн. Же болбосо, кирүү чекити эскирген WEP алгоритмин колдонуу менен маалыматтарды берсе, анда чындыгында аутентификация жок (бул жерде жашыруун ачкыч - шифрлөө ачкычы). Биз мындай опцияларды БАШКА деп классификациялайбыз.
Жашыруун Captive Portal Detection менен коомдук Wi-Fi тармагында популярдуу болгон ыкма дагы бар - браузер аркылуу аутентификация сурамы. Мындай кирүү чекиттери сканерге ачык болуп көрүнөт (алар физикалык байланыш көз карашынан алганда). Ошондуктан аларды АЧЫК деп классификациялайбыз.

Экинчи маани катары белгилениши мүмкүн негизги башкаруу алгоритми. Бул жогоруда сүрөттөлгөн аутентификация ыкмасынын параметри. Шифрлөө ачкычтары кандайча алмашылганы тууралуу кеп. Келгиле, мүмкүн болгон варианттарды карап көрөлү. EAP - аталган WPA-Enterpriceде колдонулат, киргизилген аутентификация маалыматтарын текшерүү үчүн маалымат базасын колдонот. SAE - өнүккөн WPA3 колдонулат, катаал күчкө туруктуураак. PSK - эң кеңири таралган вариант, сырсөздү киргизүүнү жана аны шифрленген түрдө берүүнү камтыйт. IEEE8021X - эл аралык стандартка ылайык (WPA үй-бүлөсү тарабынан колдоого алынгандан башкача). OWE (Opportunistic Wireless Encryption) - бул IEEE 802.11 стандартынын кеңейтилиши, биз АЧЫК деп классификациялаган пункттар үчүн. OWE аны шифрлөө аркылуу корголбогон тармак аркылуу берилүүчү маалыматтардын коопсуздугун камсыздайт. Опция мүмкүндүк ачкычтары жок болгондо да мүмкүн, келгиле, бул опцияны ЖОК деп атайлы.

Үчүнчү параметр деп аталган. шифрлөө схемалары — берилүүчү маалыматтарды коргоо үчүн шифр кандайча так колдонулат. Келгиле, варианттарды тизмектейли. WEP - RC4 агым шифрин колдонот, жашыруун ачкыч - заманбап криптография дүйнөсүндө кабыл алынгыс деп эсептелген шифрлөө ачкычы. TKIP - WPA, CKIP - WPA2 колдонулат. TKIP+CKIP - артка шайкештик үчүн WPA жана WPA2 жөндөмдүү пункттарда көрсөтүлүшү мүмкүн.

Үч элементтин ордуна сиз жалгыз WEP белгисин таба аласыз:

[WEP]

Биз жогоруда талкуулагандай, бул жок болгон ачкычтарды колдонуу алгоритмин жана демейки боюнча бирдей болгон шифрлөө ыкмасын көрсөтпөө үчүн жетиштүү.

Эми бул кашааны карап көрөлү:

[ESS]

бул Wi-Fi иштөө режими же Wi-Fi тармагынын топологиясы. Сиз BSS (Негизги Кызмат топтому) режимине туш болушуңуз мүмкүн - туташкан түзмөктөр байланыша турган бир кирүү чекити болгондо. Жергиликтүү тармактардан тапса болот. Эреже катары, кирүү чекиттери ар кандай локалдык тармактардан түзмөктөрдү туташтыруу үчүн керек, ошондуктан алар Кеңейтилген Кызмат топтомдорунун бир бөлүгү болуп саналат - ESS. IBSSs (Көз карандысыз негизги кызматтар топтому) түрү аппараттын Peer-to-Peer тармагынын бир бөлүгү экенин көрсөтөт.

Сиз ошондой эле WPS желегин көрө аласыз:

[WPS]

WPS (Wi-Fi Protected Setup) - Wi-Fi тармагын жарым автоматтык түрдө баштоо үчүн протокол. Инициализациялоо үчүн колдонуучу 8 белгиден турган сырсөздү киргизет же роутердеги баскычты басат. Эгерде сиздин кирүү чекитиңиз биринчи типте болсо жана бул кутуча сиздин кирүү чекитиңиздин аталышынын жанында пайда болсо, администратор панелине барып, WPS мүмкүнчүлүгүн өчүрүү сунушталат. Чындыгында, көп учурда 8 орундуу PINди MAC дареги боюнча табууга болот же аны кимдир бирөө чынчылдык менен пайдаланып калышы мүмкүн болгон жакынкы убакта иргеп алса болот.

6. Модель жана талдоо функциясын түзүңүз

Жогоруда билгендерибиздин негизинде биз маалымат класстары аркылуу эмне болгонун сүрөттөп беребиз:

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

Эми мүмкүнчүлүктөр талаасын талдоочу функцияны жазалы:


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. Жыйынтыгын караңыз

Мен тармакты сканерлеп, тапканымды көрсөтөм. Log.d аркылуу жөнөкөй чыгаруунун натыйжалары көрсөтүлгөн:

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

Тиркеме кодунан тармакка туташуу маселеси каралбай калган. Мен мобилдик түзүлүштүн ОСунан сакталган сырсөздөрдү окуу үчүн, сизде тамыр укуктары жана wpa_supplicant.conf окуу үчүн файлдык системаны аралап чыгууга даяр болушу керек экенин гана айтайын. Колдонмонун логикасы сырсөздү сырттан киргизүүнү талап кылса, байланыш класс аркылуу жүргүзүлүшү мүмкүн android.net.wifi.WifiManager.

Спасибо Егор Пономарев баалуу кошумчалар үчүн.

Эгер бир нерсе кошуу же оңдоо керек деп ойлосоңуз, комментарийге жазыңыз :)

Source: www.habr.com

Комментарий кошуу