Jak obejít skrytou černou listinu API na Androidu 9+

Google zavedl v Androidu 9 omezení, na kterých mohou vývojáři přistupovat k API. Zde je návod, jak obejít tato skrytá omezení API.

V roce 2018 Google vydal Android Pie. Mezi změnami uživatelského rozhraní a novými funkcemi došlo také k některým změnám na straně vývojáře. Tyto změny zahrnovaly nová rozhraní API, opravy chyb pro stávající rozhraní API a také omezení přístupu ke skrytým API.

Naštěstí však existují způsoby, jak tato omezení obejít. Než se pustím do toho, jak obejít omezení, měl bych trochu vysvětlit, co jsou to skrytá API, proč byla vůbec omezena a proč k nim možná budete chtít přistupovat.

Skrytá rozhraní API jsou rozhraní API v systému Android, která vývojáři aplikací běžně nevidí. Pokud se podíváte na kód AOSP, uvidíte spoustu tříd, proměnných a metod, které mají @hide anotace v bloku komentářů nad nimi.

Tato anotace dává pokyn, jaký nástroj Google používá při kompilaci sady SDK k vyloučení položky pod ní. Tato sada SDK je poté distribuována vývojářům v rámci sad SDK stažených prostřednictvím aplikace Android Studio. Pokud nepoužijete upravenou sadu SDK, Android Studio si bude myslet, že některá z těchto skrytých položek prostě neexistuje. Pokud se pokusíte použít přímo, zobrazí se červeně a odmítne kompilaci.

Existuje mnoho důvodů, proč může být API skryté. Některé věci jsou určeny pouze pro interní nebo systémové aplikace a nebudou fungovat, pokud je používá aplikace třetí strany. Jiné jsou experimentální nebo nestabilní a mohou být v budoucnu odstraněny nebo změněny. Některá jsou dokonce jen rozhraní API, Google prostě nechce použít normální cyklus ukončení podpory, pokud budou někdy odstraněna.

Zatímco standardní Android SDK má a hodně v tom někdy nestačí. Někdy je něco, co chcete udělat, co již v Androidu existuje, ale není veřejně odhaleno.

Například mnoho aplikací, které vytvářím, včetně SystemUI Tuner a Widgety pro uzamčení obrazovky, využijte spoustu různých skrytých API. SystemUI Tuner potřebuje přístup k některým, aby bylo možné správně sledovat, měnit a resetovat možnosti. Lockscreen Widgets některé používá mimo jiné k zobrazení tapety pod ním.

Většina vývojářů nepotřebuje přistupovat ke skrytým API, ale někdy mohou být docela užitečné.

S vydáním Androidu 9 (Pie) Google představil skrytý blacklist API. Nebylo zahrnuto každé skryté API a existovaly různé úrovně seznamů. Skrytá rozhraní API na seznamu povolených mohou mít přístup kdokoli. Skrytá rozhraní API na světle šedém seznamu mohou být přístupná z jakékoli aplikace, ale v budoucích verzích Androidu mohou být nedostupná. K čemukoli na tmavém šedém seznamu bylo možné přistupovat pouze aplikacemi, které cílí na úrovně API před Pie (tj. před úrovní API 28). Aplikacím cíleným na Pie a novější by byl odepřen přístup. Nakonec ke skrytým rozhraním API na černé listině nemohla přistupovat žádná nesystémová aplikace (nebo aplikace, která není na seznamu povolených), bez ohledu na cílové rozhraní API.

Android 10 změnil způsob uspořádání seznamů a mírně je zjednodušil, ale myšlenka zůstala stejná. K určitým skrytým rozhraním API mohly aplikace přistupovat, zatímco jiné byly blokovány. Android 11 posílila detekci přístupu na blokovat obchvat používá se pro koláč a 10.

Ve všech verzích Androidu pokaždé, když se aplikace třetí strany pokusí o přístup ke skrytému API na černé listině, Android vyvolá příslušnou chybu „nenalezeno“.

Ve skutečnosti existuje několik způsobů, jak se dostat přes skrytou černou listinu API. V závislosti na vašich potřebách si můžete vybrat ty, které fungují pro všechny verze Androidu, ty, které fungují pouze pro Android 9 a 10, ty, které používají nativní kód C++, a ty, které jsou plně založené na Javě. Existuje dokonce řešení pouze pro vývoj pomocí ADB.

Řešení ADB

Pokud vaše zařízení používá Android Pie, spusťte následující dva příkazy ADB, abyste povolili skrytý přístup k rozhraní API.

adb shell settings put global hidden_api_policy_pre_p_apps 1
adb shell settings put global hidden_api_policy_p_apps 1

Pokud vaše zařízení používá Android 10 nebo novější, spusťte následující příkaz ADB a povolte skrytý přístup k rozhraní API.

adb shell settings put global hidden_api_policy 1

Chcete-li se vrátit k výchozímu chování, stačí nahradit put s delete a odstraňte 1.

Je zřejmé, že tyto příkazy nejsou pro produkční aplikaci zrovna užitečné. Z první ruky vám mohu říci, že správně poučit uživatele o tom, jak používat ADB, je neuvěřitelně obtížné. Mohou však být užitečné, pokud potřebujete aktualizovat starou aplikaci, aby vyhovovala novým omezením.

