Android11繞過(guò)反射限制的方法
1. 問(wèn)題出現(xiàn)的背景
騰訊視頻在集成我們 replay sdk 的時(shí)候發(fā)現(xiàn)這么個(gè)錯(cuò)誤,導(dǎo)致整個(gè) db mock 功能完全失效。
Accessing hidden field Landroid/database/sqlite/SQLiteCursor;
->mDriver:Landroid/database/sqlite/SQLiteCursorDriver; (greylist-max-o, reflection, denied)java.lang.NoSuchFieldException: No field mDriver in class Landroid/database/sqlite/SQLiteCursor;
(declaration of 'android.database.sqlite.SQLiteCursor' appears in /system/framework/framework.jar)
我清晰的記得我們引入了一個(gè)第三方解決方案,在 9.0 以上已經(jīng)解決了這個(gè)問(wèn)題,大致的方案是這樣的:
if (SDK_INT >= Build.VERSION_CODES.P) { try { 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); setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", new Class[]{String[].class}); sVmRuntime = getRuntime.invoke(null); } catch (Throwable e) { Log.e(TAG, "reflect bootstrap failed:", e); } }
嚇得我趕緊去看下到底有沒(méi)有貓膩,發(fā)現(xiàn)在 Android 11 上果然有問(wèn)題:
Accessing hidden method Ldalvik/system/VMRuntime;
->setHiddenApiExemptions([Ljava/lang/String;)V (blacklist,core-platform-api, reflection, denied)Caused by: java.lang.NoSuchMethodException: dalvik.system.VMRuntime.setHiddenApiExemptions [class [Ljava.lang.String;]
......
2. 分析問(wèn)題出現(xiàn)的原因
本著時(shí)間緊任務(wù)重盡量不影響進(jìn)度的情況下,我還是想去網(wǎng)上搜索看看,但是發(fā)現(xiàn)都是一堆舊的方案。迫不得已去看看到底為什么?到底為什么?剛好前幾天找同事要了一份 Android 11 的源碼。
static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jstring name, jobjectArray args) { // …… Handle<mirror::Method> result = hs.NewHandle( mirror::Class::GetDeclaredMethodInternal<kRuntimePointerSize>( soa.Self(), klass, soa.Decode<mirror::String>(name), soa.Decode<mirror::ObjectArray<mirror::Class>>(args), GetHiddenapiAccessContextFunction(soa.Self()))); if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } return soa.AddLocalReference<jobject>(result.Get()); }
如果 ShouldDenyAccessToMember 返回 true,那么就會(huì)返回 null,上層就會(huì)拋出方法找不到的異常。這里和 Android P 沒(méi)什么不同,只是把 ShouldBlockAccessToMember 改了個(gè)名而已。
ShouldDenyAccessToMember 會(huì)調(diào)用到 hiddenapi::ShouldDenyAccessToMember,該函數(shù)是這樣實(shí)現(xiàn)的:
template<typename T> inline bool ShouldDenyAccessToMember(T* member, const std::function<AccessContext()>& fn_get_access_context, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { const uint32_t runtime_flags = GetRuntimeFlags(member); // 1:如果該成員是公開(kāi)API,直接通過(guò) if ((runtime_flags & kAccPublicApi) != 0) { return false; } // 2:不是公開(kāi)API(即為隱藏API),獲取調(diào)用者和被訪問(wèn)成員的 Domain // 主要看這個(gè) const AccessContext caller_context = fn_get_access_context(); const AccessContext callee_context(member->GetDeclaringClass()); // 3:如果調(diào)用者是可信的,直接返回 if (caller_context.CanAlwaysAccess(callee_context)) { return false; } // ...... }
原來(lái)的方案失效了能在 FirstExternalCallerVisitor 的 VisitFrame 方法中找到答案
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod *m = GetMethod(); ...... ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClass(); if (declaring_class->IsBootStrapClassLoaded()) { ...... // 如果 PREVENT_META_REFLECTION_BLACKLIST_ACCESS 為 Enabled,跳過(guò)來(lái)自 java.lang.reflect.* 的訪問(wèn) // 系統(tǒng)對(duì)“套娃反射”的限制的關(guān)鍵就在此 ObjPtr<mirror::Class> proxy_class = GetClassRoot<mirror::Proxy>(); if (declaring_class->IsInSamePackage(proxy_class) && declaring_class != proxy_class) { if (Runtime::Current()->isChangeEnabled(kPreventMetaReflectionBlacklistAccess)) { return true; } } } caller = m; return false; }
3. 解決方案
- native hook 住 ShouldDenyAccessToMember 方法,直接返回 false
- 破壞調(diào)用堆棧繞過(guò)去,使 VM 無(wú)法識(shí)別調(diào)用方
我們采用的是第二種方案,有什么方法可以讓 VM 無(wú)法識(shí)別我的調(diào)用棧呢?這可以通過(guò) JniEnv::AttachCurrentThread(…) 函數(shù)創(chuàng)建一個(gè)新的 Thread 來(lái)完成。具體我們可以看下這里 https://developer.android.com/training/articles/perf-jni ,然后配合 std::async(…) 與 std::async::get(..) 就能搞定了,下面是關(guān)鍵代碼:
// java 層直接用 jni 調(diào)用這個(gè)方法 static jobject Java_getDeclaredMethod( JNIEnv *env, jclass interface, jobject clazz, jstring method_name, jobjectArray params) { // ...... 省掉一些轉(zhuǎn)換代碼 // 先用 std::async 調(diào)用 getDeclaredMethod_internal 方法 auto future = std::async(&getDeclaredMethod_internal, global_clazz, global_method_name, global_params); auto result = future.get(); return result; } static jobject getDeclaredMethod_internal( jobject clazz, jstring method_name, jobjectArray params) { // 這里就是一些普通的 jni 操作了 JNIEnv *env = attachCurrentThread(); jclass clazz_class = env->GetObjectClass(clazz); jmethodID get_declared_method_id = env->GetMethodID(clazz_class, "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); jobject res = env->CallObjectMethod(clazz, get_declared_method_id, method_name, params); detachCurrentThread(); return env->NewGlobalRef(res); } JNIEnv *attachCurrentThread() { JNIEnv *env; // AttachCurrentThread 核心在這里 int res = _vm->AttachCurrentThread(&env, nullptr); return env; }
到此這篇關(guān)于Android11繞過(guò)反射限制的方法的文章就介紹到這了,更多相關(guān)Android 繞過(guò)反射限制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)多媒體之播放音樂(lè)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)多媒體之播放音樂(lè)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02Android利用Flutter?path繪制粽子的示例代碼
端午將至,作為中華民族的非常重要的傳統(tǒng)節(jié)日,粽子那是必不可少的。今天跟隨本篇文章用Flutter?path畫(huà)一個(gè)會(huì)科普節(jié)日的的粽子吧2022-05-05Android音頻開(kāi)發(fā)之SurfaceView的使用詳解
這篇文章主要為大家介紹了Android中SurfaceView的使用方法,本文通過(guò)簡(jiǎn)要的案例,為大家進(jìn)行了詳細(xì)的講解,需要的朋友可以參考一下2022-04-04Android實(shí)現(xiàn)支付寶螞蟻森林水滴浮動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)支付寶螞蟻森林水滴浮動(dòng)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Android判斷App前臺(tái)運(yùn)行還是后臺(tái)運(yùn)行(運(yùn)行狀態(tài))
這篇文章主要介紹了Android判斷App前臺(tái)運(yùn)行還是后臺(tái)運(yùn)行的相關(guān)資料,需要的朋友可以參考下2016-04-04Android SurfaceView拍照錄像實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了Android SurfaceView拍照錄像實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02Android使用lottie加載json動(dòng)畫(huà)的示例代碼
本篇文章主要介紹了Android使用lottie加載json動(dòng)畫(huà)的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01Android Studio使用小技巧:布局預(yù)覽時(shí)填充數(shù)據(jù)
這篇文章主要介紹了Android Studio使用小技巧:布局預(yù)覽時(shí)填充數(shù)據(jù),本文給出了代碼操作實(shí)例和效果圖例,需要的朋友可以參考下2015-05-05