Wi-Fi և շատ այլ հապավումներ: Ինչպես ստանալ տվյալներ Android հավելվածում Wi-Fi հանգույցների մասին՝ առանց այտուցվելու

Մի օր ինձ անհրաժեշտ եղավ սկանավորել Wi-Fi ցանցերը Android հավելվածներից և ստանալ մանրամասն տվյալներ մուտքի կետերի մասին:

Այստեղ մենք ստիպված էինք դիմակայել մի քանի դժվարությունների. off.Android փաստաթղթեր նկարագրված դասերից շատերը հնացել են (API մակարդակ > 26), ինչը չի արտացոլվել դրանում. Փաստաթղթերում որոշ բաների նկարագրությունը նվազագույն է (օրինակ՝ դասի հնարավորությունների դաշտը ScanResult գրելու պահին գրեթե ոչինչ նկարագրված չէ, թեև այն պարունակում է շատ կարևոր տվյալներ): Երրորդ դժվարությունը կարող է կայանալ նրանում, որ երբ դուք առաջին անգամ մոտենում եք Wi-Fi-ին, բացի տեսությունը կարդալուց և երթուղիչի կարգավորումից՝ localhost-ի միջոցով, դուք պետք է գործ ունենաք մի շարք հապավումների հետ, որոնք անհատապես հասկանալի են թվում: Բայց հնարավոր է, որ ակնհայտ չլինի, թե ինչպես կարելի է դրանք կապել և կառուցել (դատողությունը սուբյեկտիվ է և կախված է նախորդ փորձից):

Այս հոդվածը քննարկում է, թե ինչպես կարելի է ստանալ համապարփակ տվյալներ Wi-Fi միջավայրի մասին Android կոդից՝ առանց NDK-ի, հաքերների, բայց միայն օգտագործելով Android API-ն և հասկանալ, թե ինչպես կարելի է այն մեկնաբանել:

Եկեք չհապաղենք ու սկսենք կոդ գրել։

1. Ստեղծեք նախագիծ

Այս նշումը նախատեսված է նրանց համար, ովքեր մեկից ավելի անգամ ստեղծել են Android նախագիծ, ուստի մենք բաց ենք թողնում այս կետի մանրամասները: Ստորև բերված կոդը կներկայացվի Kotlin-ում, minSdkVersion=23:

2. Մուտքի թույլտվություններ

Հավելվածից Wi-Fi-ի հետ աշխատելու համար անհրաժեշտ կլինի օգտատիրոջից մի քանի թույլտվություն ստանալ: Համաձայն փաստաթղթեր, 8.0-ից հետո ՕՀ տարբերակներով սարքերում ցանցը սկանավորելու համար, բացի ցանցային միջավայրի վիճակը դիտելու հասանելիությունից, ձեզ անհրաժեշտ է կամ մուտք՝ սարքի 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 կետ)

մակարդակ — Ստացված ազդանշանի ուժգնության ցուցիչ [dBm (ռուս. dBm) — դեցիբել, հղման հզորությունը 1 մՎտ.] — ստացված ազդանշանի հզորության ցուցիչ։ Ընդունում է արժեք 0-ից մինչև -100, որքան հեռու է 0-ից, այնքան ավելի շատ ազդանշանի հզորություն է կորել Wi-Fi կետից ձեր սարք տանող ճանապարհին: Լրացուցիչ մանրամասները կարող եք գտնել, օրինակ, կայքում Վիքիպեդիա. Այստեղ ես ձեզ կասեմ, որ օգտագործելով Android դասը WifiManager Դուք կարող եք աստիճանավորել ազդանշանի մակարդակը գերազանցից սարսափելի սանդղակով ձեր ընտրած քայլով.

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

հաճախություն — Wi-Fi կետի գործառնական հաճախականությունը [Hz]: Բացի բուն հաճախականությունից, ձեզ կարող է հետաքրքրել այսպես կոչված ալիքը: Յուրաքանչյուր կետ ունի իր գործառնական մաքրությունը: Գրելու պահին Wi-Fi կետերի ամենատարածված տիրույթը 2.4 ԳՀց է: Բայց, ավելի ճիշտ, կետը տեղեկատվություն է փոխանցում ձեր հեռախոսին անվանվածին մոտ համարակալված հաճախականությամբ: Ալիքների քանակը և համապատասխան հաճախականությունները ստանդարտացված. Դա արվում է, որպեսզի մոտակա կետերը աշխատեն տարբեր հաճախականություններով՝ դրանով իսկ չխանգարելով միմյանց և փոխադարձաբար չնվազեցնելով փոխանցման արագությունն ու որակը: Այս դեպքում կետերը գործում են ոչ թե մեկ հաճախականությամբ, այլ մի շարք հաճախականությունների (պարամետր ալիքի լայնությունը), կոչվում է ալիքի լայնություն։ Այսինքն՝ հարակից (և ոչ միայն հարակից, այլ նույնիսկ իրենցից 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) տրամադրում է գաղտնաբառը գաղտնագրված ալիքով: Ամենայն հավանականությամբ, ձեր տան մուտքի կետը կապ է ապահովում այս սխեմայի համաձայն: Երկրորդ տարբերակի և առաջինի տարբերությունն այն է, որ այն ունի ավելի ուժեղ ծածկագիր՝ AES-ն ընդդեմ անապահով TKIP-ի: Աստիճանաբար ներդրվում է նաև WPA3-ը, որն ավելի բարդ և առաջադեմ է: Տեսականորեն, կարող է լինել CCKM (Cisco Centralized Key Management) ձեռնարկատիրական լուծման տարբերակ, բայց ես երբեք չեմ հանդիպել դրան:

