Kādu dienu man vajadzēja skenēt Wi-Fi tīklus no Android lietojumprogrammām un iegūt detalizētus datus par piekļuves punktiem.
Šeit mums bija jāsaskaras ar vairākām grūtībām:
Šajā rakstā ir apskatīts, kā iegūt visaptverošus datus par Wi-Fi vidi no Android koda bez NDK, uzlaušanas, bet tikai izmantojot Android API un saprast, kā to interpretēt.
Nekavēsimies un sāksim rakstīt kodu.
1. Izveidojiet projektu
Šī piezīme ir paredzēta tiem, kuri Android projektu ir izveidojuši vairāk nekā vienu reizi, tāpēc mēs izlaižam sīkāku informāciju par šo vienumu. Tālāk norādītais kods tiks parādīts valodā Kotlin, minSdkVersion=23.
2. Piekļuves atļaujas
Lai strādātu ar Wi-Fi no lietojumprogrammas, jums būs jāsaņem vairākas lietotāja atļaujas. Saskaņā ar
Tātad failā AndroidManifest.xml mēs pievienosim:
<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"/>
Un kodā, kurā ir saite uz pašreizējo darbību:
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. Izveidojiet BroadcastReceiver un abonējiet datu atjaunināšanas pasākumus par Wi-Fi tīkla vides skenēšanu.
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
}
Metode WiFiManager.startScan dokumentācijā ir atzīmēta kā novecojusi kopš API 28. versijas, bet izslēgta.
Kopumā saņēmām objektu sarakstu
4. Apskatiet ScanResult un izprotiet noteikumus
Apskatīsim dažus šīs klases laukus un aprakstīsim, ko tie nozīmē:
SSID — Service Set Identifier ir tīkla nosaukums
BSSID – Pamatpakalpojumu kopas identifikators – tīkla adaptera MAC adrese (Wi-Fi punkts)
līmenis — Saņemtā signāla stipruma indikators [dBm (Krievijas dBm) — Decibels, atsauces jauda 1 mW.] — Saņemtā signāla stipruma indikators. Tiek izmantota vērtība no 0 līdz -100, jo tālāk no 0, jo vairāk signāla jaudas tika zaudēts ceļā no Wi-Fi punkta uz ierīci. Sīkāku informāciju var atrast, piemēram, vietnē
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val numberOfLevels = 5
val level = WifiManager.calculateSignalLevel(level, numberOfLevels)
biežums — Wi-Fi punkta darbības frekvence [Hz]. Papildus pašai frekvencei jūs varētu interesēt tā sauktais kanāls. Katram punktam ir sava darbības tīrība. Rakstīšanas laikā vispopulārākais Wi-Fi punktu diapazons ir 2.4 GHz. Bet, precīzāk sakot, punkts pārsūta informāciju uz jūsu tālruni ar numurētu frekvenci, kas ir tuvu nosauktajai. Kanālu skaits un atbilstošās frekvences
/* по частоте определяем номер канала */
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
}
}
iespējas - visinteresantākā analīzes joma, ar kuru darbs prasīja daudz laika. Šeit rindā ir ierakstītas punkta “iespējas”. Šajā gadījumā jums nav jāmeklē informācija par virkņu interpretāciju dokumentācijā. Šeit ir daži piemēri tam, kas varētu būt šajā rindā:
[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. Izpratne par saīsinājumiem un parsēšanas iespējām
Ir vērts pieminēt, ka android.net.wifi.* pakotnes klases zem pārsega izmanto Linux utilīta.
Mēs rīkosimies konsekventi. Vispirms apskatīsim tāda formāta izvadi, kurā elementi iekavās ir atdalīti ar “-” zīmi:
[WPA-PSK-TKIP+CCMP]
[WPA2-PSK-CCMP]
Pirmā nozīme raksturo tā saukto. autentifikācijas metode. Tas ir, kāda darbību secība ir jāveic ierīcei un piekļuves punktam, lai piekļuves punkts ļautu sevi izmantot un kā šifrēt lietderīgo slodzi. Rakstot šo ierakstu, visizplatītākās iespējas ir WPA un WPA2, kurās vai nu katra pieslēgtā ierīce tieši vai caur t.s. RADIUS serveris (WPA-Enterprice) nodrošina paroli, izmantojot šifrētu kanālu. Visticamāk, piekļuves punkts jūsu mājās nodrošina savienojumu saskaņā ar šo shēmu. Atšķirība starp otro versiju un pirmo ir tā, ka tai ir spēcīgāks šifrs: AES pret nedrošo TKIP. Pamazām tiek ieviests arī WPA3, kas ir sarežģītāks un uzlabots. Teorētiski var būt iespēja ar uzņēmuma risinājumu CCKM (Cisco Centralized Key Management), bet es ar to nekad neesmu saskāries.
Piekļuves punkts, iespējams, ir konfigurēts autentifikācijai pēc MAC adreses. Vai arī, ja piekļuves punkts nodrošina datus, izmantojot novecojušu WEP algoritmu, autentifikācijas faktiski nav (šeit slepenā atslēga ir šifrēšanas atslēga). Mēs klasificējam šādas iespējas kā CITI.
Ir arī metode, kas ir populāra publiskajā wi-fi ar slēpto Captive Portal Detection - autentifikācijas pieprasījums, izmantojot pārlūkprogrammu. Šādi piekļuves punkti skenerim šķiet atvērti (kas tie ir no fiziskā savienojuma viedokļa). Tāpēc mēs tos klasificējam kā OPEN.
Otro vērtību var apzīmēt kā atslēgu pārvaldības algoritms. Šis ir iepriekš aprakstītās autentifikācijas metodes parametrs. Runā par to, kā tieši notiek šifrēšanas atslēgu apmaiņa. Apsvērsim iespējamās iespējas. EAP - izmanto minētajā WPA-Enterprice, izmanto datu bāzi, lai pārbaudītu ievadītos autentifikācijas datus. SAE - izmantots uzlabotajā WPA3, izturīgāks pret brutālu spēku. PSK - visizplatītākā iespēja, kas ietver paroles ievadīšanu un pārsūtīšanu šifrētā veidā. IEEE8021X - saskaņā ar starptautisku standartu (atšķiras no tā, ko atbalsta WPA saime). OWE (Opportūnistiskā bezvadu šifrēšana) ir IEEE 802.11 standarta paplašinājums punktiem, kurus klasificējām kā OPEN. OWE nodrošina nedrošā tīklā pārsūtīto datu drošību, tos šifrējot. Iespējama arī iespēja, kad nav piekļuves taustiņu, sauksim šo opciju NONE.
Trešais parametrs ir tā sauktais. šifrēšanas shēmas — kā tieši šifrs tiek izmantots pārsūtīto datu aizsardzībai. Uzskaitīsim iespējas. WEP - izmanto RC4 straumes šifru, slepenā atslēga ir šifrēšanas atslēga, kas mūsdienu kriptogrāfijas pasaulē tiek uzskatīta par nepieņemamu. TKIP - izmanto WPA, CKIP - WPA2. TKIP+CKIP - var norādīt punktos, kas spēj WPA un WPA2, lai nodrošinātu atpakaļejošu saderību.
Trīs elementu vietā varat atrast vientuļu WEP zīmi:
[WEP]
Kā mēs apspriedām iepriekš, tas ir pietiekami, lai nenorādītu atslēgu izmantošanas algoritmu, kas neeksistē, un šifrēšanas metodi, kas pēc noklusējuma ir tāda pati.
Tagad apsveriet šo iekavu:
[ESS]
Tā Wi-Fi darbības režīms vai Wi-Fi tīkla topoloģija. Jūs varat saskarties ar BSS (Basic Service Set) režīmu — ja ir viens piekļuves punkts, caur kuru savienotās ierīces sazinās. To var atrast vietējos tīklos. Parasti piekļuves punkti ir nepieciešami, lai savienotu ierīces no dažādiem vietējiem tīkliem, tāpēc tie ir daļa no paplašinātajām pakalpojumu kopām — ESS. IBSS (Independent Basic Service Sets) tips norāda, ka ierīce ir vienādranga tīkla daļa.
Varat arī redzēt WPS karogu:
[WPS]
WPS (Wi-Fi Protected Setup) ir protokols pusautomātiskai Wi-Fi tīkla inicializācijai. Lai inicializētu, lietotājs vai nu ievada 8 rakstzīmju paroli, vai nospiež maršrutētāja pogu. Ja jūsu piekļuves punkts ir pirmā tipa un šī izvēles rūtiņa tiek parādīta blakus piekļuves punkta nosaukumam, ļoti ieteicams doties uz administratora paneli un atspējot WPS piekļuvi. Lieta tāda, ka nereti 8 ciparu PIN var uzzināt pēc MAC adreses vai arī to var sakārtot pārskatāmā laikā, ko kāds negodprātīgi var izmantot.
6. Izveidojiet modeli un parsēšanas funkciju
Pamatojoties uz to, ko noskaidrojām iepriekš, mēs aprakstīsim notikušo, izmantojot datu klases:
/* схема аутентификации */
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.
}
Tagad uzrakstīsim funkciju, kas parsēs iespēju lauku:
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. Skatiet rezultātu
Es pārmeklēšu tīklu un parādīšu, ko atradu. Tiek parādīti vienkāršas izvades rezultāti, izmantojot Log.d:
Capability of Home-Home [WPA2-PSK-CCMP][ESS][WPS]
...
capabilities=[Capability(authScheme=WPA2, keyManagementAlgorithm=PSK, cipherMethod=CCMP)], topologyMode=ESS, availableWps=true
Jautājums par savienojuma izveidi ar tīklu no lietojumprogrammas koda palika neizskatīts. Teikšu tikai to, ka, lai nolasītu saglabātās paroles no mobilās ierīces OS, ir nepieciešamas root tiesības un vēlme rakņāties pa failu sistēmu, lai lasītu wpa_supplicant.conf. Ja lietojumprogrammas loģika prasa ievadīt paroli no ārpuses, savienojumu var izveidot caur klasi
Paldies
Ja uzskati, ka kaut kas jāpapildina vai jālabo, raksti komentāros :)
Avots: www.habr.com