Jednoho dne jsem potřeboval skenovat Wi-Fi sítě z aplikací pro Android a získat podrobné údaje o přístupových bodech.
Zde jsme museli čelit několika potížím:
Tento článek pojednává o tom, jak získat komplexní data o prostředí Wi-Fi z kódu Android bez NDK, hacků, ale pouze pomocí Android API a pochopit, jak je interpretovat.
Nezdržujme a začněme psát kód.
1. Vytvořte projekt
Tato poznámka je určena pro ty, kteří vytvořili projekt Android více než jednou, takže podrobnosti o této položce vynecháváme. Níže uvedený kód bude uveden v Kotlin, minSdkVersion=23.
2. Přístupová oprávnění
Pro práci s Wi-Fi z aplikace budete muset získat několik oprávnění od uživatele. V souladu s
Takže do AndroidManifest.xml přidáme:
<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"/>
A v kódu, který obsahuje odkaz na aktuální aktivitu:
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. Vytvořte BroadcastReceiver a přihlaste se k odběru událostí aktualizace dat o skenování prostředí sítě 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
}
Metoda WiFiManager.startScan v dokumentaci je od verze API 28 označena jako zastaralá, ale vypnutá.
Celkem jsme dostali seznam objektů
4. Podívejte se na ScanResult a pochopte podmínky
Podívejme se na některá pole této třídy a popišme, co znamenají:
SSID — Service Set Identifier je název sítě
BSSID – Basic Service Set Identifier – MAC adresa síťového adaptéru (Wi-Fi bodu)
úroveň — Indikátor síly přijímaného signálu [dBm (Russian dBm) — Decibel, referenční výkon 1 mW.] — Indikátor síly přijímaného signálu. Nabývá hodnoty od 0 do -100, čím dále od 0, tím více energie signálu bylo ztraceno na cestě z bodu Wi-Fi do vašeho zařízení. Více podrobností najdete například na
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val numberOfLevels = 5
val level = WifiManager.calculateSignalLevel(level, numberOfLevels)
frekvence — provozní frekvence bodu Wi-Fi [Hz]. Kromě samotné frekvence vás může zajímat tzv. channel. Každý bod má svou vlastní provozní čistotu. V době psaní tohoto článku je nejoblíbenější rozsah Wi-Fi bodů 2.4 GHz. Přesněji řečeno, bod přenáší informace do vašeho telefonu na očíslované frekvenci blízké jmenovanému. Počet kanálů a odpovídající frekvence
/* по частоте определяем номер канала */
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
}
}
schopnosti - nejzajímavější pole pro analýzu, práce s nímž vyžadovala mnoho času. Zde jsou v řádku zapsány „schopnosti“ bodu. V tomto případě nemusíte hledat podrobnosti o interpretaci řetězců v dokumentaci. Zde je několik příkladů toho, co může být v tomto řádku:
[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. Pochopení zkratek a schopností analýzy
Za zmínku stojí, že třídy balíčku android.net.wifi.* používá pod kapotou linuxová utilita
Budeme jednat důsledně. Podívejme se nejprve na výstup formátu, ve kterém jsou prvky uvnitř závorek odděleny znaménkem „-“:
[WPA-PSK-TKIP+CCMP]
[WPA2-PSK-CCMP]
První význam popisuje tzv. autentizační metoda. To znamená, jakou sekvenci akcí musí zařízení a přístupový bod provést, aby se přístupový bod mohl používat, a jak zašifrovat datovou část. V době psaní tohoto příspěvku jsou nejběžnější možnosti WPA a WPA2, ve kterých buď každé připojené zařízení přímo, nebo prostřednictvím tzv. Server RADIUS (WPA-Enterprice) poskytuje heslo přes šifrovaný kanál. S největší pravděpodobností přístupový bod ve vaší domácnosti poskytuje připojení podle tohoto schématu. Rozdíl mezi druhou verzí a první je v tom, že má silnější šifru: AES versus nezabezpečený TKIP. Postupně se také zavádí WPA3, který je složitější a pokročilejší. Teoreticky může existovat možnost s podnikovým řešením CCKM (Cisco Centralized Key Management), ale nikdy jsem se s tím nesetkal.
Přístupový bod mohl být nakonfigurován pro ověřování pomocí MAC adresy. Nebo pokud přístupový bod poskytuje data pomocí zastaralého algoritmu WEP, pak ve skutečnosti neexistuje žádná autentizace (tajným klíčem je zde šifrovací klíč). Takové možnosti klasifikujeme jako JINÉ.
Existuje také metoda, která je oblíbená ve veřejné wi-fi se skrytou detekcí Captive Portal Detection – žádostí o ověření přes prohlížeč. Takové přístupové body se skeneru jeví jako otevřené (což jsou z pohledu fyzického připojení). Proto je klasifikujeme jako OTEVŘENÉ.
Druhou hodnotu lze označit jako algoritmus správy klíčů. Toto je parametr výše popsané metody ověřování. Mluví o tom, jak přesně se vyměňují šifrovací klíče. Zvažme možné možnosti. EAP - používá se ve zmíněném WPA-Enterprice, využívá databázi k ověření zadaných autentizačních údajů. SAE - používá se v pokročilém WPA3, odolnější vůči hrubé síle. PSK - nejběžnější možnost, zahrnuje zadání hesla a jeho přenos v zašifrované podobě. IEEE8021X - podle mezinárodního standardu (odlišného od standardu podporovaného rodinou WPA). OWE (Opportunistic Wireless Encryption) je rozšířením standardu IEEE 802.11 pro body, které jsme klasifikovali jako OPEN. OWE zajišťuje bezpečnost dat přenášených přes nezabezpečenou síť jejich šifrováním. Možnost je také možná, když neexistují žádné přístupové klíče, nazvěme tuto možnost ŽÁDNÁ.
Třetím parametrem je tzv. šifrovací schémata — jak přesně je šifra použita k ochraně přenášených dat. Pojďme si vyjmenovat možnosti. WEP - používá proudovou šifru RC4, tajným klíčem je šifrovací klíč, který je ve světě moderní kryptografie považován za nepřijatelný. TKIP - používá se ve WPA, CKIP - ve WPA2. TKIP+CKIP - lze specifikovat v bodech schopných WPA a WPA2 pro zpětnou kompatibilitu.
Místo tří prvků můžete najít osamělou značku WEP:
[WEP]
Jak jsme diskutovali výše, to stačí k tomu, abychom nespecifikovali algoritmus pro použití klíčů, který neexistuje, a metodu šifrování, která je ve výchozím nastavení stejná.
Nyní zvažte tento držák:
[ESS]
To Provozní režim Wi-Fi nebo Topologie sítě Wi-Fi. Můžete se setkat s režimem BSS (Basic Service Set) – když existuje jeden přístupový bod, přes který komunikují připojená zařízení. Lze jej nalézt v místních sítích. Přístupové body jsou zpravidla potřeba pro připojení zařízení z různých lokálních sítí, proto jsou součástí Extended Service Sets - ESS. Typ IBSSs (Independent Basic Service Sets) označuje, že zařízení je součástí sítě Peer-to-Peer.
Můžete také vidět příznak WPS:
[WPS]
WPS (Wi-Fi Protected Setup) je protokol pro poloautomatickou inicializaci sítě Wi-Fi. Pro inicializaci uživatel buď zadá 8místné heslo, nebo stiskne tlačítko na routeru. Pokud je váš přístupový bod prvního typu a toto zaškrtávací políčko se zobrazí vedle názvu vašeho přístupového bodu, důrazně doporučujeme přejít na panel správce a zakázat přístup WPS. Faktem je, že často lze 8místný PIN zjistit podle MAC adresy nebo jej lze v dohledné době vyřešit, čehož může někdo nepoctivě využít.
6. Vytvořte model a funkci analýzy
Na základě toho, co jsme zjistili výše, popíšeme, co se stalo pomocí datových tříd:
/* схема аутентификации */
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.
}
Nyní napíšeme funkci, která bude analyzovat pole schopností:
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. Podívejte se na výsledek
Prohledám síť a ukážu vám, co jsem našel. Jsou zobrazeny výsledky jednoduchého výstupu přes Log.d:
Capability of Home-Home [WPA2-PSK-CCMP][ESS][WPS]
...
capabilities=[Capability(authScheme=WPA2, keyManagementAlgorithm=PSK, cipherMethod=CCMP)], topologyMode=ESS, availableWps=true
Otázka připojení k síti z kódu aplikace zůstala neprozkoumaná. Řeknu pouze, že pro čtení uložených hesel z OS mobilního zařízení potřebujete práva root a ochotu prohrabovat se souborovým systémem, abyste mohli číst wpa_supplicant.conf. Pokud logika aplikace vyžaduje zadání hesla zvenčí, lze připojení provést prostřednictvím třídy
Díky
Pokud si myslíte, že je potřeba něco doplnit nebo opravit, napište do komentářů :)
Zdroj: www.habr.com