Wi-Fi болон бусад олон товчлолууд. Андройд аппликешн дээрх Wi-Fi зангилааны мэдээллийг хавдахгүйгээр хэрхэн авах вэ

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

Энд бид хэд хэдэн бэрхшээлтэй тулгарсан: унтраасан.Android баримт бичиг тайлбарласан олон ангиуд хуучирсан (API түвшин > 26), үүнд тусгагдаагүй; Баримт бичигт байгаа зарим зүйлийн тайлбар нь хамгийн бага (жишээлбэл, ангийн чадварын талбар). ScanResult бичиж байх үед бараг юу ч тайлбарлаагүй, гэхдээ энэ нь маш олон чухал өгөгдлийг агуулдаг). Гурав дахь бэрхшээл нь та Wi-Fi-д анх ойртохдоо онолыг уншиж, чиглүүлэгчийг localhost-ээр тохируулахаас бусад тохиолдолд тус тусад нь ойлгомжтой мэт санагдах хэд хэдэн товчлолтой тулгардагтай холбоотой байж болох юм. Гэхдээ тэдгээрийг хэрхэн уялдуулж, бүтэцжүүлэх нь тодорхойгүй байж магадгүй (шүүлт нь субъектив бөгөөд өмнөх туршлагаас хамаарна).

Энэ нийтлэлд NDK, хакердалгүйгээр, зөвхөн Android API ашиглан Wi-Fi орчны талаарх дэлгэрэнгүй мэдээллийг Android кодоос хэрхэн олж авах, хэрхэн тайлбарлах талаар ярилцах болно.

Хойшлуулахгүй, код бичиж эхэлцгээе.

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 цэг)

