Iun tagon mi bezonis skani Wifi-retojn de Android-aplikoj kaj akiri detalajn datumojn pri alirpunktoj.
Ĉi tie ni devis alfronti plurajn malfacilaĵojn:
Ĉi tiu artikolo diskutas kiel akiri ampleksajn datumojn pri la Wi-Fi-medio de Android-kodo sen NDK, hakoj, sed nur uzante la Android-API kaj kompreni kiel interpreti ĝin.
Ni ne prokrastu kaj komencu skribi kodon.
1. Kreu projekton
Ĉi tiu noto estas destinita por tiuj, kiuj kreis Android-projekton pli ol unufoje, do ni preterlasas la detalojn de ĉi tiu ero. La suba kodo estos prezentita en Kotlin, minSdkVersion=23.
2. Alirpermesoj
Por labori kun Wi-Fi de la aplikaĵo, vi devos akiri plurajn permesojn de la uzanto. Konforme
Do, en AndroidManifest.xml ni aldonos:
<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"/>
Kaj en la kodo kiu enhavas ligilon al la nuna Agado:
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. Kreu BroadcastReceiver kaj abonu datumajn ĝisdatigajn eventojn pri skanado de la reto-medio de WiFi
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
}
La WiFiManager.startScan-metodo en la dokumentaro estas markita kiel senvalorigita ekde API-versio 28, sed malŝaltita.
Entute ni ricevis liston de objektoj
4. Rigardu ScanResult kaj komprenu la terminojn
Посмотрим на некоторые поля этого класса и опишем, что они означают:
SSID — Serva Aro-Identigilo estas la nomo de la reto
BSSID - Baza Serva Aro-Identigilo - MAC-adreso de la retadaptilo (Wi-Fi-punkto)
nivelo — Indikilo de la forto de ricevita signalo [dBm (ruse dBm) — decibelo, referenca potenco 1 mW.] — Indikilo de la ricevita signalforto. Prenas valoron de 0 ĝis -100, ju pli for de 0, des pli da signalpotenco perdiĝis survoje de la Wifi-punkto al via aparato. Pliaj detaloj troveblas, ekzemple, ĉe
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val numberOfLevels = 5
val level = WifiManager.calculateSignalLevel(level, numberOfLevels)
frekvenco — operacia frekvenco de la Wifi-punkto [Hz]. Krom la ofteco mem, vi eble interesiĝas pri la tiel nomata kanalo. Ĉiu punkto havas sian propran operacian purecon. Dum la skribado, la plej populara gamo de Wi-Fi-punktoj estas 2.4 GHz. Sed, por esti pli preciza, la punkto transdonas informojn al via telefono je numerita frekvenco proksima al tiu nomita. Nombro de kanaloj kaj respondaj frekvencoj
/* по частоте определяем номер канала */
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
}
}
Kapabloj - la plej interesa kampo por analizo, laboro kun kiu postulis multe da tempo. Ĉi tie la "kapabloj" de la punkto estas skribitaj en la linio. En ĉi tiu kazo, vi ne devas serĉi detalojn pri korda interpreto en la dokumentado. Jen kelkaj ekzemploj de kio povus esti en ĉi tiu linio:
[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. Kompreni mallongigojn kaj analizajn kapablojn
Menciindas, ke la klasoj de la pako android.net.wifi.* estas uzataj sub la kapuĉo de Linuksa ilo.
Ni agos konsekvence. Ni unue konsideru la eligon de formato en kiu elementoj en krampoj estas apartigitaj per signo “-“:
[WPA-PSK-TKIP+CCMP]
[WPA2-PSK-CCMP]
La unua signifo priskribas la tn. aŭtentikiga metodo. Tio estas, kian sekvencon de agoj devas la aparato kaj la alirpunkto plenumi por ke la alirpunkto lasu sin uzi kaj kiel ĉifri la utilan ŝarĝon. Al la horo de skribi ĉi tiun afiŝon, la plej oftaj ebloj estas WPA kaj WPA2, en kiuj aŭ ĉiu konektita aparato rekte aŭ tra la tn. La RADIUS-servilo (WPA-Enterprice) provizas la pasvorton per ĉifrita kanalo. Plej verŝajne, la alirpunkto en via hejmo provizas konekton laŭ ĉi tiu skemo. La diferenco inter la dua versio kaj la unua estas, ke ĝi havas pli fortan ĉifron: AES kontraŭ la nesekura TKIP. WPA3, kiu estas pli kompleksa kaj progresinta, ankaŭ estas iom post iom enkondukata. Teorie, povas ekzisti opcio kun la entreprena solvo CCKM (Cisco Centralized Key Management), sed mi neniam renkontis ĝin.
La alirpunkto eble estis agordita por aŭtentikigi per MAC-adreso. Aŭ, se la alirpunkto provizas datumojn uzante la malmodernan WEP-algoritmon, tiam fakte ne ekzistas aŭtentikigo (la sekreta ŝlosilo ĉi tie estas la ĉifrada ŝlosilo). Ni klasifikas tiajn opciojn kiel ALIAJ.
Ekzistas ankaŭ metodo, kiu estas populara en publika wi-fi kun kaŝita Kaptive Portal Detection - aŭtentikigpeto per retumilo. Tiaj alirpunktoj ŝajnas al la skanilo malfermitaj (kiu ili estas el la vidpunkto de la fizika konekto). Tial, ni klasifikas ilin kiel MALFERMA.
La dua valoro povas esti indikita kiel ŝlosila administra algoritmo. Ĉi tio estas parametro de la aŭtentikiga metodo priskribita supre. Parolas pri ĝuste kiel ĉifradaj ŝlosiloj estas interŝanĝitaj. Ni konsideru la eblajn eblojn. EAP - uzata en la menciita WPA-Enterprice, uzas datumbazon por kontroli la enmetitajn aŭtentigajn datumojn. SAE - uzata en progresinta WPA3, pli imuna al krudforto. PSK - la plej ofta opcio, implikas enigi pasvorton kaj transdoni ĝin en ĉifrita formo. IEEE8021X - laŭ internacia normo (malsama de tiu subtenata de la familio WPA). OWE (Opportunistic Wireless Encryption) estas etendaĵo de la IEEE 802.11 normo por punktoj, kiujn ni klasifikis kiel MALFERMA. OWE certigas la sekurecon de datumoj transdonitaj tra nesekurigita reto per ĉifrado de ĝi. Opcio ankaŭ eblas kiam ne ekzistas alirŝlosiloj, ni nomu ĉi tiun opcion NENIUJ.
La tria parametro estas la tn. ĉifradaj skemoj — kiel ĝuste la ĉifro estas uzata por protekti la transdonitajn datumojn. Ni listigu la eblojn. WEP - uzas RC4-fluan ĉifron, la sekreta ŝlosilo estas la ĉifrada ŝlosilo, kiu estas konsiderata neakceptebla en la mondo de moderna kriptografio. TKIP - uzata en WPA, CKIP - en WPA2. TKIP+CKIP - povas esti specifita en punktoj kapablaj je WPA kaj WPA2 por retrokongruo.
Anstataŭ tri elementoj, vi povas trovi solecan WEP-markon:
[WEP]
Kiel ni diskutis supre, ĉi tio sufiĉas por ne specifi la algoritmon por uzi ŝlosilojn, kiu ne ekzistas, kaj la ĉifradan metodon, kiu estas la sama defaŭlte.
Nun konsideru ĉi tiun krampon:
[ESS]
ĉi Funkcia reĝimo Wi-Fi aŭ Topologio de reto WiFi. Vi povas renkonti BSS (Basic Service Set) reĝimon - kiam ekzistas unu alirpunkto tra kiu konektitaj aparatoj komunikas. Troveblas en lokaj retoj. Kiel regulo, alirpunktoj estas bezonataj por konekti aparatojn de malsamaj lokaj retoj, do ili estas parto de Plilongigitaj Servaj Aroj - ESS. La tipo IBSSs (Independent Basic Service Sets) indikas ke la aparato estas parto de Peer-to-Peer reto.
Vi ankaŭ povas vidi la WPS-flagon:
[WPS]
WPS (Wi-Fi Protected Setup) estas protokolo por duonaŭtomata inicialigo de WiFi-reto. Por pravalorigi, la uzanto aŭ enigas 8-karakteran pasvorton aŭ premas butonon sur la enkursigilo. Se via alirpunkto estas de la unua tipo kaj ĉi tiu markobutono aperas apud la nomo de via alirpunkto, vi forte rekomendas iri al la administra panelo kaj malŝalti WPS-aliron. La fakto estas, ke ofte la 8-cifera PIN estas eltrovebla per la MAC-adreso, aŭ ĝi povas esti ordigita en antaŭvidebla tempo, kiun iu malhoneste povas utiligi.
6. Kreu modelon kaj analizan funkcion
Surbaze de tio, kion ni eksciis supre, ni priskribos kio okazis uzante datumklasojn:
/* схема аутентификации */
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.
}
Nun ni skribu funkcion, kiu analizos la kampon de kapabloj:
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. Vidu la rezulton
Mi skanos la reton kaj montros al vi tion, kion mi trovis. Montriĝas la rezultoj de simpla eligo per Log.d:
Capability of Home-Home [WPA2-PSK-CCMP][ESS][WPS]
...
capabilities=[Capability(authScheme=WPA2, keyManagementAlgorithm=PSK, cipherMethod=CCMP)], topologyMode=ESS, availableWps=true
La afero pri konekto al la reto de la aplika kodo restis neekzamenita. Mi nur diros, ke por legi konservitajn pasvortojn de la OS de poŝtelefono, vi bezonas radikrajtojn kaj volon traserĉi la dosiersistemon por legi wpa_supplicant.conf. Se la aplika logiko postulas enigi pasvorton de ekstere, la konekto povas esti farita per la klaso
Спасибо
Se vi pensas, ke io devas esti aldonita aŭ korektita, skribu en la komentoj :)
fonto: www.habr.com