Vývojári: Je veľmi jednoduché obísť skryté obmedzenia API systému Android

click fraud protection

Android 9 Pie a Android 10 hádžu upozornenia alebo priamo blokujú prístup k skrytým rozhraniam API. Tu je návod, ako môžu vývojári obísť skryté obmedzenia rozhrania API.

Späť na obdobie spred viac ako roka a všetci sme nadšení z toho, čo príde v beta verziách systému Android P. Používatelia sa tešia na nové funkcie a vývojári sa tešia na niektoré nové nástroje na zlepšenie ich aplikácií. Nanešťastie pre niektorých z týchto vývojárov prišla prvá beta verzia systému Android P s trochu nepríjemným prekvapením: skrytými obmedzeniami rozhrania API. Predtým, ako sa ponorím do toho, čo to presne znamená, dovoľte mi vysvetliť trochu jeho kontext.

O čom to všetko je?

Vývojári aplikácií pre Android nemusia pri vytváraní aplikácie začínať od nuly. Google poskytuje nástroje, ktoré zjednodušujú vývoj aplikácií a znižujú ich opakovanie. Jedným z týchto nástrojov je Android SDK. SDK je v podstate odkaz na všetky funkcie, ktoré môžu vývojári bezpečne používať vo svojich aplikáciách. Tieto funkcie sú štandardom pre všetky varianty Androidu, ktoré Google schvaľuje. Súprava SDK však nie je vyčerpávajúca. Existuje pomerne veľa „skrytých“ častí rámca systému Android, ktoré nie sú súčasťou súpravy SDK.

Tieto „skryté“ časti môžu byť neuveriteľne užitočné pre zložitejšie veci alebo veci na nízkej úrovni. Napríklad môj Aplikácia Widget Drawer využíva funkciu, ktorá nie je súčasťou súpravy SDK, aby zistila, kedy používateľ spustí aplikáciu z miniaplikácie, aby sa zásuvka mohla automaticky zavrieť. Možno si pomyslíte: „Prečo jednoducho neurobiť tieto funkcie, ktoré nie sú súčasťou súpravy SDK?“ No problém je v tom, že ich fungovanie nie je úplne predvídateľné. Google nemôže zabezpečiť, aby každá jednotlivá časť rámca fungovala na každom jednom zariadení, ktoré schváli, preto sa na overenie vyberajú dôležitejšie metódy. Google nezaručuje, že zvyšok rámca zostane konzistentný. Výrobcovia môžu tieto skryté funkcie zmeniť alebo úplne odstrániť. Ani v rôznych verziách AOSP nikdy neviete, či skrytá funkcia bude stále existovať alebo bude fungovať tak, ako predtým.

Ak sa pýtate, prečo používam slovo „skryté“, je to preto, že tieto funkcie nie sú ani súčasťou súpravy SDK. Za normálnych okolností, ak sa v aplikácii pokúsite použiť skrytú metódu alebo triedu, kompilácia zlyhá. Ich použitie vyžaduje odraz alebo upravenú súpravu SDK.

S Androidom P sa Google rozhodol, že len ich skrytie nestačí. Keď bola vydaná prvá beta, bolo to oznámené väčšina (nie všetky) skryté funkcie už nebola dostupná pre vývojárov aplikácií. Prvá beta vás upozorní, keď vaša aplikácia použije funkciu na čiernej listine. Nasledujúce beta verzie jednoducho zrútili vašu aplikáciu. Aspoň pre mňa bola táto čierna listina dosť otravná. Nielenže sa to dosť zlomilo Navigačné gestá, ale keďže skryté funkcie sú v predvolenom nastavení na čiernej listine (Google musí manuálne pridať niektoré na bielu listinu pre jednotlivé vydania), mal som veľké problémy so spustením zásuviek miniaplikácií.

Teraz existovalo niekoľko spôsobov, ako obísť čiernu listinu. Prvým bolo jednoducho zachovať zacielenie vašej aplikácie na rozhranie API 27 (Android 8.1), pretože zoznam zakázaných položiek sa týkal iba aplikácií zacielených na najnovšie rozhranie API. Bohužiaľ, s Google minimálne požiadavky na API pre publikovanie v obchode Play by bolo možné tak dlho cieliť iba na API 27. Od 1. novembra 2019, všetky aktualizácie aplikácií v Obchode Play musia byť zamerané na rozhranie API 28 alebo novšie.

Druhým riešením je vlastne niečo, čo Google zabudoval do Androidu. Je možné spustiť príkaz ADB na úplné vypnutie čiernej listiny. To je skvelé na osobné použitie a testovanie, ale z prvej ruky vám môžem povedať, že snažiť sa prinútiť koncových používateľov, aby si nastavili a spustili ADB, je nočná mora.