түвшин — Хүлээн авсан дохионы хүч чадлын үзүүлэлт [дБм (Оросын дБм) — Децибел, жишиг хүч 1 мВт.] — Хүлээн авсан дохионы хүч чадлын үзүүлэлт. 0-ээс -100 хүртэлх утгыг авна, 0-ээс холдох тусам Wi-Fi цэгээс таны төхөөрөмж рүү хүрэх замд илүү их дохионы хүч алдагдана. Дэлгэрэнгүй мэдээллийг, жишээлбэл, эндээс олж болно Википедиа. Энд би танд Android ангиудыг ашиглахыг хэлье Wifi менежер Та сонгосон алхамдаа дохионы түвшинг маш сайнаас аймшигтай хүртэл тохируулж болно.

        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 GHz юм. Гэхдээ илүү нарийвчлалтай хэлэхэд энэ цэг нь таны утсанд нэрлэсэнтэй ойролцоо дугаарласан давтамжтайгаар мэдээллийг дамжуулдаг. Сувгийн тоо ба холбогдох давтамж стандартчилагдсан. Энэ нь ойролцоох цэгүүд өөр өөр давтамжтайгаар ажилладаг тул бие биедээ саад болохгүй, дамжуулалтын хурд, чанарыг харилцан бууруулахгүй байхаар хийгддэг. Энэ тохиолдолд цэгүүд нэг давтамж дээр биш, харин олон давтамжийн хүрээнд ажилладаг (параметр сувгийн өргөн), сувгийн өргөн гэж нэрлэдэг. Өөрөөр хэлбэл, зэргэлдээх (зөвхөн зэргэлдээ төдийгүй өөрөөсөө 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.* багцын ангиудыг Линукс хэрэглүүрийн дор ашигладаг гэдгийг дурдах нь зүйтэй. wpa_өргөгч ба чадамжийн талбар дахь гаралтын үр дүн нь сканнердах үед тугуудын талбарын хуулбар юм.

Бид тууштай ажиллах болно. Эхлээд хаалт доторх элементүүдийг "-" тэмдгээр тусгаарласан форматын гаралтыг авч үзье.

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

Эхний утга нь гэж нэрлэгддэг зүйлийг тодорхойлдог. баталгаажуулах арга. Өөрөөр хэлбэл, хандалтын цэгийг өөрөө ашиглах, ачааллыг хэрхэн шифрлэхийг зөвшөөрөхийн тулд төхөөрөмж болон хандалтын цэг нь ямар дарааллаар ажиллах ёстой вэ. Энэ нийтлэлийг бичиж байх үед хамгийн түгээмэл сонголт бол WPA ба WPA2 бөгөөд тэдгээр нь төхөөрөмж бүрийг шууд эсвэл дамжуулан холбож өгдөг. RADIUS сервер (WPA-Enterprice) нь шифрлэгдсэн сувгаар нууц үг өгдөг. Магадгүй таны гэрт нэвтрэх цэг нь энэ схемийн дагуу холболтыг хангадаг. Хоёрдахь хувилбар болон эхний хувилбарын хоорондох ялгаа нь илүү хүчтэй шифртэй байдаг: AES нь найдвартай биш TKIP-тэй харьцуулахад. Илүү төвөгтэй, дэвшилтэт WPA3-ийг мөн аажмаар нэвтрүүлж байна. Онолын хувьд CCKM (Cisco Төвлөрсөн Түлхүүр Удирдлага) гэсэн аж ахуйн нэгжийн шийдэлтэй сонголт байж болох ч би үүнийг хэзээ ч харж байгаагүй.

Хандалтын цэгийг MAC хаягаар баталгаажуулахаар тохируулсан байж магадгүй. Эсвэл, хэрэв хандалтын цэг нь хуучирсан WEP алгоритмыг ашиглан өгөгдөл өгдөг бол үнэн хэрэгтээ баталгаажуулалт байхгүй болно (энд нууц түлхүүр нь шифрлэлтийн түлхүүр юм). Бид ийм сонголтыг БУСАД гэж ангилдаг.
Мөн олон нийтийн wi-fi-д түгээмэл хэрэглэгддэг, олзлогдсон портал илрүүлэх далд арга байдаг - хөтчөөр дамжуулан баталгаажуулах хүсэлт. Ийм хандалтын цэгүүд нь сканнерт нээлттэй мэт харагддаг (энэ нь физик холболтын үүднээс). Тиймээс бид тэдгээрийг НЭЭЛТТЭЙ гэж ангилдаг.

Хоёр дахь утгыг дараах байдлаар тэмдэглэж болно гол удирдлагын алгоритм. Энэ нь дээр дурдсан баталгаажуулалтын аргын параметр юм. Шифрлэлтийн түлхүүрүүдийг яг хэрхэн сольж байгаа талаар ярьдаг. Боломжит хувилбаруудыг авч үзье. EAP - дурдсан WPA-Enterprice-д ашигладаг бөгөөд оруулсан баталгаажуулалтын өгөгдлийг шалгахын тулд мэдээллийн санг ашигладаг. SAE - дэвшилтэт WPA3-д ашиглагддаг, харгис хэрцгий хүчинд илүү тэсвэртэй. PSK - хамгийн түгээмэл сонголт бол нууц үг оруулах, шифрлэгдсэн хэлбэрээр дамжуулах явдал юм. IEEE8021X - олон улсын стандартын дагуу (WPA гэр бүлийн дэмждэг стандартаас өөр). OWE (Opportunistic Wireless Encryption) нь бидний НЭЭЛТТЭЙ гэж ангилсан онооны IEEE 802.11 стандартын өргөтгөл юм. OWE нь хамгаалалтгүй сүлжээгээр дамжуулж буй мэдээллийн аюулгүй байдлыг шифрлэх замаар баталгаажуулдаг. Хандалтын түлхүүр байхгүй үед сонголт бас боломжтой, энэ сонголтыг NONE гэж нэрлэе.

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

Гурван элементийн оронд та ганцаардсан WEP тэмдгийг олж болно:

[WEP]

Дээр дурдсанчлан, энэ нь байхгүй түлхүүрүүдийг ашиглах алгоритм болон анхдагчаар ижил шифрлэлтийн аргыг зааж өгөхгүй байхад хангалттай юм.

Одоо энэ хаалтыг авч үзье:

[ESS]

энэ Wi-Fi үйлдлийн горим буюу Wi-Fi сүлжээний топологи. Холбогдсон төхөөрөмжүүд хоорондоо холбогддог нэг хандалтын цэг байгаа үед та BSS (Үндсэн үйлчилгээний багц) горимтой тулгарч магадгүй юм. Дотоод сүлжээнээс олж болно. Дүрмээр бол хандалтын цэгүүд нь өөр өөр дотоод сүлжээнээс төхөөрөмжүүдийг холбоход шаардлагатай байдаг тул тэдгээр нь Өргөтгөсөн үйлчилгээний багц - ESS-ийн нэг хэсэг юм. IBSSs (Independent Basic Service Sets) төрөл нь төхөөрөмж нь Peer-to-Peer сүлжээний нэг хэсэг гэдгийг харуулж байна.

Та мөн WPS тугийг харж болно:

[WPS]

WPS (Wi-Fi Protected Setup) нь Wi-Fi сүлжээг хагас автоматаар эхлүүлэх протокол юм. Эхлэхийн тулд хэрэглэгч 8 тэмдэгтээс бүрдэх нууц үг оруулах эсвэл чиглүүлэгч дээрх товчлуурыг дарна. Хэрэв таны хандалтын цэг эхний төрлийнх бөгөөд таны хандалтын цэгийн нэрний хажууд энэ нүд гарч байвал админ самбар руу орж WPS хандалтыг идэвхгүй болгохыг зөвлөж байна. Баримт нь ихэвчлэн 8 оронтой ПИН кодыг 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

Програмын кодоос сүлжээнд холбогдох асуудал шалгагдаагүй хэвээр байна. Хөдөлгөөнт төхөөрөмжийн үйлдлийн системээс хадгалсан нууц үгийг уншихын тулд танд root эрх, wpa_supplicant.conf-г уншихын тулд файлын системээр гүйлгэх хүсэл хэрэгтэй гэдгийг л хэлье. Хэрэглээний логик нь гаднаас нууц үг оруулах шаардлагатай бол классаар дамжуулан холболт хийж болно android.net.wifi.WifiManager.

Спасибо Егор Пономарев үнэ цэнэтэй нэмэлтүүдийн хувьд.

Хэрэв та ямар нэг зүйл нэмэх эсвэл засах шаардлагатай гэж бодож байвал сэтгэгдэл дээр бичээрэй :)

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх