本文基於對Android P(Preview 1)的源碼分析,實現了三種繞過對調用隱藏API限制的方法,有效性均已獲得驗證,可以成功調用系統隱藏API。java
首先拋開Android P的具體實現過程,安卓系統要實現限制用戶代碼調用系統隱藏API,至少要作如下兩個區分:android
具體到Android P的代碼實現,它會在全部經過反射方式和JNI方式獲取Method和Field的地方調用如下函數判斷是否用戶代碼調用了系統的隱藏API(位於art/runtime/hidden_api.h),若是這個函數返回true,那麼說明用戶代碼調用了系統的隱藏API,Android P(Preview1)會經過log發出警告,用戶代碼仍然可以獲取到正確的Method或Field,在後續版本中獲取到的Method或Field極有可能爲空。api
那麼它是如何進行上述兩個區分的呢?app
下面咱們以調用android.app.ActivityThread類的currentActivityThread這個隱藏方法爲例,講解繞過限制的方法。ide
經過上面的論述結合源碼分析,咱們發現只有在經過反射方式和JNI方式獲取Method和Field時,系統纔有可能攔截對隱藏API的獲取,也就是說直接調用是能夠的!所以方法一的核心思想就是千方百計直接調用系統隱藏API。具體實現時須要用Provided方式提供Module或自定義android.jar。下面以一個例子說明實現過程。
咱們新建一個普通的android工程,在其MainActivity中直接調用ActivityThread.currentActivityThread();發現IDE提示找不到類ActivityThread,這是由於在sdk的android.jar(位於SDK/platforms/android-XX目錄下)中並無這個類的聲明,可是在實際運行時這個類是存在於系統中的。咱們的解決方法是以Provided方式提供一個Module,在此Module中提供須要的類(Provided方式是爲了編譯經過,這樣的Module的代碼並不會編譯到最終的apk中)。具體操做以下:
新建一個Module,其類型爲Java Library,命名爲libfakeandroid,而後在app的build.gradle中以Provided方式依賴libfakeandroid
函數
完成以上操做以後,MainActivity中就能直接調用ActivityThread.currentActivityThread();方法了。在Android P(Preview1)系統上運行不會出現警告log,成功!
注意:若是須要調用的隱藏API所在的類已經位於android.jar中,Provided方式再也不適用,此時須要自定義android.jar,將須要的Method或Field添加到android.jar中。
優勢:實現起來很是簡單方便,而且穩定性很好。
缺點:只能調用訪問權限爲public和default的Method和Field,不能直接調用protected和private的。工具
如今回頭看"限制原理"中論述的兩個區分,其實只要咱們可以混淆任何一個區分點都可以成功繞過此限制。混淆第一個區分點,會讓系統錯誤地認爲本來隱藏的API是公開的;混淆第二個區分點,會讓系統錯誤地將用戶代碼調用識別爲系統代碼調用。方法二的核心思想就是混淆第二個區分點。
關注第二個區分點,能夠發現,其實只要在BootStrapClassLoader加載的類中有任何一個幫助咱們進行反射的類就能繞過這個問題,那麼咱們可否將咱們apk中定義的類的ClassLoader改成BootStrapClassLoader呢?答案是確定的!查看art/runtime/mirror/class.h可知SetClassLoader函數能夠爲一個類指定ClassLoader,用IDA查看/system/lib/libart.so確認此函數位於導出符號表中。SetClassLoader的第一個參數類型爲ObjPtrmirror::Class,如何將jclass轉化爲此類型呢?經過在Android源碼中查找,在art/runtime/well_known_classes.h中有一個很是合適的函數ToClass可以完成此任務,其聲明以下
源碼分析
方法三經過混淆第一個區分點突破限制。gradle
本文提出並實現了三種在Android P上調用隱藏API的方法,分別有不一樣特色和適用範圍,工程中能夠根據實際狀況選用不一樣方法。ui