Utvecklare: Det är superlätt att kringgå Androids dolda API-begränsningar

click fraud protection

Android 9 Pie och Android 10 ger varningar eller blockerar direkt åtkomst till dolda API: er. Så här kan utvecklare komma runt de dolda API-begränsningarna.

Flashback till över ett år sedan, och vi är alla glada över att se vad som kommer att komma i Android P-betorna. Användare ser fram emot nya funktioner och utvecklare ser fram emot några nya verktyg för att göra sina appar bättre. Tyvärr för vissa av dessa utvecklare kom den första Android P-betan med lite av en otäck överraskning: dolda API-begränsningar. Innan jag dyker in i exakt vad det betyder, låt mig förklara lite om dess sammanhang.

Vad handlar det här om?

Android-apputvecklare behöver inte börja om från början när de skapar en app. Google tillhandahåller verktyg för att göra apputveckling enklare och mindre repetitiv. Ett av dessa verktyg är Android SDK. SDK är i huvudsak en referens till alla funktioner som utvecklare säkert kan använda i sina appar. Dessa funktioner är standard på alla varianter av Android som Google godkänner. SDK är dock inte uttömmande. Det finns en hel del "dolda" delar av Androids ramverk som inte är en del av SDK.

Dessa "dolda" delar kan vara otroligt användbara för mer hackiga eller lågnivåsaker. Till exempel min Widget Drawer app använder en icke-SDK-funktion för att upptäcka när en användare startar en app från en widget så att lådan automatiskt kan stängas. Du kanske tänker: "Varför inte bara göra dessa icke-SDK-funktioner till en del av SDK?" Tja, problemet är att deras operation inte är helt förutsägbar. Google kan inte se till att varje enskild del av ramverket fungerar på varje enhet som den godkänner, så viktigare metoder väljs ut för att verifieras. Google garanterar inte att resten av ramverket kommer att förbli konsekvent. Tillverkare kan ändra eller helt ta bort dessa dolda funktioner. Även i olika versioner av AOSP vet du aldrig om en dold funktion fortfarande kommer att finnas eller fungerar som den brukade.

Om du undrar varför jag har använt ordet "dold" beror det på att dessa funktioner inte ens är en del av SDK: n. Normalt, om du försöker använda en dold metod eller klass i en app, kommer den att misslyckas med att kompilera. Att använda dem kräver reflexion eller en modifierad SDK.

Med Android P beslutade Google att det inte räckte att bara gömma dem. När den första betan släpptes, det meddelades att de flesta (inte alla) dolda funktioner var inte längre tillgängliga för apputvecklare. Den första betan skulle varna dig när din app använde en svartlistad funktion. Nästa betaversion kraschade helt enkelt din app. Åtminstone för mig var den här svarta listan ganska irriterande. Inte nog med att det gick sönder en hel del Navigeringsgester, men eftersom dolda funktioner är svartlistade som standard (Google måste manuellt vitlista vissa per utgåva), hade jag stora problem med att få Widget Drawer att fungera.

Nu fanns det några sätt att kringgå den svarta listan. Den första var att helt enkelt behålla din appinriktning API 27 (Android 8.1), eftersom den svarta listan bara gällde appar som riktade in sig på det senaste API: et. Tyvärr med Googles minimikrav för API för publicering i Play Store skulle det bara vara möjligt att rikta in sig på API 27 så länge. Från och med den 1 november 2019, alla appuppdateringar till Play Butik måste vara inriktade på API 28 eller senare.

Den andra lösningen är faktiskt något som Google har byggt in i Android. Det är möjligt att köra ett ADB-kommando för att inaktivera den svarta listan helt. Det är bra för personligt bruk och testning, men jag kan berätta för dig att det är en mardröm att försöka få slutanvändare att ställa in och köra ADB.

Så var lämnar det oss? Vi kan inte längre rikta in oss på API 27 om vi vill ladda upp till Play Butik, och ADB-metoden är helt enkelt inte genomförbar. Det betyder dock inte att vi har slut på alternativ.