Tak kde nás to opúšťa? Ak chceme nahrať do Obchodu Play, už nemôžeme zacieliť na API 27 a metóda ADB jednoducho nie je životaschopná. To však neznamená, že sme mimo možnosti.

Skrytá čierna listina API sa vzťahuje len na používateľské aplikácie, ktoré nie sú na zozname povolených. Systémové aplikácie, aplikácie podpísané podpisom platformy a aplikácie špecifikované v konfiguračnom súbore sú vyňaté z čiernej listiny. Je zábavné, že všetky balíky služieb Google Play sú špecifikované v tomto konfiguračnom súbore. Myslím, že Google je lepší ako my ostatní.

Každopádne, hovorme ďalej o čiernej listine. Časť, ktorá nás dnes zaujíma, je, že systémové aplikácie sú vyňaté. To napríklad znamená, že aplikácia System UI môže používať všetky skryté funkcie, ktoré chce, pretože je na systémovom oddiele. Je zrejmé, že nemôžeme len tak natlačiť aplikáciu do systémového oddielu. To chce root, dobrého správcu súborov, znalosť oprávnení... Bolo by jednoduchšie použiť ADB. Nie je to však jediný spôsob, ako môžeme byť systémovou aplikáciou, aspoň pokiaľ ide o skrytý zoznam zakázaných rozhraní API.

Vráťte svoju myseľ späť do obdobia pred siedmimi odsekmi, keď som spomenul reflexiu. Ak neviete, čo je odraz, odporúčam prečítať táto strana, ale tu je rýchle zhrnutie. V Jave je reflexia spôsob, ako pristupovať k bežne nedostupným triedam, metódam a poliam. Je to neuveriteľne silný nástroj. Ako som povedal v tomto odseku, reflexia bývala spôsob, ako získať prístup k funkciám, ktoré nie sú SDK. Čierna listina API blokuje použitie odrazu, ale neblokuje použitie dvojitý-reflexia.

No, tu je to trochu divné. Normálne, ak by ste chceli zavolať skrytú metódu, urobili by ste niečo takéto (toto je v Kotline, ale Java je podobná):

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

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

Vďaka čiernej listine API by ste však dostali iba výnimku ClassNotFoundException. Ak sa však zamyslíte dvakrát, funguje to dobre:

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")

Divné, že? No áno, ale aj nie. Čierna listina API sleduje, kto volá funkciu. Ak zdroj nie je vyňatý, zlyhá. V prvom príklade je zdrojom aplikácia. V druhom príklade je však zdrojom samotný systém. Namiesto toho, aby sme použili reflexiu na priame získanie toho, čo chceme, používame to na to, aby sme povedali systému, aby dostal to, čo chceme. Keďže zdrojom volania skrytej funkcie je systém, blacklist sa nás už netýka.

Tak sme skončili. Teraz máme spôsob, ako obísť čiernu listinu API. Je to trochu neohrabané, ale mohli by sme napísať funkciu wrapper, aby sme nemuseli zakaždým dvakrát odrážať. Nie je to skvelé, ale je to lepšie ako nič. Ale v skutočnosti sme ešte neskončili. Existuje lepší spôsob, ako to urobiť, ktorý nám umožní použiť normálnu reflexiu alebo upravenú súpravu SDK, ako za starých dobrých čias.

Keďže presadzovanie na čiernej listine sa vyhodnocuje pre každý proces (čo je vo väčšine prípadov rovnaké ako pre každú aplikáciu), môže existovať spôsob, ako systém zaznamenať, či je príslušná aplikácia vyňatá alebo nie. Našťastie existuje a je nám prístupný. Pomocou tohto novo nájdeného hacku s dvojitým odrazom máme blok kódu na jednorazové použitie:

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"))

Dobre, technicky to teda systému nehovorí, že naša aplikácia je vyňatá z čiernej listiny API. V skutočnosti existuje ďalší príkaz ADB, ktorý môžete spustiť na zadanie funkcií, ktoré by nemali byť na čiernej listine. To je to, čo využívame vyššie. Kód v podstate prepíše čokoľvek, čo si systém myslí, že je pre našu aplikáciu vyňaté. Odovzdanie „L“ na konci znamená, že všetky metódy sú vyňaté. Ak chcete vyňať konkrétne metódy, použite syntax Smali: Landroid/some/hidden/Class; Landroid/some/other/hidden/Class;.

Teraz sme vlastne skončili. Vytvorte vlastnú triedu aplikácií, vložte tento kód do onCreate() metóda, a bam, už žiadne obmedzenia.


Ďakujeme XDA Member weishu, vývojárovi VirtualXposed a Taichi, za originálny objav tejto metódy. Tiež by sme sa chceli poďakovať XDA Recognized Developer topjohnwu za to, že ma upozornil na toto riešenie. Tu je trochu viac o tom, ako to funguje, hoci je v čínštine. ja tiež napísal o tom na Stack Overflow, s príkladom aj v JNI.