Wi-Fi και πολλές άλλες συντομογραφίες. Πώς να λάβετε δεδομένα σχετικά με τους κόμβους Wi-Fi σε μια εφαρμογή Android χωρίς να διογκωθείτε

Μια μέρα χρειάστηκε να σαρώσω δίκτυα Wi-Fi από εφαρμογές Android και να λάβω λεπτομερή δεδομένα σχετικά με τα σημεία πρόσβασης.

Εδώ αντιμετωπίσαμε αρκετές δυσκολίες: off.Τεκμηρίωση Android Πολλές από τις περιγραφόμενες κατηγορίες καταργήθηκαν (επίπεδο API > 26), κάτι που δεν αντικατοπτρίστηκε σε αυτό. η περιγραφή ορισμένων πραγμάτων στην τεκμηρίωση είναι ελάχιστη (για παράδειγμα, το πεδίο δυνατοτήτων της τάξης ScanResult τη στιγμή της σύνταξης, σχεδόν τίποτα δεν περιγράφεται, αν και περιέχει πολλά σημαντικά δεδομένα). Η τρίτη δυσκολία μπορεί να έγκειται στο γεγονός ότι όταν πλησιάζετε για πρώτη φορά στο Wi-Fi, εκτός από την ανάγνωση της θεωρίας και τη ρύθμιση του δρομολογητή μέσω localhost, πρέπει να αντιμετωπίσετε μια σειρά από συντομογραφίες που φαίνονται κατανοητές μεμονωμένα. Αλλά μπορεί να μην είναι προφανές πώς να τα συσχετίσετε και να τα δομήσετε (η κρίση είναι υποκειμενική και εξαρτάται από την προηγούμενη εμπειρία).

Αυτό το άρθρο περιγράφει πώς μπορείτε να αποκτήσετε ολοκληρωμένα δεδομένα σχετικά με το περιβάλλον Wi-Fi από κώδικα Android χωρίς NDK, hacks, αλλά μόνο χρησιμοποιώντας το API 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 στην τεκμηρίωση έχει επισημανθεί ως καταργημένη από την έκδοση 28 του API, αλλά είναι απενεργοποιημένη. καθοδηγήσει προτείνει τη χρήση του.

Συνολικά, λάβαμε μια λίστα με αντικείμενα ScanResult.

4. Κοιτάξτε το ScanResult και κατανοήστε τους όρους

Ας δούμε μερικά πεδία αυτής της κατηγορίας και ας περιγράψουμε τι σημαίνουν:

SSID — Αναγνωριστικό συνόλου υπηρεσιών είναι το όνομα του δικτύου

BSSID – Αναγνωριστικό βασικού συνόλου υπηρεσιών – Διεύθυνση MAC του προσαρμογέα δικτύου (σημείο Wi-Fi)

επίπεδο — Ένδειξη ισχύος ληφθέντος σήματος [dBm (Ρωσικά dBm) — Decibel, ισχύς αναφοράς 1 mW.] — Ένδειξη της ισχύος του λαμβανόμενου σήματος. Λαμβάνει μια τιμή από 0 έως -100, όσο πιο μακριά από το 0, τόσο περισσότερη ισχύς σήματος χάθηκε κατά τη διαδρομή από το σημείο Wi-Fi στη συσκευή σας. Περισσότερες λεπτομέρειες μπορείτε να βρείτε, για παράδειγμα, στη διεύθυνση Wikipedia. Εδώ θα σας πω ότι χρησιμοποιώντας την τάξη Android Διαχειριστής WiFi μπορείτε να βαθμονομήσετε τη στάθμη του σήματος σε μια κλίμακα από εξαιρετική έως τρομερή στο βήμα που επιλέγετε:

        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 GHz. Αλλά, για να είμαστε πιο ακριβείς, το σημείο μεταδίδει πληροφορίες στο τηλέφωνό σας σε αριθμημένη συχνότητα κοντά σε αυτήν που ονομάζεται. Αριθμός καναλιών και αντίστοιχες συχνότητες τυποποιημένη. Αυτό γίνεται έτσι ώστε τα κοντινά σημεία να λειτουργούν σε διαφορετικές συχνότητες, με αποτέλεσμα να μην παρεμβάλλονται μεταξύ τους και να μην μειώνεται αμοιβαία η ταχύτητα και η ποιότητα μετάδοσης. Σε αυτή την περίπτωση, τα σημεία δεν λειτουργούν σε μία συχνότητα, αλλά σε ένα εύρος συχνοτήτων (παράμετρος πλάτος καναλιού), ονομάζεται πλάτος καναλιού. Δηλαδή, σημεία που λειτουργούν σε γειτονικά (και όχι μόνο γειτονικά, αλλά ακόμη και 3 από τα ίδια) κανάλια παρεμβαίνουν μεταξύ τους. Μπορεί να σας φανεί χρήσιμος αυτός ο απλός κώδικας, ο οποίος σας επιτρέπει να υπολογίσετε τον αριθμό του καναλιού από την τιμή συχνότητας για σημεία με συχνότητα 2.4 και 5 Ghz:


    /* по частоте определяем номер канала */
    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, το οποίο είναι πιο περίπλοκο και προηγμένο, εισάγεται επίσης σταδιακά. Θεωρητικά, μπορεί να υπάρχει μια επιλογή με τη λύση Enterprice CCKM (Cisco Centralized Key Management), αλλά δεν την έχω συναντήσει ποτέ.

