Egy nap meg kellett vizsgálnom a Wi-Fi hálózatokat Android-alkalmazásokból, és részletes adatokat kellett szereznem a hozzáférési pontokról.
Itt több nehézséggel kellett szembenéznünk:
Ez a cikk azt tárgyalja, hogyan lehet átfogó adatokat szerezni a Wi-Fi környezetről Android-kódból NDK, hack nélkül, de csak az Android API használatával, és megérti, hogyan értelmezhető.
Ne késlekedjünk, és kezdjük el a kódírást.
1. Hozzon létre egy projektet
Ez a megjegyzés azoknak szól, akik egynél többször készítettek Android-projektet, ezért ennek a tételnek a részleteit figyelmen kívül hagyjuk. Az alábbi kód Kotlinban jelenik meg, minSdkVersion=23.
2. Hozzáférési engedélyek
Ha az alkalmazásból Wi-Fi-vel szeretne dolgozni, több engedélyt kell beszereznie a felhasználótól. Vminek megfelelően
Tehát az AndroidManifest.xml fájlhoz hozzáadjuk:
<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"/>
Az aktuális tevékenységre mutató hivatkozást tartalmazó kódban pedig:
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. Hozzon létre egy BroadcastReceivert, és iratkozzon fel a Wi-Fi hálózati környezet vizsgálatával kapcsolatos adatfrissítési eseményekre
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
}
A dokumentációban szereplő WiFiManager.startScan metódus az API 28-as verziója óta meg van jelölve, de ki van kapcsolva.
Összességében kaptunk egy listát az objektumokról
4. Tekintse meg a ScanResultot, és értse meg a feltételeket
Nézzük meg ennek az osztálynak néhány mezőjét, és írjuk le, mit jelentenek:
SSID — A Service Set Identifier a hálózat neve
BSSID – Basic Service Set Identifier – A hálózati adapter MAC-címe (Wi-Fi pont)
szint — Fogadott jelerősség jelzője [dBm (orosz dBm) — Decibel, referenciateljesítmény 1 mW.] — A vett jelerősség jelzője. 0 és -100 közötti értéket vesz fel, minél távolabb 0-tól, annál több jel veszett el a Wi-Fi-ponttól az eszközig tartó úton. További részletek találhatók például a címen
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val numberOfLevels = 5
val level = WifiManager.calculateSignalLevel(level, numberOfLevels)
frekvencia — a Wi-Fi pont működési frekvenciája [Hz]. Magán a frekvencián kívül az úgynevezett csatorna is érdekelhet. Minden pontnak megvan a maga működési tisztasága. A cikk írásakor a Wi-Fi pontok legnépszerűbb tartománya 2.4 GHz. De pontosabban a pont a megnevezett frekvenciához közeli számozott frekvencián továbbítja az információkat a telefonjára. A csatornák száma és a megfelelő frekvenciák
/* по частоте определяем номер канала */
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
}
}
képességek - a legérdekesebb elemzési terület, amely sok időt igényelt. Itt a pont „képességei” vannak beírva a sorban. Ebben az esetben nem kell a karakterlánc-értelmezés részleteit keresni a dokumentációban. Íme néhány példa arra, hogy mi lehet ebben a sorban:
[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. A rövidítések és az elemzési képességek megértése
Érdemes megemlíteni, hogy az android.net.wifi.* csomag osztályait egy Linux segédprogram használja a motorháztető alatt.
Következetesen fogunk cselekedni. Először nézzük meg egy olyan formátum kimenetét, amelyben a zárójelben lévő elemeket „-“ jel választja el egymástól:
[WPA-PSK-TKIP+CCMP]
[WPA2-PSK-CCMP]
Az első jelentés leírja az ún. hitelesítési módszer. Vagyis milyen műveletsort kell végrehajtania az eszköznek és a hozzáférési pontnak ahhoz, hogy a hozzáférési pont engedélyezze a használatát, és hogyan kell titkosítani a hasznos adatot. A bejegyzés írásakor a legelterjedtebb opciók a WPA és a WPA2, amelyekben vagy minden csatlakoztatott eszköz közvetlenül vagy az ún. A RADIUS-kiszolgáló (WPA-Enterprice) titkosított csatornán keresztül biztosítja a jelszót. Valószínűleg az otthoni hozzáférési pont biztosítja a kapcsolatot ennek a sémának megfelelően. A különbség a második és az első között az, hogy erősebb titkosítással rendelkezik: AES a nem biztonságos TKIP-hez képest. Fokozatosan bevezetésre kerül a WPA3 is, amely összetettebb és fejlettebb. Elméletileg a CCKM (Cisco Centralized Key Management) vállalati megoldással lehet egy lehetőség, de én még nem találkoztam vele.
Lehetséges, hogy a hozzáférési pontot úgy konfigurálták, hogy MAC-cím alapján hitelesítsen. Vagy ha a hozzáférési pont az elavult WEP-algoritmus használatával szolgáltat adatokat, akkor valójában nincs hitelesítés (a titkos kulcs itt a titkosítási kulcs). Az ilyen opciókat az EGYÉB kategóriába soroljuk.
Van egy olyan módszer is, amely népszerű a nyilvános wi-fi-ben a rejtett Captive Portal Detection funkcióval - egy böngészőn keresztüli hitelesítési kérés. Az ilyen hozzáférési pontok nyitottnak tűnnek a szkenner számára (ami a fizikai kapcsolat szempontjából azok). Ezért NYITOTT kategóriába soroljuk őket.
A második érték jelölhető kulcskezelési algoritmus. Ez a fent leírt hitelesítési módszer paramétere. Pontosan arról beszél, hogyan történik a titkosítási kulcsok cseréje. Tekintsük a lehetséges lehetőségeket. EAP - az említett WPA-Enterprice-ben használatos, adatbázist használ a bevitt hitelesítési adatok ellenőrzésére. SAE - fejlett WPA3-ban használják, jobban ellenáll a nyers erőnek. A PSK - a leggyakoribb lehetőség, amely magában foglalja a jelszó megadását és titkosított formában történő továbbítását. IEEE8021X - nemzetközi szabvány szerint (eltér a WPA család által támogatotttól). Az OWE (Opportunistic Wireless Encryption) az IEEE 802.11 szabvány kiterjesztése azokra a pontokra, amelyeket NYITOTT kategóriába soroltunk. Az OWE titkosítással biztosítja a nem biztonságos hálózaton továbbított adatok biztonságát. Olyan opció is lehetséges, amikor nincsenek hozzáférési kulcsok, nevezzük ezt az opciót NONE-nak.
A harmadik paraméter az ún. titkosítási sémák — pontosan hogyan használják a titkosítást a továbbított adatok védelmére. Soroljuk fel a lehetőségeket. WEP - RC4 adatfolyam titkosítót használ, a titkos kulcs a titkosítási kulcs, amely elfogadhatatlannak számít a modern kriptográfia világában. TKIP - WPA-ban használatos, CKIP - WPA2-ben. TKIP+CKIP - a visszamenőleges kompatibilitás érdekében WPA-ra és WPA2-re képes pontokban adható meg.
Három elem helyett magányos WEP-jelölést találhat:
[WEP]
Ahogy fentebb tárgyaltuk, ez elég ahhoz, hogy ne adja meg a kulcshasználati algoritmust, amely nem létezik, és a titkosítási módszert, amely alapértelmezés szerint ugyanaz.
Most nézzük ezt a zárójelet:
[ESS]
Ezt Wi-Fi üzemmód vagy Wi-Fi hálózati topológia. Találkozhat BSS (Basic Service Set) móddal – ha van egy hozzáférési pont, amelyen keresztül a csatlakoztatott eszközök kommunikálnak. Megtalálható a helyi hálózatokon. Általános szabály, hogy a különböző helyi hálózatokból származó eszközök csatlakoztatásához hozzáférési pontokra van szükség, ezért ezek az Extended Service Sets (ESS) részét képezik. Az IBSS-ek (Independent Basic Service Sets) típus azt jelzi, hogy az eszköz egy Peer-to-Peer hálózat része.
Láthatja a WPS zászlót is:
[WPS]
A WPS (Wi-Fi Protected Setup) a Wi-Fi hálózat félautomata inicializálására szolgáló protokoll. Az inicializáláshoz a felhasználó beír egy 8 karakteres jelszót, vagy megnyom egy gombot az útválasztón. Ha a hozzáférési pont az első típusú, és ez a jelölőnégyzet megjelenik a hozzáférési pont neve mellett, akkor erősen ajánlott az adminisztrációs panel megnyitása és a WPS-hozzáférés letiltása. Az tény, hogy a 8 számjegyű PIN-kód sokszor a MAC-cím alapján kideríthető, vagy belátható időn belül rendbe hozható, amit valaki tisztességtelenül kihasználhat.
6. Hozzon létre egy modellt és elemző függvényt
A fent megtudottak alapján adatosztályok segítségével írjuk le a történteket:
/* схема аутентификации */
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.
}
Most írjunk egy függvényt, amely elemzi a képességmezőt:
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. Lásd az eredményt
Átkutatom a hálózatot, és megmutatom, mit találtam. Megjelennek a Log.d-n keresztüli egyszerű kimenet eredményei:
Capability of Home-Home [WPA2-PSK-CCMP][ESS][WPS]
...
capabilities=[Capability(authScheme=WPA2, keyManagementAlgorithm=PSK, cipherMethod=CCMP)], topologyMode=ESS, availableWps=true
Vizsgálatlan maradt az alkalmazáskódból a hálózathoz való csatlakozás kérdése. Csak annyit mondok, hogy a mentett jelszavak mobileszköz operációs rendszeréből való olvasásához root jogokra van szükség, és készen kell állnia a fájlrendszerben való turkálásra a wpa_supplicant.conf olvasásához. Ha az alkalmazás logikája megköveteli a jelszó beírását kívülről, a kapcsolat az osztályon keresztül is létrejöhet
Köszönöm
Ha úgy gondolod, hogy valamit kiegészíteni vagy javítani kell, írd meg kommentben :)
Forrás: will.com