Jak ominąć ukrytą czarną listę API w systemie Android 9+

Google wprowadziło w systemie Android 9 ograniczenia dotyczące dostępu programistów do API. Oto jak ominąć te ukryte ograniczenia API.

Już w 2018 roku Google wypuściło Androida Pie. Wśród zmian w interfejsie użytkownika i nowych funkcji pojawiło się także kilka zmian po stronie deweloperów. Zmiany te obejmowały nowe interfejsy API, poprawki błędów dla istniejących interfejsów API, a także ograniczenia dostępu do ukrytych interfejsów API.

Na szczęście istnieją sposoby na obejście tych ograniczeń. Zanim przejdę do tego, jak ominąć ograniczenia, powinienem wyjaśnić, czym są ukryte interfejsy API, dlaczego w ogóle zostały ograniczone i dlaczego warto uzyskać do nich dostęp.

Ukryte interfejsy API to interfejsy API w systemie Android, których zwykle nie widzą twórcy aplikacji. Jeśli spojrzysz na kod AOSP, zobaczysz całą masę klas, zmiennych i metod, które mają @hide adnotację w bloku komentarza nad nimi.

Ta adnotacja instruuje narzędzie, którego Google używa podczas kompilowania pakietu SDK, aby wykluczyć z niego element. Ten zestaw SDK jest następnie dystrybuowany do programistów w ramach zestawów SDK pobranych za pośrednictwem Android Studio. Jeśli nie użyjesz zmodyfikowanego zestawu SDK, Android Studio pomyśli, że którykolwiek z tych ukrytych elementów po prostu nie istnieje. Jeśli spróbujesz użyć jednego bezpośrednio, wyświetli się ono na czerwono i odmówi kompilacji.

Istnieje wiele powodów, dla których interfejs API może być ukryty. Niektóre rzeczy są przeznaczone wyłącznie do użytku przez aplikacje wewnętrzne lub systemowe i nie będą działać, jeśli będą używane przez aplikację innej firmy. Inne mają charakter eksperymentalny lub niestabilny i mogą zostać usunięte lub zmienione w przyszłości. Niektóre z nich to nawet tylko interfejsy API, do których Google po prostu nie chce zastosować normalnego cyklu wycofywania, jeśli zostaną kiedykolwiek usunięte.

Chociaż standardowy zestaw SDK systemu Android ma działka w tym, czasami to nie wystarczy. Czasami jest coś, co chcesz zrobić i co już istnieje w Androidzie, ale nie jest publicznie dostępne.

Na przykład wiele aplikacji, które tworzę, w tym Tuner SystemUI I Widżety na ekranie blokady, skorzystaj z wielu różnych ukrytych interfejsów API. Tuner SystemUI musi uzyskać dostęp do niektórych, aby prawidłowo śledzić, zmieniać i resetować opcje. Widżety ekranu blokady używają niektórych, między innymi do wyświetlania tapety pod sobą.

Większość programistów nie musi uzyskiwać dostępu do ukrytych interfejsów API, ale czasami mogą one być całkiem przydatne.

Wraz z wydaniem Androida 9 (Pie) Google wprowadził ukrytą czarną listę API. Nie uwzględniono wszystkich ukrytych interfejsów API i istniały różne poziomy list. Dostęp do ukrytych interfejsów API na białej liście może uzyskać każdy. Dostęp do ukrytych interfejsów API na jasnoszarej liście może uzyskać każda aplikacja, ale mogą one być niedostępne w przyszłych wersjach Androida. Dostęp do wszystkiego na ciemnoszarej liście mogły uzyskać tylko aplikacje korzystające z poziomów API wcześniejszych niż Pie (tj. wcześniejszych niż poziom API 28). Aplikacje kierowane na Pie i nowsze wersje otrzymają odmowę dostępu. Wreszcie, dostęp do ukrytych interfejsów API na czarnej liście nie mogła uzyskać żadna aplikacja niesystemowa (lub nie znajdująca się na białej liście), niezależnie od docelowego interfejsu API.

Android 10 zmienił organizację list i nieco je uprościł, ale pomysł pozostał ten sam. Aplikacje mogły uzyskać dostęp do niektórych ukrytych interfejsów API, inne zaś były blokowane. Androida 11 wzmocniono wykrywanie dostępu Do zablokować obejście używany do ciasta i 10.

We wszystkich wersjach Androida za każdym razem, gdy aplikacja innej firmy próbuje uzyskać dostęp do ukrytego interfejsu API znajdującego się na czarnej liście, system Android zgłasza odpowiedni błąd „nie znaleziono”.

Istnieje kilka sposobów na ominięcie ukrytej czarnej listy API. W zależności od potrzeb możesz wybrać te, które działają na wszystkich wersjach Androida, te, które działają tylko na Androidzie 9 i 10, te, które korzystają z natywnego kodu C++ oraz te, które są w pełni oparte na Javie. Istnieje nawet obejście przeznaczone wyłącznie dla programistów, wykorzystujące ADB.

Rozwiązanie ADB

Jeśli na Twoim urządzeniu działa system Android Pie, uruchom następujące dwa polecenia ADB, aby włączyć ukryty dostęp do interfejsu API.

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