Den dolda API-svartlistan gäller endast icke-vitlistade användarapplikationer. Systemapplikationer, applikationer signerade med plattformssignaturen och applikationer som anges i en konfigurationsfil är alla undantagna från den svarta listan. Lustigt nog är Google Play Services-sviten alla specificerade i den konfigurationsfilen. Jag antar att Google är bättre än oss andra.

Hur som helst, låt oss fortsätta prata om den svarta listan. Den del vi är intresserade av idag är att systemapplikationer är undantagna. Det betyder till exempel att System UI-appen kan använda alla dolda funktioner den vill ha eftersom den finns på systempartitionen. Uppenbarligen kan vi inte bara skjuta en app till systempartitionen. Det kräver root, en bra filhanterare, kunskap om behörigheter... Det skulle vara lättare att använda ADB. Det är dock inte det enda sättet vi kan vara en systemapp, åtminstone när det gäller den dolda API-svarta listan.

Kasta tillbaka ditt sinne till för sju stycken sedan när jag nämnde reflektion. Om du inte vet vad reflektion är rekommenderar jag att läsa denna sida, men här är en snabb sammanfattning. I Java är reflektion ett sätt att komma åt normalt otillgängliga klasser, metoder och fält. Det är ett otroligt kraftfullt verktyg. Som jag sa i det stycket, brukade reflektion vara ett sätt att komma åt icke-SDK-funktioner. API-svartlistan blockerar användningen av reflektion, men den blockerar inte användningen av dubbel-reflexion.

Nu, här är där det blir lite konstigt. Normalt, om du vill anropa en dold metod, skulle du göra något så här (detta är i Kotlin, men Java är liknande):

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

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

Tack vare API-svartlistan skulle du dock bara få en ClassNotFoundException. Men om du reflekterar två gånger fungerar det bra:

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

Konstigt va? Jo, men också nej. API-svartlistan spårar vem som anropar en funktion. Om källan inte är undantagen kraschar den. I det första exemplet är källan appen. Men i det andra exemplet är källan själva systemet. Istället för att använda reflektion för att få det vi vill ha direkt, använder vi det för att tala om för systemet att få det vi vill ha. Eftersom källan till anropet till den dolda funktionen är systemet, påverkar den svarta listan oss inte längre.

Så vi är klara. Vi har ett sätt att kringgå API-svartlistan nu. Det är lite klumpigt, men vi skulle kunna skriva en omslagsfunktion så att vi inte behöver dubbelreflektera varje gång. Det är inte bra, men det är bättre än ingenting. Men egentligen är vi inte klara. Det finns ett bättre sätt att göra detta som låter oss använda normal reflektion eller en modifierad SDK, som på den gamla goda tiden.

Eftersom den svarta listans tillämpning utvärderas per process (vilket är samma som per app i de flesta fall), kan det finnas något sätt för systemet att registrera om appen i fråga är undantagen eller inte. Lyckligtvis finns det, och det är tillgängligt för oss. Genom att använda det nyfunna dubbelreflektionshacket har vi ett kodblock för engångsbruk:

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

Okej, tekniskt sett talar detta inte om för systemet att vår app är undantagen från API: s svarta lista. Det finns faktiskt ett annat ADB-kommando du kan köra för att specificera funktioner som inte ska svartlistas. Det är vad vi drar nytta av ovan. Koden åsidosätter i princip allt som systemet tror är undantaget för vår app. Att passera "L" i slutet betyder att alla metoder är undantagna. Om du vill undanta specifika metoder, använd Smali-syntaxen: Landroid/some/hidden/Class; Landroid/some/other/hidden/Class;.

Nu är vi faktiskt klara. Gör en anpassad applikationsklass, lägg den koden i onCreate() metod, och bam, inga fler restriktioner.


Tack till XDA Member weishu, utvecklaren av VirtualXposed och Taichi, för att du ursprungligen upptäckte denna metod. Vi vill också tacka XDA Recognized Developer topjohnwu för att ha påpekat denna lösning för mig. Här är lite mer om hur det fungerar, även om det är på kinesiska. jag med skrev om detta på Stack Overflow, med ett exempel i JNI också.