Kūrėjai: labai lengva apeiti paslėptus „Android“ API apribojimus

click fraud protection

„Android 9 Pie“ ir „Android 10“ pateikia įspėjimus arba visiškai blokuoja prieigą prie paslėptų API. Štai kaip kūrėjai gali apeiti paslėptus API apribojimus.

Prisiminkite daugiau nei prieš metus ir mes visi džiaugiamės galėdami pamatyti, kas bus „Android P“ beta versijoje. Naudotojai nekantriai laukia naujų funkcijų, o kūrėjai laukia kai kurių naujų įrankių, skirtų patobulinti savo programas. Deja, kai kuriems iš tų kūrėjų pirmoji „Android P“ beta versija buvo su nemalonia staigmena: paslėptais API apribojimais. Prieš pasinerdamas į tai, ką tai reiškia, leiskite šiek tiek paaiškinti jo kontekstą.

Kas tai per?

Kurdami programą „Android“ programų kūrėjai neprivalo pradėti nuo nulio. „Google“ teikia įrankius, kad programų kūrimas būtų lengvesnis ir nesikartotų. Vienas iš šių įrankių yra „Android“ SDK. SDK iš esmės yra nuoroda į visas funkcijas, kurias kūrėjai gali saugiai naudoti savo programose. Šios funkcijos yra standartinės visuose „Google“ patvirtintuose „Android“ variantuose. Tačiau SDK nėra baigtinis. Yra nemažai „paslėptų“ „Android“ sistemos dalių, kurios nėra SDK dalis.

Šios „paslėptos“ dalys gali būti neįtikėtinai naudingos, kai reikia ieškoti sudėtingesnių ar žemesnio lygio dalykų. Pavyzdžiui, mano Programėlė Widget Drawer naudoja ne SDK funkciją, kad nustatytų, kada vartotojas paleidžia programą iš valdiklio, kad stalčius galėtų automatiškai užsidaryti. Galbūt galvojate: „Kodėl šių ne SDK funkcijų tiesiog nepadarius SDK dalimi? Na, problema ta, kad jų veikimas nėra visiškai nuspėjamas. „Google“ negali užtikrinti, kad kiekviena sistemos dalis veiktų kiekviename jos patvirtintame įrenginyje, todėl parenkami svarbesni metodai, kuriuos reikia patikrinti. „Google“ negarantuoja, kad likusi sistemos dalis išliks nuosekli. Gamintojai gali pakeisti arba visiškai pašalinti šias paslėptas funkcijas. Net ir skirtingose ​​AOSP versijose niekada nežinote, ar paslėpta funkcija vis dar egzistuos, ar veiks taip, kaip anksčiau.

Jei jums įdomu, kodėl aš vartojau žodį „paslėpta“, tai todėl, kad šios funkcijos net nėra SDK dalis. Paprastai, jei programoje bandysite naudoti paslėptą metodą ar klasę, jos kompiliuoti nepavyks. Jų naudojimas reikalauja atspindys arba pakeistas SDK.

Naudodama „Android P“, „Google“ nusprendė, kad vien jų paslėpti neužtenka. Kai buvo išleista pirmoji beta versija, buvo paskelbta, kad daugumos (ne visų) paslėptų funkcijų programų kūrėjai nebegalėjo naudotis. Pirmoji beta versija įspės jus, kai jūsų programoje buvo naudojama funkcija, įtraukta į juodąjį sąrašą. Kitos beta versijos tiesiog sudužo jūsų programa. Bent jau man šis juodasis sąrašas buvo gana erzinantis. Jis ne tik šiek tiek sulūžo Navigacijos gestai, bet kadangi paslėptos funkcijos yra įtrauktos į juodąjį sąrašą pagal numatytuosius nustatymus („Google“ turi rankiniu būdu įtraukti kai kuriuos leidimus į baltąjį sąrašą), turėjau daug problemų, kad įjungtų valdiklių stalčių.

Dabar buvo keletas būdų, kaip apeiti juodąjį sąrašą. Pirmasis buvo tiesiog palikti programą taikyti pagal API 27 („Android 8.1“), nes juodasis sąrašas taikomas tik programoms, kurios taikomos naujausiai API. Deja, su Google minimalūs API reikalavimai skelbti „Play“ parduotuvėje, tiek ilgai būtų galima taikyti tik API 27. Nuo 2019 m. lapkričio 1 d, visi „Play“ parduotuvės programų naujiniai turi būti taikomi 28 ar naujesnės versijos API.

Antrasis sprendimas iš tikrųjų yra kažkas, ką „Google“ įdiegė „Android“. Galima paleisti ADB komandą, kad visiškai išjungtumėte juodąjį sąrašą. Tai puikiai tinka asmeniniam naudojimui ir bandymams, tačiau galiu pasakyti, kad bandymas priversti galutinius vartotojus nustatyti ir paleisti ADB yra košmaras.

Taigi kur tai palieka mus? Nebegalime taikyti API 27, jei norime įkelti į „Play“ parduotuvę, o ADB metodas tiesiog nėra perspektyvus. Tačiau tai nereiškia, kad mums pritrūko galimybių.

Paslėptas API juodasis sąrašas taikomas tik neįtrauktoms vartotojų programoms. Sistemos programos, programos, pasirašytos platformos parašu, ir programos, nurodytos konfigūracijos faile, neįtrauktos į juodąjį sąrašą. Įdomu tai, kad visi „Google Play“ paslaugų rinkiniai nurodyti tame konfigūracijos faile. Manau, kad „Google“ yra geresnė nei mes visi.