Jeśli na Twoim urządzeniu działa system Android 10 lub nowszy, uruchom następujące polecenie ADB, aby włączyć ukryty dostęp API.

adb shell settings put global hidden_api_policy 1

Aby powrócić do domyślnego zachowania, po prostu zamień put z delete i usuń 1.

Oczywiście te polecenia nie są zbyt przydatne w przypadku aplikacji produkcyjnej. Mogę powiedzieć z pierwszej ręki, że prawidłowe instruowanie użytkowników, jak korzystać z ADB, jest niezwykle trudne. Mogą się jednak przydać, jeśli chcesz zaktualizować starą aplikację, aby była zgodna z nowymi ograniczeniami.

Obejście natywne/JNI

Istnieją dwa sposoby ominięcia ukrytej czarnej listy API za pomocą JNI w aplikacji na Androida. Jeden działa na Androidzie 9 i 10, a drugi na Androidzie 9 i nowszych.

Androida 9 i 10

Jeśli masz już natywną część swojej aplikacji, będzie to łatwe do wdrożenia. Po prostu skorzystaj z JNI_OnLoad() funkcjonować.

grafika statyczna:: Runtime* runtime = nullptr;

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

Pamiętaj, że ta metoda działa tylko na Androidzie 9 i 10.

Android 9 i nowsze wersje

W przypadku dowolnej wersji Androida masz do wyboru dwie biblioteki umożliwiające ominięcie ukrytych ograniczeń API: FreeReflection i RestrictionBypass.

Obydwa są łatwe do wdrożenia i użycia.

Aby zaimplementować FreeReflection, dodaj zależność do pliku build.gradle na poziomie modułu.

implementation 'me.weishu: free_reflection: 3.0.1'

Następnie nadpisz attachBaseContext() w klasie aplikacji.

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

Jeśli nie masz klasy aplikacji, możesz ją łatwo dodać. Utwórz nową klasę, która rozszerza Application a następnie wskaż go w pliku AndroidManifest.xml.

Przykład:

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

Reflection.unseal(base);
}
}

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

Aby zaimplementować RestrictionBypass, dodaj repozytorium JitPack do pliku build.gradle na poziomie projektu.

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

Następnie dodaj zależność do pliku build.gradle na poziomie modułu.

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

I to wszystko. Ta biblioteka automatycznie usuwa ograniczenia czarnej listy.

Obejście Java

Chociaż rozwiązania JNI są skuteczne, czasami możesz nie chcieć używać kodu natywnego. Jeśli jeszcze nie pracujesz w języku C++, może on dodać do Twojej aplikacji niepotrzebny rozmiar wraz z ograniczeniami platformy. Na szczęście istnieją sposoby na ominięcie ukrytej czarnej listy API, używając wyłącznie języka Java.

Androida 9 i 10

W Androidzie 9 i 10 możesz użyć czegoś, co można nazwać podwójnym odbiciem lub meta-odbiciem, aby ominąć ukrytą czarną listę API. Ponieważ system sprawdza tylko, co wywołują aplikacje innych firm, podwójna refleksja oszukuje go, myśląc, że system wykonuje ukryte wywołania API.

Tę sztuczkę można wykorzystać do wywołania metody zapewniającej aplikacji ukryte wyjątki API, o trafnej nazwie setHiddenApiExemptions(). Po prostu dodaj następujący kod gdzieś na początku cyklu życia aplikacji (np onCreate() metoda) i poradzi sobie z ominięciem czarnej listy.

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

Jeśli Twoja aplikacja jest kompatybilna z wersjami Androida starszymi niż 9, pamiętaj o uwzględnieniu tego w kontroli wersji.

Android 9 i nowsze wersje

Aby ominąć ukrytą czarną listę API w systemie Android 9 i dowolnej nowszej wersji, możesz skorzystać z biblioteki LSPosed. Ta biblioteka korzysta z interfejsu API Javy Unsafe, więc jest mało prawdopodobne, że kiedykolwiek się zepsuje.

Aby to zaimplementować, po prostu dodaj zależność do pliku build.gradle na poziomie modułu.

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

Następnie użyj go, aby ominąć czarną listę.

HiddenApiBypass.addHiddenApiExemptions("L");

Jeśli Twoja aplikacja jest kompatybilna z wersjami Androida starszymi niż 9, pamiętaj o uwzględnieniu tego w kontroli wersji.

Wnioski i więcej informacji

Istnieje wiele opcji ominięcia ukrytej czarnej listy API w systemie Android, niezależnie od tego, na którą wersję platformy się wybierasz lub z której korzystasz. Jeśli chcesz dowiedzieć się więcej o działaniu tych metod i bibliotek, koniecznie sprawdź poniższe linki.

  • Pytania i odpowiedzi dotyczące przepełnienia stosu.
  • Biblioteka Hidden API Bypass firmy LSposed w serwisie GitHub.
  • Biblioteka RestrictionBypass firmy ChickenHook w serwisie GitHub.
  • biblioteka FreeReflection firmy Tiann w serwisie GitHub.
  • Dokumentacja Google dotycząca ukrytej czarnej listy API.