Το σημείο πρόσβασης μπορεί να έχει διαμορφωθεί για έλεγχο ταυτότητας με διεύθυνση MAC. Ή, εάν το σημείο πρόσβασης παρέχει δεδομένα χρησιμοποιώντας τον απαρχαιωμένο αλγόριθμο WEP, τότε στην πραγματικότητα δεν υπάρχει έλεγχος ταυτότητας (το μυστικό κλειδί εδώ είναι το κλειδί κρυπτογράφησης). Κατατάσσουμε τέτοιες επιλογές ως ΑΛΛΕΣ.
Υπάρχει επίσης μια μέθοδος που είναι δημοφιλής στο δημόσιο wi-fi με κρυφή ανίχνευση αιχμαλώτων πύλης - ένα αίτημα ελέγχου ταυτότητας μέσω ενός προγράμματος περιήγησης. Τέτοια σημεία πρόσβασης εμφανίζονται στον σαρωτή ως ανοιχτά (που είναι από την άποψη της φυσικής σύνδεσης). Επομένως, τα ταξινομούμε ως ΑΝΟΙΚΤΑ.

Η δεύτερη τιμή μπορεί να συμβολιστεί ως αλγόριθμος διαχείρισης κλειδιών. Αυτή είναι μια παράμετρος της μεθόδου ελέγχου ταυτότητας που περιγράφεται παραπάνω. Μιλάει για το πώς ακριβώς ανταλλάσσονται τα κλειδιά κρυπτογράφησης. Ας εξετάσουμε τις πιθανές επιλογές. EAP - χρησιμοποιείται στο αναφερόμενο WPA-Enterprice, χρησιμοποιεί μια βάση δεδομένων για την επαλήθευση των καταχωρημένων δεδομένων ελέγχου ταυτότητας. SAE - χρησιμοποιείται σε προηγμένο WPA3, πιο ανθεκτικό στην ωμή βία. PSK - η πιο κοινή επιλογή, περιλαμβάνει την εισαγωγή ενός κωδικού πρόσβασης και τη μετάδοσή του σε κρυπτογραφημένη μορφή. IEEE8021X - σύμφωνα με ένα διεθνές πρότυπο (διαφορετικό από αυτό που υποστηρίζεται από την οικογένεια WPA). Το OWE (Opportunistic Wireless Encryption) είναι μια επέκταση του προτύπου IEEE 802.11 για σημεία που ταξινομήσαμε ως OPEN. Το OWE διασφαλίζει την ασφάλεια των δεδομένων που μεταδίδονται μέσω ενός μη ασφαλούς δικτύου κρυπτογραφώντας το. Μια επιλογή είναι επίσης δυνατή όταν δεν υπάρχουν κλειδιά πρόσβασης, ας ονομάσουμε αυτήν την επιλογή ΚΑΜΙΑ.

Η τρίτη παράμετρος είναι η λεγόμενη. σχήματα κρυπτογράφησης — πώς ακριβώς χρησιμοποιείται η κρυπτογράφηση για την προστασία των μεταδιδόμενων δεδομένων. Ας παραθέσουμε τις επιλογές. WEP - χρησιμοποιεί έναν κρυπτογράφηση ροής RC4, το μυστικό κλειδί είναι το κλειδί κρυπτογράφησης, το οποίο θεωρείται απαράδεκτο στον κόσμο της σύγχρονης κρυπτογραφίας. TKIP - χρησιμοποιείται σε WPA, CKIP - σε WPA2. TKIP+CKIP - μπορεί να καθοριστεί σε σημεία με δυνατότητα WPA και WPA2 για συμβατότητα προς τα πίσω.

Αντί για τρία στοιχεία, μπορείτε να βρείτε ένα μοναχικό σήμα WEP:

[WEP]

Όπως συζητήσαμε παραπάνω, αυτό αρκεί για να μην καθοριστεί ο αλγόριθμος για τη χρήση κλειδιών, ο οποίος δεν υπάρχει, και η μέθοδος κρυπτογράφησης, η οποία είναι η ίδια από προεπιλογή.

Τώρα λάβετε υπόψη αυτήν την παρένθεση:

[ESS]

Το Λειτουργία Wi-Fi ή Τοπολογία δικτύου Wi-Fi. Ενδέχεται να αντιμετωπίσετε τη λειτουργία BSS (Basic Service Set) - όταν υπάρχει ένα σημείο πρόσβασης μέσω του οποίου επικοινωνούν οι συνδεδεμένες συσκευές. Μπορεί να βρεθεί σε τοπικά δίκτυα. Κατά κανόνα, τα σημεία πρόσβασης χρειάζονται για τη σύνδεση συσκευών από διαφορετικά τοπικά δίκτυα, επομένως αποτελούν μέρος των Εκτεταμένων Συνόλων Υπηρεσιών - 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

Το ζήτημα της σύνδεσης στο δίκτυο από τον κωδικό εφαρμογής παρέμεινε ανεξέταστο. Θα πω μόνο ότι για να διαβάσετε τους αποθηκευμένους κωδικούς πρόσβασης του λειτουργικού συστήματος μιας κινητής συσκευής, χρειάζεστε δικαιώματα root και προθυμία να ψάξετε στο σύστημα αρχείων για να διαβάσετε το wpa_supplicant.conf. Εάν η λογική της εφαρμογής απαιτεί την εισαγωγή ενός κωδικού πρόσβασης από έξω, η σύνδεση μπορεί να γίνει μέσω της κλάσης android.net.wifi.WifiManager.

σας ευχαριστώ Έγκορ Πονομάρεφ για πολύτιμες προσθήκες.

Εάν πιστεύετε ότι κάτι πρέπει να προστεθεί ή να διορθωθεί, γράψτε στα σχόλια :)

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο