Програмери: Супер је лако заобићи Андроид-ова скривена ограничења АПИ-ја

click fraud protection

Андроид 9 Пие и Андроид 10 бацају упозорења или директно блокирају приступ скривеним АПИ-јима. Ево како програмери могу да заобиђу скривена ограничења АПИ-ја.

Вратимо се на пре више од годину дана и сви смо узбуђени што ћемо видети шта ће доћи у Андроид П бета верзијама. Корисници се радују новим функцијама, а програмери се радују неким новим алатима да побољшају своје апликације. Нажалост за неке од тих програмера, прва Андроид П бета верзија је дошла са мало гадног изненађења: скривена ограничења АПИ-ја. Пре него што зароним у шта то тачно значи, дозволите ми да објасним мало о његовом контексту.

о чему се ради?

Програмери Андроид апликација не морају да почињу од нуле када праве апликацију. Гоогле обезбеђује алатке за лакше и мање понављање развоја апликација. Један од ових алата је Андроид СДК. СДК је у суштини референца на све функције које програмери могу безбедно да користе у својим апликацијама. Ове функције су стандардне на свим варијантама Андроид-а које Гоогле одобрава. Међутим, СДК није исцрпан. Постоји доста „скривених“ делова Андроидовог оквира који нису део СДК-а.

Ови „скривени“ делови могу бити невероватно корисни за више хакованих ствари или ствари ниског нивоа. На пример, мој Апликација Видгет Дравер користи функцију која није СДК за откривање када корисник покрене апликацију из виџета тако да се фиока може аутоматски затворити. Можда мислите: „Зашто једноставно не учините ове функције које нису СДК део СДК-а?“ Па, проблем је што њихов рад није у потпуности предвидљив. Гугл не може да се увери да сваки део оквира ради на сваком уређају који одобри, па се бирају важније методе за проверу. Гоогле не гарантује да ће остатак оквира остати доследан. Произвођачи могу променити или потпуно уклонити ове скривене функције. Чак и у различитим верзијама АОСП-а, никада не знате да ли ће скривена функција и даље постојати или ће радити као некада.

Ако се питате зашто сам користио реч „скривено“, то је зато што ове функције нису чак ни део СДК-а. Обично, ако покушате да користите скривени метод или класу у апликацији, неће успети да се преведе. Њихово коришћење захтева рефлексија или модификовани СДК.

Са Андроидом П, Гоогле је одлучио да само њихово скривање није довољно. Када је објављена прва бета, саопштено је да већина (не све) скривених функција више није била доступна за коришћење програмерима апликација. Прва бета верзија би вас упозорила када ваша апликација користи функцију са црне листе. Следеће бета верзије су једноставно срушиле вашу апликацију. Барем за мене, ова црна листа је била прилично досадна. Не само да се прилично сломио Покрети за навигацију, али пошто су скривене функције подразумевано на црној листи (Гоогле мора ручно да стави на белу листу неке по издању), имао сам много проблема да натерам Видгет Дравер да ради.

Сада је било неколико начина да се заобиђе црна листа. Први је био да једноставно задржите своју апликацију да циља АПИ 27 (Андроид 8.1), пошто се црна листа односи само на апликације које циљају најновији АПИ. Нажалост, са Гугловим минимални АПИ захтеви за објављивање у Плаи продавници, било би могуће циљати само АПИ 27 тако дуго. Од 01.11.2019, сва ажурирања апликација у Плаи продавници морају да циљају АПИ 28 или новији.

Друго решење је заправо нешто што је Гоогле уградио у Андроид. Могуће је покренути АДБ команду да бисте у потпуности онемогућили црну листу. То је одлично за личну употребу и тестирање, али могу вам рећи из прве руке да је покушај да се крајњи корисници подесе и покрену АДБ ноћна мора.

Па где нас то оставља? Не можемо више да циљамо АПИ 27 ако желимо да отпремимо у Плаи продавницу, а АДБ метод једноставно није одржив. То, међутим, не значи да смо остали без могућности.

Скривена црна листа АПИ-ја се примењује само на корисничке апликације које нису на белој листи. Системске апликације, апликације потписане потписом платформе и апликације наведене у конфигурационој датотеци су изузете са црне листе. Занимљиво је да су сви пакети Гоогле Плаи услуга наведени у тој конфигурационој датотеци. Претпостављам да је Гугл бољи од нас осталих.

У сваком случају, хајде да наставимо да причамо о црној листи. Део који нас данас занима је да су системске апликације изузете. То значи, на пример, да апликација Систем УИ може да користи све скривене функције које жели пошто се налази на системској партицији. Очигледно, не можемо само да гурнемо апликацију на системску партицију. За то је потребан роот, добар менаџер датотека, познавање дозвола... Било би лакше користити АДБ. Међутим, то није једини начин на који можемо бити системска апликација, барем што се скривене листе црних АПИ-ја тиче.

Вратите се на пре седам пасуса када сам поменуо размишљање. Ако не знате шта је рефлексија, препоручујем да прочитате Ова страница, али ево кратког резимеа. У Јави, рефлексија је начин да се приступи нормално недоступним класама, методама и пољима. То је невероватно моћан алат. Као што сам рекао у том параграфу, рефлексија је некада била начин приступа функцијама које нису СДК. АПИ црна листа блокира употребу рефлексије, али не блокира употребу дупло-рефлексија.

Сада, ево где постаје мало чудно. Обично, ако желите да позовете скривени метод, урадили бисте нешто овако (ово је у Котлину, али Јава је слична):

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

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

Међутим, захваљујући црној листи АПИ-ја, само бисте добили ЦлассНотФоундЕкцептион. Међутим, ако размислите двапут, добро функционише:

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

Чудно зар не? Па да, али и не. АПИ црна листа прати ко позива функцију. Ако извор није изузет, руши се. У првом примеру, извор је апликација. Међутим, у другом примеру извор је сам систем. Уместо да користимо рефлексију да директно добијемо оно што желимо, ми је користимо да кажемо систему да добије оно што желимо. Пошто је извор позива скривеној функцији систем, црна листа нас више не утиче.

Дакле, завршили смо. Сада имамо начин да заобиђемо црну листу АПИ-ја. Мало је незграпно, али могли бисмо да напишемо функцију омотача тако да не морамо сваки пут да рефлектујемо два пута. Није сјајно, али је боље него ништа. Али у ствари, нисмо завршили. Постоји бољи начин да то урадимо који ће нам омогућити да користимо нормалну рефлексију или модификовани СДК, као у стара добра времена.

Пошто се примена црне листе процењује по процесу (што је исто као по апликацији у већини случајева), можда постоји начин да систем забележи да ли је дотична апликација изузета или не. Срећом, има, и нама је доступно. Користећи тај новооткривени хак за двоструку рефлексију, имамо блок кода за једнократну употребу:

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

У реду, технички, ово не говори систему да је наша апликација изузета са црне листе АПИ-ја. Заправо постоји још једна АДБ команда коју можете покренути да одредите функције које не би требало да буду на црној листи. То је оно што користимо горе. Код у основи поништава све што систем мисли да је изузето за нашу апликацију. Преношење "Л" на крају значи да су све методе изузете. Ако желите да изузмете одређене методе, користите Смали синтаксу: Landroid/some/hidden/Class; Landroid/some/other/hidden/Class;.

Сада смо заправо завршили. Направите прилагођену класу апликације, ставите тај код у onCreate() метод, и бам, нема више ограничења.


Хвала члану КСДА веисху, програмеру ВиртуалКспосед и Таицхи, што је првобитно открио овај метод. Такође бисмо желели да се захвалимо КСДА Рецогнизед Девелопер топјохнву што ми је указао на ово решење. Ево мало више о томе како то функционише, иако је на кинеском. Такође писао о томе на Стацк Оверфлов-у, са примером и у ЈНИ.