Մուտքի կետը կարող է կազմաձևված լինել նույնականացման համար MAC հասցեով: Կամ, եթե մուտքի կետը տրամադրում է տվյալներ՝ օգտագործելով հնացած WEP ալգորիթմը, ապա իրականում վավերացում չկա (այստեղ գաղտնի բանալին գաղտնագրման բանալին է): Մենք դասակարգում ենք նման տարբերակները որպես ԱՅԼ:
Գոյություն ունի նաև մի մեթոդ, որը հանրաճանաչ է հանրային Wi-Fi-ում՝ թաքնված Captive Portal Detection-ով. նույնականացման հարցում բրաուզերի միջոցով: Նման մուտքի կետերը սկաների վրա հայտնվում են որպես բաց (որը դրանք ֆիզիկական կապի տեսանկյունից են): Հետեւաբար, մենք դրանք դասակարգում ենք որպես ԲԱՑ:

Երկրորդ արժեքը կարող է նշանակվել որպես բանալիների կառավարման ալգորիթմ. Սա վերը նկարագրված նույնականացման մեթոդի պարամետրն է: Խոսում է այն մասին, թե ինչպես են փոխանակվում գաղտնագրման բանալիները: Դիտարկենք հնարավոր տարբերակները։ EAP - օգտագործվում է նշված WPA-Enterprice-ում, օգտագործում է տվյալների բազա՝ մուտքագրված վավերացման տվյալները ստուգելու համար: SAE - օգտագործվում է առաջադեմ WPA3-ում, ավելի դիմացկուն է կոպիտ ուժի նկատմամբ: PSK - ամենատարածված տարբերակն է, որը ներառում է գաղտնաբառի մուտքագրում և գաղտնագրված ձևով փոխանցում: IEEE8021X - ըստ միջազգային ստանդարտի (տարբերվում է WPA ընտանիքի կողմից աջակցվողից): OWE (Oportunistic Wireless Encryption) IEEE 802.11 ստանդարտի ընդլայնումն է այն կետերի համար, որոնք մենք դասակարգել ենք որպես ԲԱՑ: OWE-ն ապահովում է անապահով ցանցով փոխանցվող տվյալների անվտանգությունը՝ գաղտնագրելով դրանք: Հնարավոր է նաև տարբերակ, երբ մուտքի ստեղներ չկան, այս տարբերակը կոչենք ՈՉ ՄԻ։

Երրորդ պարամետրը այսպես կոչված. կոդավորման սխեմաներ — ինչպես է գաղտնագիրը օգտագործվում փոխանցված տվյալները պաշտպանելու համար: Թվարկենք տարբերակները։ WEP - օգտագործում է RC4 հոսքային ծածկագիրը, գաղտնի բանալին գաղտնագրման բանալին է, որն անընդունելի է համարվում ժամանակակից կրիպտոգրաֆիայի աշխարհում։ TKIP - օգտագործվում է WPA-ում, CKIP - WPA2-ում: TKIP + CKIP - կարող է նշվել կետերում, որոնք ունակ են WPA և WPA2՝ հետին համատեղելիության համար:

Երեք տարրերի փոխարեն կարող եք գտնել միայնակ WEP նշան.

[WEP]

Ինչպես վերը քննարկեցինք, սա բավական է չնշելու բանալիների օգտագործման ալգորիթմը, որը գոյություն չունի, և գաղտնագրման մեթոդը, որը լռելյայն նույնն է:

Այժմ հաշվի առեք այս փակագիծը.

[ESS]

այս Wi-Fi գործառնական ռեժիմ կամ Wi-Fi ցանցի տոպոլոգիա. Դուք կարող եք հանդիպել BSS (Basic Service Set) ռեժիմին, երբ կա մեկ մուտքի կետ, որի միջոցով կապակցված սարքերը հաղորդակցվում են: Կարելի է գտնել տեղական ցանցերում: Որպես կանոն, մուտքի կետերը անհրաժեշտ են տարբեր տեղական ցանցերից սարքերը միացնելու համար, ուստի դրանք Extended Service Sets - ESS-ի մաս են կազմում: IBSSs (Independent Basic Service Sets) տեսակը ցույց է տալիս, որ սարքը 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

Добавить комментарий