Řešení nativní/JNI

Existují dva způsoby, jak obejít skrytý seznam zakázaných rozhraní API pomocí JNI v aplikaci pro Android. Jeden funguje pro Android 9 a 10 a druhý funguje pro Android 9 a novější.

Android 9 a 10

Pokud již máte nativní část své aplikace, bude snadné ji implementovat. Stačí použít JNI_OnLoad() funkce.

static art:: Runtime* runtime = nullptr;

extern "C"jint JNI_OnLoad(JavaVM *vm, void *reserved){
...
runtime = reinterpret_cast<: javavmext>(vm)->GetRuntime();
runtime->SetHiddenApiEnforcementPolicy(art:: hiddenapi:: EnforcementPolicy:: kNoChecks);
...
}

Uvědomte si, že tato metoda funguje pouze na Androidu 9 a 10.

Android 9 a novější

Pro libovolnou verzi Androidu máte na výběr ze dvou knihoven, které obejdou skryté omezení API: FreeReflection a RestrictionBypass.

Oba se snadno implementují a používají.

Implementovat FreeReflection, přidejte závislost do svého build.gradle na úrovni modulu.

implementation 'me.weishu: free_reflection: 3.0.1'

Pak přepište attachBaseContext() ve vaší třídě aplikací.

@Override
protectedvoidattachBaseContext(Context base){
super.attachBaseContext(base);
Reflection.unseal(base);
}

Pokud nemáte třídu Application, můžete ji přidat docela snadno. Vytvořte novou třídu, která se rozšiřuje Application a poté na něj ukažte v souboru AndroidManifest.xml.

Příklad:

publicclassAppextendsApplication{
...
@Override
protectedvoidattachBaseContext(Context base){
super.attachBaseContext(base);

Reflection.unseal(base);
}
}

<manifest>
...
...
name=".App">
...
application>
manifest>

Pro implementaci RestrictionBypass, přidejte úložiště JitPack do svého build.gradle na úrovni projektu.

allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}

Poté přidejte závislost do svého build.gradle na úrovni modulu.

implementation 'com.github.ChickenHook: RestrictionBypass: 2.2'

A to je vše. Tato knihovna automaticky odstraňuje omezení černé listiny.

Java řešení

I když jsou řešení JNI efektivní, jsou chvíle, kdy možná nebudete chtít používat nativní kód. Pokud ještě neděláte věci v C++, může to vaší aplikaci přidat zbytečnou velikost spolu s omezeními platformy. Naštěstí existují způsoby, jak obejít skrytý blacklist API pouze pomocí Javy.

Android 9 a 10

V Androidu 9 a 10 můžete použít to, co lze nazvat dvojitým odrazem nebo metareflexí, abyste obešli skrytý blacklist API. Protože systém kontroluje pouze to, co volají aplikace třetích stran, dvojitá reflexe jej přiměje, aby si myslel, že systém provádí skrytá volání API.

Tento trik lze použít k volání metody, která vaší aplikaci poskytne skryté výjimky z rozhraní API, vhodně pojmenované setHiddenApiExemptions(). Jednoduše přidejte následující kód někde na začátku životního cyklu vaší aplikace (například Application's onCreate() metoda) a poradí si s obcházením černé listiny.

Method forName = Class.class.getDeclaredMethod("forName", String.class);
Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);

Class vmRuntimeClass = (Class) forName.invoke(null, "dalvik.system.VMRuntime");
Method getRuntime = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null);
Method setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", newClass[] { String[].class} );

Object vmRuntime = getRuntime.invoke(null);
setHiddenApiExemptions.invoke(vmRuntime, newString[][] { newString[] { "L" } });

Pokud je vaše aplikace kompatibilní s verzemi Androidu nižšími než 9, nezapomeňte to zabalit do kontroly verze.

Android 9 a novější

Chcete-li obejít skrytý blacklist API na Androidu 9 a jakékoli novější verzi, můžete použít knihovnu LSPosed. Tato knihovna používá Java Unsafe API, takže je nepravděpodobné, že by se někdy zlomila.

Chcete-li to implementovat, stačí přidat závislost do vašeho build.gradle na úrovni modulu.

implementation 'org.lsposed.hiddenapibypass: hiddenapibypass: 2.0'

Poté jej použijte k obejití černé listiny.

HiddenApiBypass.addHiddenApiExemptions("L");

Pokud je vaše aplikace kompatibilní s verzemi Androidu nižšími než 9, nezapomeňte to zabalit do kontroly verze.

Závěr a další informace

Existuje spousta možností, jak obejít skrytý blacklist API v systému Android, bez ohledu na to, na kterou verzi platformy cílíte nebo používáte. Pokud se chcete dozvědět více o tom, jak tyto metody a knihovny fungují, nezapomeňte se podívat na následující odkazy.

  • Můj Stack Overflow Q&A.
  • Knihovna Hidden API Bypass LSposed na GitHubu.
  • Knihovna ChickenHook's RestrictionBypass na GitHubu.
  • tiann's FreeReflection knihovna na GitHubu.
  • Dokumentace Google na skryté černé listině API.