En dag havde jeg brug for at scanne Wi-Fi-netværk fra Android-applikationer og få detaljerede data om adgangspunkter.
Her stod vi over for flere vanskeligheder:
Denne artikel diskuterer, hvordan man opnår omfattende data om Wi-Fi-miljøet fra Android-kode uden NDK, hacks, men kun ved hjælp af Android API og forstår, hvordan man fortolker det.
Lad os ikke forsinke og begynde at skrive kode.
1. Opret et projekt
Denne note er beregnet til dem, der har oprettet et Android-projekt mere end én gang, så vi udelader detaljerne i dette element. Koden nedenfor vil blive præsenteret i Kotlin, minSdkVersion=23.
2. Adgangstilladelser
For at arbejde med Wi-Fi fra applikationen skal du indhente flere tilladelser fra brugeren. I overensstemmelse med
Så i AndroidManifest.xml tilføjer vi:
<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"/>
Og i koden, der indeholder et link til den aktuelle aktivitet:
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. Opret en BroadcastReceiver og abonner på dataopdateringshændelser om scanning af Wi-Fi-netværksmiljøet
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-metoden i dokumentationen er markeret som afskrevet siden API-version 28, men deaktiveret.
I alt modtog vi en liste over genstande
4. Se på ScanResult og forstå vilkårene
Lad os se på nogle felter i denne klasse og beskrive, hvad de betyder:
SSID — Service Set Identifier er navnet på netværket
BSSID – Basic Service Set Identifier – MAC-adresse på netværksadapteren (Wi-Fi-punkt)
niveau — Indikator for modtaget signalstyrke [dBm (russisk dBm) — Decibel, referenceeffekt 1 mW.] — En indikator for den modtagne signalstyrke. Tager en værdi fra 0 til -100, jo længere fra 0, jo mere signalstyrke blev tabt undervejs fra Wi-Fi-punktet til din enhed. Flere detaljer kan f.eks. findes på
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val numberOfLevels = 5
val level = WifiManager.calculateSignalLevel(level, numberOfLevels)
frekvens — driftsfrekvens for Wi-Fi-punktet [Hz]. Ud over selve frekvensen kan du være interesseret i den såkaldte kanal. Hvert punkt har sin egen driftsrenhed. I skrivende stund er det mest populære udvalg af Wi-Fi-punkter 2.4 GHz. Men for at være mere præcis sender punktet information til din telefon med en nummereret frekvens tæt på den nævnte. Antal kanaler og tilsvarende frekvenser
/* по частоте определяем номер канала */
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
}
}
kapaciteter - det mest interessante felt for analyse, arbejde med hvilket krævede meget tid. Her er punktets "evner" skrevet i linjen. I dette tilfælde behøver du ikke lede efter detaljer om strengfortolkning i dokumentationen. Her er nogle eksempler på, hvad der kan være i denne linje:
[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. Forståelse af forkortelser og parsefunktioner
Det er værd at nævne, at klasserne i android.net.wifi.*-pakken bruges under emhætten af et Linux-værktøj
Vi vil handle konsekvent. Lad os først overveje outputtet af et format, hvor elementer inden for parentes er adskilt af et "-"-tegn:
[WPA-PSK-TKIP+CCMP]
[WPA2-PSK-CCMP]
Den første betydning beskriver den såkaldte. autentificeringsmetode. Det vil sige, hvilken rækkefølge af handlinger skal enheden og adgangspunktet udføre, for at adgangspunktet kan tillade sig selv at blive brugt, og hvordan man krypterer nyttelasten. I skrivende stund er de mest almindelige muligheder WPA og WPA2, hvor enten hver tilsluttet enhed direkte eller gennem den såkaldte. RADIUS-serveren (WPA-Enterprice) leverer adgangskoden over en krypteret kanal. Mest sandsynligt giver adgangspunktet i dit hjem en forbindelse i henhold til denne ordning. Forskellen mellem den anden version og den første er, at den har en stærkere chiffer: AES versus den usikre TKIP. WPA3, som er mere kompleks og avanceret, er også gradvist ved at blive introduceret. Teoretisk set kan der være en mulighed med enterprice løsningen CCKM (Cisco Centralized Key Management), men jeg er aldrig stødt på det.
Adgangspunktet kan være konfigureret til at godkende ved hjælp af MAC-adresse. Eller, hvis adgangspunktet leverer data ved hjælp af den forældede WEP-algoritme, er der faktisk ingen godkendelse (den hemmelige nøgle her er krypteringsnøglen). Vi klassificerer sådanne muligheder som ANDRE.
Der er også en metode, der er populær i offentlig wi-fi med skjult Captive Portal Detection - en godkendelsesanmodning gennem en browser. Sådanne adgangspunkter fremstår for scanneren som åbne (hvilket de er set fra den fysiske forbindelses synspunkt). Derfor klassificerer vi dem som ÅBNE.
Den anden værdi kan betegnes som nøglestyringsalgoritme. Dette er en parameter for godkendelsesmetoden beskrevet ovenfor. Taler om præcis, hvordan krypteringsnøgler udveksles. Lad os overveje de mulige muligheder. EAP - brugt i den nævnte WPA-Enterprice, bruger en database til at verificere de indtastede autentificeringsdata. SAE - brugt i avanceret WPA3, mere modstandsdygtig over for brute force. PSK - den mest almindelige mulighed, involverer at indtaste en adgangskode og sende den i krypteret form. IEEE8021X - i henhold til en international standard (forskellig fra den, der understøttes af WPA-familien). OWE (Opportunistic Wireless Encryption) er en udvidelse af IEEE 802.11-standarden for punkter, som vi klassificerede som OPEN. OWE sikrer sikkerheden for data transmitteret over et usikkert netværk ved at kryptere dem. En mulighed er også mulig, når der ikke er nogen adgangsnøgler, lad os kalde denne mulighed INGEN.
Den tredje parameter er den såkaldte. krypteringsordninger — præcis hvordan chifferen bruges til at beskytte de overførte data. Lad os liste mulighederne. WEP - bruger en RC4 stream cipher, den hemmelige nøgle er krypteringsnøglen, hvilket anses for uacceptabelt i en verden af moderne kryptografi. TKIP - bruges i WPA, CKIP - i WPA2. TKIP+CKIP - kan specificeres i punkter, der er i stand til WPA og WPA2 for bagudkompatibilitet.
I stedet for tre elementer kan du finde et ensomt WEP-mærke:
[WEP]
Som vi diskuterede ovenfor, er dette nok til ikke at specificere algoritmen til brug af nøgler, som ikke eksisterer, og krypteringsmetoden, som er den samme som standard.
Overvej nu denne parentes:
[ESS]
Det Wi-Fi driftstilstand eller Wi-Fi-netværkstopologi. Du kan støde på BSS-tilstand (Basic Service Set) - når der er ét adgangspunkt, hvorigennem tilsluttede enheder kommunikerer. Kan findes på lokale netværk. Som regel er adgangspunkter nødvendige for at forbinde enheder fra forskellige lokale netværk, så de er en del af Extended Service Sets - ESS. Typen IBSS'er (Independent Basic Service Sets) angiver, at enheden er en del af et Peer-to-Peer-netværk.
Du kan også se WPS-flaget:
[WPS]
WPS (Wi-Fi Protected Setup) er en protokol til semi-automatisk initialisering af et Wi-Fi-netværk. For at initialisere, indtaster brugeren enten en adgangskode på 8 tegn eller trykker på en knap på routeren. Hvis dit adgangspunkt er af den første type, og dette afkrydsningsfelt vises ud for navnet på dit adgangspunkt, anbefales det kraftigt at gå til administrationspanelet og deaktivere WPS-adgang. Faktum er, at ofte kan den 8-cifrede PIN-kode findes ud af MAC-adressen, eller den kan ordnes inden for en overskuelig tid, hvilket nogen uærligt kan drage fordel af.
6. Opret en model og parsing funktion
Baseret på det, vi fandt ud af ovenfor, vil vi beskrive, hvad der skete ved hjælp af dataklasser:
/* схема аутентификации */
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.
}
Lad os nu skrive en funktion, der vil analysere kapacitetsfeltet:
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. Se resultatet
Jeg scanner netværket og viser dig, hvad jeg fandt. Vist er resultaterne af simpelt output via Log.d:
Capability of Home-Home [WPA2-PSK-CCMP][ESS][WPS]
...
capabilities=[Capability(authScheme=WPA2, keyManagementAlgorithm=PSK, cipherMethod=CCMP)], topologyMode=ESS, availableWps=true
Spørgsmålet om at oprette forbindelse til netværket fra applikationskoden forblev uundersøgt. Jeg vil kun sige, at for at læse de gemte adgangskoder til operativsystemet på en mobil enhed, skal du have root-rettigheder og en vilje til at rode gennem filsystemet for at læse wpa_supplicant.conf. Hvis applikationslogikken kræver indtastning af en adgangskode udefra, kan forbindelsen oprettes gennem klassen
Tak
Hvis du synes noget skal tilføjes eller rettes, så skriv i kommentarfeltet :)
Kilde: www.habr.com