Bet kokiu atveju, toliau kalbėkime apie juodąjį sąrašą. Šiandien mus domina ta, kad sistemos taikomosios programos yra neapmokestinamos. Tai reiškia, kad, pavyzdžiui, sistemos vartotojo sąsajos programa gali naudoti visas norimas paslėptas funkcijas, nes ji yra sistemos skaidinyje. Akivaizdu, kad negalime tiesiog perkelti programos į sistemos skaidinį. Tam reikia root, geros failų tvarkyklės, žinių apie leidimus... Būtų lengviau naudoti ADB. Tačiau tai nėra vienintelis būdas būti sistemos programa, bent jau kalbant apie paslėptą API juodąjį sąrašą.

Grįžkite į prieš septynias pastraipas, kai paminėjau apmąstymus. Jei nežinote, kas yra atspindys, rekomenduoju perskaityti šitas puslapis, bet čia trumpa santrauka. Java programoje refleksija yra būdas pasiekti įprastai nepasiekiamas klases, metodus ir laukus. Tai neįtikėtinai galingas įrankis. Kaip sakiau toje pastraipoje, refleksija buvo būdas pasiekti ne SDK funkcijas. API juodasis sąrašas blokuoja atspindžio naudojimą, bet neblokuoja dvigubai-atspindys.

Štai čia pasidaro šiek tiek keista. Paprastai, jei norite iškviesti paslėptą metodą, atlikite kažką panašaus (tai yra Kotlin, bet Java yra panaši):

val someHiddenClass = Class.forName("android.some.hidden.Class")
val someHiddenMethod = someHiddenClass.getMethod("someHiddenMethod", String::class.java)

someHiddenMethod.invoke(null, "some important string")

Tačiau dėl API juodojo sąrašo jūs tiesiog gausite ClassNotFoundException. Tačiau jei atspindėsite du kartus, viskas gerai:

val forName = Class::class.java.getMethod("forName", String:: class.java)
val getMethod = Class::class.java.getMethod("getMethod", String:: class.java, arrayOf>()::class.java)
val someHiddenClass = forName.invoke(null, "android.some.hidden.Class") asClass
val someHiddenMethod = getMethod.invoke(someHiddenClass, "someHiddenMethod", String::class.java)

someHiddenMethod.invoke(null, "some important string")

Keista tiesa? Na taip, bet ir ne. API juodasis sąrašas seka, kas iškviečia funkciją. Jei šaltinis nėra atleistas, jis sugenda. Pirmajame pavyzdyje šaltinis yra programa. Tačiau antrajame pavyzdyje šaltinis yra pati sistema. Užuot naudoję refleksiją, kad gautume tai, ko norime tiesiogiai, mes naudojame jį, kad nurodytume sistemai gauti tai, ko norime. Kadangi iškvietimo į paslėptą funkciją šaltinis yra sistema, juodasis sąrašas mūsų nebeturi įtakos.

Taigi baigėme. Dabar turime būdą, kaip apeiti API juodąjį sąrašą. Tai šiek tiek sudėtinga, bet galėtume parašyti įvyniojimo funkciją, kad nereikėtų kiekvieną kartą dvigubai atspindėti. Tai nėra puiku, bet tai geriau nei nieko. Bet iš tikrųjų mes to nebaigėme. Yra geresnis būdas tai padaryti – naudoti įprastą atspindį arba modifikuotą SDK, kaip senais gerais laikais.

Kadangi juodojo sąrašo vykdymas vertinamas atsižvelgiant į procesą (kas daugeliu atvejų yra tas pats, kaip ir kiekvienai programai), sistemai gali būti koks nors būdas įrašyti, ar atitinkamai programai taikoma išimtis, ar ne. Laimei, yra ir jis mums prieinamas. Naudodami tą naujai atrastą dvigubo atspindžio įsilaužimą, turime vienkartinį kodo bloką:

val forName = Class::class.java.getDeclaredMethod("forName", String:: class.java)
val getDeclaredMethod = Class::class.java.getDeclaredMethod("getDeclaredMethod", String:: class.java, arrayOf>()::class.java)

val vmRuntimeClass = forName.invoke(null, "dalvik.system.VMRuntime") asClass
val getRuntime = getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null) as Method
val setHiddenApiExemptions = getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", arrayOf(arrayOf<String>()::class.java)) asMethod

val vmRuntime = getRuntime.invoke(null)

setHiddenApiExemptions.invoke(vmRuntime, arrayOf("L"))

Gerai, techniškai tai nereiškia, kad mūsų programa yra išbraukta iš API juodojo sąrašo. Iš tikrųjų yra dar viena ADB komanda, kurią galite vykdyti norėdami nurodyti funkcijas, kurios neturėtų būti įtrauktos į juodąjį sąrašą. Tuo mes ir pasinaudojame aukščiau. Kodas iš esmės nepaiso to, ką sistema mano, kad mūsų programai netaikoma. „L“ praleidimas pabaigoje reiškia, kad visi metodai atleidžiami. Jei norite atleisti konkrečius metodus, naudokite Smali sintaksę: Landroid/some/hidden/Class; Landroid/some/other/hidden/Class;.

Dabar mes iš tikrųjų baigėme. Sukurkite pasirinktinę programos klasę, įdėkite šį kodą į onCreate() metodas, ir bam, jokių apribojimų.


Dėkojame XDA nariui Weishu, „VirtualXposed“ ir „Taichi“ kūrėjui, už šio metodo atradimą. Taip pat norėtume padėkoti XDA pripažintam kūrėjui topjohnwu, kuris nurodė man šį sprendimą. Štai šiek tiek daugiau apie tai, kaip tai veikia, nors jis yra kinų kalba. aš taip pat rašė apie tai Stack Overflow, su pavyzdžiu ir JNI.