Մի օր ինձ անհրաժեշտ եղավ սկանավորել Wi-Fi ցանցերը Android հավելվածներից և ստանալ մանրամասն տվյալներ մուտքի կետերի մասին:
Այստեղ մենք ստիպված էինք դիմակայել մի քանի դժվարությունների.
Այս հոդվածը քննարկում է, թե ինչպես կարելի է ստանալ համապարփակ տվյալներ Wi-Fi միջավայրի մասին Android կոդից՝ առանց NDK-ի, հաքերների, բայց միայն օգտագործելով Android API-ն և հասկանալ, թե ինչպես կարելի է այն մեկնաբանել:
Եկեք չհապաղենք ու սկսենք կոդ գրել։
1. Ստեղծեք նախագիծ
Այս նշումը նախատեսված է նրանց համար, ովքեր մեկից ավելի անգամ ստեղծել են Android նախագիծ, ուստի մենք բաց ենք թողնում այս կետի մանրամասները: Ստորև բերված կոդը կներկայացվի Kotlin-ում, minSdkVersion=23:
2. Մուտքի թույլտվություններ
Հավելվածից Wi-Fi-ի հետ աշխատելու համար անհրաժեշտ կլինի օգտատիրոջից մի քանի թույլտվություն ստանալ: Համաձայն
Այսպիսով, AndroidManifest.xml-ում մենք կավելացնենք.
<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"/>
Իսկ կոդում, որը պարունակում է հղում դեպի ընթացիկ Գործունեությունը.
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. Ստեղծեք BroadcastReceiver և բաժանորդագրվեք 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
}
Փաստաթղթերում WiFiManager.startScan մեթոդը նշված է որպես API-ի 28 տարբերակից զրկված, բայց անջատված է:
Ընդհանուր առմամբ, մենք ստացել ենք օբյեկտների ցանկ
4. Նայեք ScanResult-ին և հասկացեք պայմանները
Եկեք նայենք այս դասի որոշ ոլորտներին և նկարագրենք, թե դրանք ինչ են նշանակում.
SSID — Ծառայությունների հավաքածուի նույնացուցիչը ցանցի անունն է
BSSID- ը – Հիմնական ծառայության հավաքածուի նույնացուցիչ – ցանցային ադապտերի MAC հասցեն (Wi-Fi կետ)
մակարդակ — Ստացված ազդանշանի ուժգնության ցուցիչ [dBm (ռուս. dBm) — դեցիբել, հղման հզորությունը 1 մՎտ.] — ստացված ազդանշանի հզորության ցուցիչ։ Ընդունում է արժեք 0-ից մինչև -100, որքան հեռու է 0-ից, այնքան ավելի շատ ազդանշանի հզորություն է կորել Wi-Fi կետից ձեր սարք տանող ճանապարհին: Լրացուցիչ մանրամասները կարող եք գտնել, օրինակ, կայքում
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
val numberOfLevels = 5
val level = WifiManager.calculateSignalLevel(level, numberOfLevels)
հաճախություն — Wi-Fi կետի գործառնական հաճախականությունը [Hz]: Բացի բուն հաճախականությունից, ձեզ կարող է հետաքրքրել այսպես կոչված ալիքը: Յուրաքանչյուր կետ ունի իր գործառնական մաքրությունը: Գրելու պահին Wi-Fi կետերի ամենատարածված տիրույթը 2.4 ԳՀց է: Բայց, ավելի ճիշտ, կետը տեղեկատվություն է փոխանցում ձեր հեռախոսին անվանվածին մոտ համարակալված հաճախականությամբ: Ալիքների քանակը և համապատասխան հաճախականությունները
/* по частоте определяем номер канала */
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
}
}
կարողությունները - վերլուծության համար ամենահետաքրքիր ոլորտը, որի հետ աշխատանքը շատ ժամանակ էր պահանջում: Այստեղ տողում գրված են կետի «կարողությունները»։ Այս դեպքում, դուք պետք չէ փաստաթղթերում փնտրել տողերի մեկնաբանման մանրամասները: Ահա մի քանի օրինակներ, թե ինչ կարող է լինել այս տողում.
[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. Հասկանալով հապավումները և վերլուծության հնարավորությունները
Հարկ է նշել, որ android.net.wifi.* փաթեթի դասերն օգտագործվում են կափարիչի տակ՝ Linux կոմունալ ծրագրով։
Մենք գործելու ենք հետևողական. Եկեք նախ դիտարկենք ձևաչափի արդյունքը, որտեղ փակագծերի ներսում գտնվող տարրերն առանձնացված են «-» նշանով.
[WPA-PSK-TKIP+CCMP]
[WPA2-PSK-CCMP]
Առաջին իմաստը նկարագրում է այսպես կոչված. նույնականացման մեթոդ. Այսինքն՝ գործողությունների ինչ հաջորդականություն պետք է կատարեն սարքը և մուտքի կետը, որպեսզի մուտքի կետն իրեն թույլ տա օգտագործել և ինչպես գաղտնագրել օգտակար բեռը: Այս գրառումը գրելու պահին ամենատարածված տարբերակներն են WPA-ն և WPA2-ը, որոնցում կա՛մ յուրաքանչյուր միացված սարք ուղղակիորեն, կա՛մ այսպես կոչված. RADIUS սերվերը (WPA-Enterprice) տրամադրում է գաղտնաբառը գաղտնագրված ալիքով: Ամենայն հավանականությամբ, ձեր տան մուտքի կետը կապ է ապահովում այս սխեմայի համաձայն: Երկրորդ տարբերակի և առաջինի տարբերությունն այն է, որ այն ունի ավելի ուժեղ ծածկագիր՝ AES-ն ընդդեմ անապահով TKIP-ի: Աստիճանաբար ներդրվում է նաև WPA3-ը, որն ավելի բարդ և առաջադեմ է: Տեսականորեն, կարող է լինել CCKM (Cisco Centralized Key Management) ձեռնարկատիրական լուծման տարբերակ, բայց ես երբեք չեմ հանդիպել դրան:
Մուտքի կետը կարող է կազմաձևված լինել նույնականացման համար MAC հասցեով: Կամ, եթե մուտքի կետը տրամադրում է տվյալներ՝ օգտագործելով հնացած WEP ալգորիթմը, ապա իրականում վավերացում չկա (այստեղ գաղտնի բանալին գաղտնագրման բանալին է): Մենք դասակարգում ենք նման տարբերակները որպես ԱՅԼ:
Գոյություն ունի նաև մի մեթոդ, որը հանրաճանաչ է հանրային Wi-Fi-ում՝ թաքնված Captive Portal Detection-ով. նույնականացման հարցում բրաուզերի միջոցով: Նման մուտքի կետերը սկաների վրա հայտնվում են որպես բաց (որը դրանք ֆիզիկական կապի տեսանկյունից են): Հետեւաբար, մենք դրանք դասակարգում ենք որպես ԲԱՑ:
Երկրորդ արժեքը կարող է նշանակվել որպես բանալիների կառավարման ալգորիթմ. Սա վերը նկարագրված նույնականացման մեթոդի պարամետրն է: Խոսում է այն մասին, թե ինչպես են փոխանակվում գաղտնագրման բանալիները: Դիտարկենք հնարավոր տարբերակները։ EAP - օգտագործվում է նշված WPA-Enterprice-ում, օգտագործում է տվյալների բազա՝ մուտքագրված վավերացման տվյալները ստուգելու համար: SAE - օգտագործվում է առաջադեմ WPA3-ում, ավելի դիմացկուն է կոպիտ ուժի նկատմամբ: PSK - ամենատարածված տարբերակն է, որը ներառում է գաղտնաբառի մուտքագրում և գաղտնագրված ձևով փոխանցում: IEEE8021X - ըստ միջազգային ստանդարտի (տարբերվում է WPA ընտանիքի կողմից աջակցվողից): OWE (Oportunistic Wireless Encryption) IEEE 802.11 ստանդարտի ընդլայնումն է այն կետերի համար, որոնք մենք դասակարգել ենք որպես ԲԱՑ: OWE-ն ապահովում է անապահով ցանցով փոխանցվող տվյալների անվտանգությունը՝ գաղտնագրելով դրանք: Հնարավոր է նաև տարբերակ, երբ մուտքի ստեղներ չկան, այս տարբերակը կոչենք ՈՉ ՄԻ։
Երրորդ պարամետրը այսպես կոչված. կոդավորման սխեմաներ — ինչպես է գաղտնագիրը օգտագործվում փոխանցված տվյալները պաշտպանելու համար: Թվարկենք տարբերակները։ WEP - օգտագործում է RC4 հոսքային ծածկագիրը, գաղտնի բանալին գաղտնագրման բանալին է, որն անընդունելի է համարվում ժամանակակից կրիպտոգրաֆիայի աշխարհում։ TKIP - օգտագործվում է WPA-ում, CKIP - WPA2-ում: TKIP + CKIP - կարող է նշվել կետերում, որոնք ունակ են WPA և WPA2՝ հետին համատեղելիության համար:
Երեք տարրերի փոխարեն կարող եք գտնել միայնակ WEP նշան.
[WEP]
Ինչպես վերը քննարկեցինք, սա բավական է չնշելու բանալիների օգտագործման ալգորիթմը, որը գոյություն չունի, և գաղտնագրման մեթոդը, որը լռելյայն նույնն է:
Այժմ հաշվի առեք այս փակագիծը.
[ESS]
այս Wi-Fi գործառնական ռեժիմ կամ Wi-Fi ցանցի տոպոլոգիա. Դուք կարող եք հանդիպել BSS (Basic Service Set) ռեժիմին, երբ կա մեկ մուտքի կետ, որի միջոցով կապակցված սարքերը հաղորդակցվում են: Կարելի է գտնել տեղական ցանցերում: Որպես կանոն, մուտքի կետերը անհրաժեշտ են տարբեր տեղական ցանցերից սարքերը միացնելու համար, ուստի դրանք Extended Service Sets - ESS-ի մաս են կազմում: IBSSs (Independent Basic Service Sets) տեսակը ցույց է տալիս, որ սարքը Peer-to-Peer ցանցի մաս է:
Դուք կարող եք նաև տեսնել WPS դրոշը.
[WPS]
WPS (Wi-Fi Protected Setup) Wi-Fi ցանցի կիսաավտոմատ սկզբնավորման պրոտոկոլ է: Նախաստորագրելու համար օգտատերը կամ մուտքագրում է 8 նիշանոց գաղտնաբառ, կամ սեղմում է երթուղիչի կոճակը: Եթե ձեր մուտքի կետը առաջին տիպի է, և այս վանդակը հայտնվում է ձեր մուտքի կետի անվան կողքին, ձեզ խստորեն խորհուրդ է տրվում գնալ ադմինիստրատորի վահանակ և անջատել WPS մուտքը: Փաստն այն է, որ հաճախ 8 նիշանոց PIN-ը կարելի է պարզել MAC հասցեով կամ տեսանելի ժամանակում այն կարգավորել, ինչից ինչ-որ մեկը անազնիվ կերպով կարող է օգտվել:
6. Ստեղծեք մոդելի և վերլուծության ֆունկցիա
Ելնելով այն ամենից, ինչ մենք պարզեցինք վերևում, մենք նկարագրելու ենք, թե ինչ է տեղի ունեցել տվյալների դասերի միջոցով.
/* схема аутентификации */
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.
}
Հիմա եկեք գրենք ֆունկցիա, որը կվերլուծի հնարավորությունների դաշտը.
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. Տես արդյունքը
Ես կսկանավորեմ ցանցը և ցույց կտամ, թե ինչ եմ գտել: Ցուցադրված են Log.d-ի միջոցով պարզ ելքի արդյունքները՝
Capability of Home-Home [WPA2-PSK-CCMP][ESS][WPS]
...
capabilities=[Capability(authScheme=WPA2, keyManagementAlgorithm=PSK, cipherMethod=CCMP)], topologyMode=ESS, availableWps=true
Հավելվածի կոդից ցանցին միանալու հարցը մնաց չուսումնասիրված։ Ես միայն կասեմ, որ բջջային սարքի ՕՀ-ից պահպանված գաղտնաբառերը կարդալու համար անհրաժեշտ են արմատային իրավունքներ և ֆայլային համակարգը շրջելու պատրաստակամություն՝ wpa_supplicant.conf կարդալու համար: Եթե հավելվածի տրամաբանությունը պահանջում է մուտքագրել գաղտնաբառ դրսից, կապը կարող է իրականացվել դասի միջոցով
Շնորհակալություն
Եթե կարծում եք, որ ինչ-որ բան պետք է ավելացնել կամ ուղղել, գրեք մեկնաբանություններում :)
Source: www.habr.com