En dag trengte jeg å skanne Wi-Fi-nettverk fra Android-applikasjoner og få detaljerte data om tilgangspunkter.
Her måtte vi møte flere vanskeligheter:
Denne artikkelen diskuterer hvordan du kan få omfattende data om Wi-Fi-miljøet fra Android-kode uten NDK, hacks, men bare ved å bruke Android API og forstå hvordan du tolker det.
La oss ikke utsette og begynne å skrive kode.
1. Lag et prosjekt
Dette notatet er ment for de som har opprettet et Android-prosjekt mer enn én gang, så vi utelater detaljene for dette elementet. Koden nedenfor vil bli presentert i Kotlin, minSdkVersion=23.
2. Tilgangstillatelser
For å jobbe med Wi-Fi fra applikasjonen, må du få flere tillatelser fra brukeren. I samsvar med
Så i AndroidManifest.xml legger vi til:
<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 som inneholder en lenke til gjeldende 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. Opprett en kringkastingsmottaker og abonner på dataoppdateringshendelser om skanning av Wi-Fi-nettverksmiljø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 dokumentasjonen er merket som avskrevet siden API-versjon 28, men av.
Totalt fikk vi en liste over objekter
4. Se på ScanResult og forstå vilkårene
La oss se på noen felt i denne klassen og beskrive hva de betyr:
SSID — Service Set Identifier er navnet på nettverket
BSSID – Basic Service Set Identifier – MAC-adressen til nettverksadapteren (Wi-Fi-punkt)
nivå — Mottatt signalstyrkeindikator [dBm (russisk dBm) — Desibel, referanseeffekt 1 mW.] — En indikator for den mottatte signalstyrken. Tar en verdi fra 0 til -100, jo lenger fra 0, jo mer signalstyrke gikk tapt underveis fra Wi-Fi-punktet til enheten. Flere detaljer finner du for eksempel på
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val numberOfLevels = 5
val level = WifiManager.calculateSignalLevel(level, numberOfLevels)
frekvens — driftsfrekvensen til Wi-Fi-punktet [Hz]. I tillegg til selve frekvensen kan du være interessert i den såkalte kanalen. Hvert punkt har sin egen driftsrenhet. I skrivende stund er det mest populære utvalget av Wi-Fi-punkter 2.4 GHz. Men for å være mer presis, sender punktet informasjon til telefonen din med en nummerert frekvens nær den som er nevnt. Antall 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
}
}
evner - det mest interessante feltet for analyse, arbeid med som krevde mye tid. Her er "egenskapene" til punktet skrevet i linjen. I dette tilfellet trenger du ikke se etter detaljer om strengtolkning i dokumentasjonen. Her er noen eksempler på hva som kan stå i denne linjen:
[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å forkortelser og analysefunksjoner
Det er verdt å nevne at klassene til android.net.wifi.*-pakken brukes under panseret av et Linux-verktøy
Vi vil handle konsekvent. La oss først vurdere utdataene til et format der elementer i parenteser er atskilt med et "-"-tegn:
[WPA-PSK-TKIP+CCMP]
[WPA2-PSK-CCMP]
Den første betydningen beskriver den såkalte. autentiseringsmetode. Det vil si hvilken rekkefølge av handlinger må enheten og tilgangspunktet utføre for at tilgangspunktet skal tillate seg å brukes og hvordan man krypterer nyttelasten. I skrivende stund dette innlegget er de vanligste alternativene WPA og WPA2, der enten hver tilkoblet enhet direkte eller gjennom den såkalte. RADIUS-serveren (WPA-Enterprice) gir passordet over en kryptert kanal. Mest sannsynlig gir tilgangspunktet i hjemmet en forbindelse i henhold til denne ordningen. Forskjellen mellom den andre versjonen og den første er at den har en sterkere chiffer: AES versus den usikre TKIP. WPA3, som er mer kompleks og avansert, blir også gradvis introdusert. Teoretisk sett kan det være et alternativ med enterprice-løsningen CCKM (Cisco Centralized Key Management), men jeg har aldri vært borti den.
Tilgangspunktet kan ha blitt konfigurert til å autentisere med MAC-adresse. Eller, hvis tilgangspunktet gir data ved hjelp av den utdaterte WEP-algoritmen, er det faktisk ingen autentisering (den hemmelige nøkkelen her er krypteringsnøkkelen). Vi klassifiserer slike alternativer som ANDRE.
Det er også en metode som er populær i offentlig wi-fi med skjult Captive Portal Detection - en autentiseringsforespørsel gjennom en nettleser. Slike tilgangspunkter fremstår for skanneren som åpne (som de er fra den fysiske tilkoblingens synspunkt). Derfor klassifiserer vi dem som ÅPNE.
Den andre verdien kan betegnes som nøkkelstyringsalgoritme. Dette er en parameter for autentiseringsmetoden beskrevet ovenfor. Snakker om nøyaktig hvordan krypteringsnøkler utveksles. La oss vurdere de mulige alternativene. EAP - brukt i nevnte WPA-Enterprice, bruker en database for å verifisere de angitte autentiseringsdataene. SAE - brukt i avansert WPA3, mer motstandsdyktig mot brute force. PSK - det vanligste alternativet, innebærer å skrive inn et passord og overføre det i kryptert form. IEEE8021X - i henhold til en internasjonal standard (forskjellig fra den som støttes av WPA-familien). OWE (Opportunistic Wireless Encryption) er en utvidelse av IEEE 802.11-standarden for punkter som vi har klassifisert som ÅPNE. OWE sikrer sikkerheten til data som overføres over et usikret nettverk ved å kryptere dem. Et alternativ er også mulig når det ikke er noen tilgangsnøkler, la oss kalle dette alternativet INGEN.
Den tredje parameteren er den såkalte. krypteringsordninger — nøyaktig hvordan chifferen brukes for å beskytte de overførte dataene. La oss liste opp alternativene. WEP - bruker et RC4-strømchiffer, den hemmelige nøkkelen er krypteringsnøkkelen, som anses som uakseptabel i moderne kryptografi. TKIP - brukt i WPA, CKIP - i WPA2. TKIP+CKIP - kan spesifiseres i punkter som kan WPA og WPA2 for bakoverkompatibilitet.
I stedet for tre elementer kan du finne et ensomt WEP-merke:
[WEP]
Som vi diskuterte ovenfor, er dette nok til å ikke spesifisere algoritmen for bruk av nøkler, som ikke eksisterer, og krypteringsmetoden, som er den samme som standard.
Vurder nå denne parentesen:
[ESS]
Den Wi-Fi driftsmodus eller Wi-Fi-nettverkstopologi. Du kan støte på BSS-modus (Basic Service Set) - når det er ett tilgangspunkt som tilkoblede enheter kommuniserer gjennom. Finnes på lokale nettverk. Som regel er det nødvendig med tilgangspunkter for å koble til enheter fra forskjellige lokale nettverk, så de er en del av Extended Service Sets - ESS. IBSS-typene (Independent Basic Service Sets) indikerer at enheten er en del av et node-til-node-nettverk.
Du kan også se WPS-flagget:
[WPS]
WPS (Wi-Fi Protected Setup) er en protokoll for halvautomatisk initialisering av et Wi-Fi-nettverk. For å initialisere skriver brukeren enten inn et 8-tegns passord eller trykker på en knapp på ruteren. Hvis tilgangspunktet ditt er av den første typen og denne avmerkingsboksen vises ved siden av navnet på tilgangspunktet, anbefales det på det sterkeste å gå til administrasjonspanelet og deaktivere WPS-tilgang. Faktum er at ofte kan den 8-sifrede PIN-koden bli funnet ut av MAC-adressen, eller den kan sorteres ut i løpet av overskuelig tid, noe noen uærlig kan dra nytte av.
6. Lag en modell og parsefunksjon
Basert på det vi fant ut ovenfor, vil vi beskrive hva som skjedde ved hjelp av 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.
}
La oss nå skrive en funksjon som vil analysere funksjonsfeltet:
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 skanner nettverket og viser deg hva jeg fant. Vist er resultatene av enkel utdata 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ørsmålet om å koble til nettverket fra applikasjonskoden forble uundersøkt. Jeg vil bare si at for å lese lagrede passord fra operativsystemet til en mobilenhet, trenger du rotrettigheter og vilje til å rote gjennom filsystemet for å lese wpa_supplicant.conf. Hvis applikasjonslogikken krever inntasting av passord utenfra, kan tilkoblingen gjøres gjennom klassen
Takk
Hvis du mener noe må legges til eller rettes, skriv i kommentarfeltet :)
Kilde: www.habr.com