Un ghjornu avia bisognu di scansà e rete Wi-Fi da l'applicazioni Android è uttene dati detallati nantu à i punti d'accessu.
Quì avemu avutu affruntà parechje difficultà:
Stu articulu discute quantu à ottene dati cumpleta circa l 'ambienti Wi-Fi da codice Android senza NDK, pirate, ma solu cù l' API Android è capisce quantu à interpretà lu.
Ùn ritardemu micca è cuminciamu à scrive codice.
1. Crea un prughjettu
Questa nota hè destinata à quelli chì anu creatu un prughjettu Android più di una volta, cusì omettemu i dettagli di questu articulu. U codice sottu serà prisentatu in Kotlin, minSdkVersion=23.
2. Access permissions
Per travaglià cù Wi-Fi da l'applicazione, avete bisognu di ottene parechji permessi da l'utilizatore. In cunfurmità cù
Allora, in AndroidManifest.xml aghjunghjemu:
<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"/>
È in u codice chì cuntene un ligame à l'attività attuale:
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. Crea un BroadcastReceiver è abbonate à l'eventi di l'aghjurnamentu di dati nantu à scanning l'ambiente di a rete 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
}
U mètudu WiFiManager.startScan in a documentazione hè marcatu cum'è depricatu da a versione API 28, ma off.
In tuttu, avemu ricevutu una lista di l'uggetti
4. Fighjate à ScanResult è capisce i termini
Fighjemu certi campi di sta classa è discrìvemu ciò chì significanu:
SSID — Service Set Identifier hè u nome di a reta
BSSID - Basic Service Set Identifier - Indirizzu MAC di l'adattatore di rete (puntu Wi-Fi)
livellu - Ricivutu Signal Strength Indicator [dBm (Russian dBm) - Decibel, putenza di riferimentu 1 mW.] - Un indicatore di a forza di u signale ricevutu. Piglia un valore da 0 à -100, u più luntanu da 0, u più putere di signale hè stata persa in a strada da u puntu Wi-Fi à u vostru dispositivu. Più dettagli ponu esse truvati, per esempiu, à
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val numberOfLevels = 5
val level = WifiManager.calculateSignalLevel(level, numberOfLevels)
frequency | - frequenza operativa di u puntu Wi-Fi [Hz]. In più di a freccia stessu, pudete esse interessatu in u chjamatu canali. Ogni puntu hà a so purezza operativa. À u mumentu di a scrittura, a gamma più famosa di punti Wi-Fi hè 2.4 GHz. Ma, per esse più precisu, u puntu trasmette infurmazione à u vostru telefunu à una freccia numerata vicinu à quellu chjamatu. Numero di canali è frequenze currispundenti
/* по частоте определяем номер канала */
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
}
}
capacità - u campu più interessante per l'analisi, u travagliu cù quale hà bisognu di assai tempu. Quì i "capacità" di u puntu sò scritti in a linea. In questu casu, ùn avete micca bisognu di circà i dettagli di l'interpretazione di stringa in a documentazione. Eccu alcuni esempi di ciò chì puderia esse in questa linea:
[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. Capisce l'abbreviazioni è e capacità di parsing
Hè da nutà chì e classi di u pacchettu android.net.wifi.* sò usati sottu u cappucciu da una utilità Linux.
Agiremu in modu coerente. Cunsideremu prima l'output di un furmatu in quale elementi in parentesi sò separati da un signu "-":
[WPA-PSK-TKIP+CCMP]
[WPA2-PSK-CCMP]
U primu significatu descrive u cusì chjamatu. metudu di autentificazione. Questu hè, chì sequenza d'azzioni deve esse realizatu u dispusitivu è u puntu d'accessu per chì u puntu d'accessu si permette d'utilizà è cumu per criptà a carica. A l'ora di scrive stu post, l'opzioni più cumuni sò WPA è WPA2, in quale sia ogni dispusitivu cunnessu direttamente o attraversu u cusì chjamatu. U servitore RADIUS (WPA-Enterprice) furnisce a password nantu à un canale criptatu. Probabilmente, u puntu d'accessu in a vostra casa furnisce una cunnessione secondu stu schema. A diferenza trà a seconda versione è a prima hè chì hà un cifru più forte: AES versus TKIP insecure. WPA3, chì hè più cumplessu è avanzatu, hè ancu introduttu gradualmente. Teoricamente, ci pò esse una opzione cù a soluzione intrapresa CCKM (Cisco Centralized Key Management), ma ùn aghju mai scontru.
U puntu d'accessu pò esse cunfiguratu per autentificà per l'indirizzu MAC. Or, se u puntu d'accessu furnisce dati cù l'algoritmu WEP anticu, allora ùn ci hè micca autentificazione (a chjave secreta quì hè a chjave di criptografia). Classificamu tali opzioni cum'è OTHER.
Ci hè ancu un metudu chì hè populari in u wi-fi publicu cù a Rilevazione di Portal Captive nascosta - una dumanda di autentificazione attraversu un navigatore. Tali punti d'accessu pareanu à u scanner cum'è aperti (chì sò da u puntu di vista di a cunnessione fisica). Dunque, li classificàmu cum'è OPEN.
U sicondu valore pò esse indicatu cum'è algoritmu di gestione chjave. Questu hè un paràmetru di u metudu di autentificazione descrittu sopra. Parla esattamente cumu si scambianu e chjave di criptografia. Cunsideremu l'opzioni pussibuli. EAP - utilizatu in u WPA-Enterprice citatu, usa una basa di dati per verificà i dati di autentificazione inseriti. SAE - utilizatu in WPA3 avanzatu, più resistente à a forza bruta. PSK - l'opzione più cumuna, implica l'ingressu di una password è a trasmissione in forma criptata. IEEE8021X - secondu un standard internaziunale (differente da quellu supportatu da a famiglia WPA). OWE (Opportunistic Wireless Encryption) hè una estensione di u standard IEEE 802.11 per i punti chì avemu classificatu cum'è OPEN. OWE assicura a securità di e dati trasmessi nantu à una rete micca assicurata criptendu. Una opzione hè ancu pussibule quandu ùn ci sò micca chjave d'accessu, chjamemu questa opzione NONE.
U terzu paràmetru hè u cusì chjamatu. schemi di criptografia - cumu esattamente u cifru hè utilizatu per prutege i dati trasmessi. Elenchemu l'opzioni. WEP - usa un cifru di flussu RC4, a chjave secreta hè a chjave di criptografia, chì hè cunsideratu inacceptable in u mondu di a criptografia muderna. TKIP - utilizatu in WPA, CKIP - in WPA2. TKIP + CKIP - pò esse specificatu in punti capaci di WPA è WPA2 per a cumpatibilità inversa.
Invece di trè elementi, pudete truvà una marca WEP solitaria:
[WEP]
Cumu avemu discututu sopra, questu hè abbastanza per micca specificà l'algoritmu per l'usu di e chjave, chì ùn esiste micca, è u metudu di criptografia, chì hè u listessu per automaticamente.
Avà cunsiderà sta parentesi:
[ESS]
issu Modu operativu Wi-Fi o Topulugia di a rete Wi-Fi. Pudete scuntrà u modu BSS (Set di Serviziu Bascu) - quandu ci hè un puntu d'accessu attraversu quale i dispositi cunnessi cumunicanu. Pò esse truvatu nantu à e rete lucali. Comu regula, i punti d'accessu sò necessarii per cunnette i dispositi da e diverse rete lucali, per quessa, sò parti di Sets di Serviziu Estensi - ESS. U tipu IBSSs (Sets di Serviziu Basic Indipendenti) indica chì u dispusitivu hè parti di una reta Peer-to-Peer.
Pudete ancu vede a bandiera WPS:
[WPS]
WPS (Wi-Fi Protected Setup) hè un protokollu per l'inizializazione semi-automatica di una rete Wi-Fi. Per inizializà, l'utilizatore entra in una password di 8 caratteri o pressu un buttone nantu à u router. Se u vostru puntu d'accessu hè di u primu tipu è sta casella di spunta cumparisce accantu à u nome di u vostru puntu d'accessu, vi cunsigliu assai di andà à u pannellu di amministrazione è disattivà l'accessu WPS. U fattu hè chì spessu u PIN di 8 cifre pò esse scupertu da l'indirizzu MAC, o pò esse risoltu in un tempu prevedibile, chì qualcunu pò apprufittà dishonestly.
6. Crea un mudellu è funzione parsing
Basatu nantu à ciò chì avemu scupertu sopra, descriveremu ciò chì hè accadutu cù e classi di dati:
/* схема аутентификации */
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.
}
Avà scrivemu una funzione chì analizà u campu di capacità:
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. Vede u risultatu
Scanneraghju a reta è vi mustraraghju ciò chì aghju trovu. I risultati di l'output simplici via Log.d sò mostrati:
Capability of Home-Home [WPA2-PSK-CCMP][ESS][WPS]
...
capabilities=[Capability(authScheme=WPA2, keyManagementAlgorithm=PSK, cipherMethod=CCMP)], topologyMode=ESS, availableWps=true
U prublema di cunnessione à a reta da u codice di l'applicazione ùn hè micca esaminatu. Diceraghju solu chì per leghje e password salvate da u SO di un dispositivu mobile, avete bisognu di diritti di root è una vuluntà di rummage through the file system per leghje wpa_supplicant.conf. Se a logica di l'applicazione richiede l'inserimentu di una password da l'esternu, a cunnessione pò esse fatta attraversu a classe
Спасибо
Se pensate chì qualcosa deve esse aghjuntu o currettu, scrivite in i cumenti :)
Source: www.habr.com