Android源码中现在有大量的方法和变量被@hide所修饰,而这些被hide修饰的方法和变量是不允许应用层进行反射获取的,所以富有探索精神的程序员们就开始想尽各种办法绕过系统hide限制来使用@hide修饰的方法和变量。
1、套娃(适配Android10即之前)
Android11之前我们可以使用套娃的形式来欺骗系统,让系统误以为是系统调用的hide方法。而到了Android11之后,套娃就已经失效了喽,要寻找新的方法来和系统对抗。
我们通过反射 API 拿到 getDeclaredMethod 方法。getDeclaredMethod 是 public 的,不存在问题;这个通过反射拿到的方法我们称之为元反射方法。
我们通过刚刚反射拿到元反射方法去反射调用 getDeclardMethod。这里我们就实现了以系统身份去反射的目的——反射相关的 API 都是系统类,因此我们的元反射方法也是被系统类加载的方法;所以我们的元反射方法调用的 getDeclardMethod 会被认为是系统调用的,可以反射任意的方法。
例子:
Method metaGetDeclaredMethod = Class.class.getDeclaredMethod("getDeclardMethod"); // 公开API,无问题Method hiddenMethod = metaGetDeclaredMethod.invoke(hiddenClass, "hiddenMethod", "hiddenMethod参数列表"); // 系统类通过反射使用隐藏 API,检查直接通过。hiddenMethod.invoke // 正确找到 Method 直接反射调用
2、源码分析
而目前的Android11和Android12系统修复了这个漏洞,套娃无法在继续使用。
所以我们只能另辟蹊径。
系统在判断调用者的时候是通过调用栈来判断调用者的方向的,所以只要我们在调用栈上做手脚,让系统误以为不是应用层的调用栈,即可绕过@hide限制。
static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jstring name, jobjectArray args) { // …… Handle result = hs.NewHandle( mirror::Class::GetDeclaredMethodInternal( soa.Self(), klass, soa.Decode(name), soa.Decode>(args), GetHiddenapiAccessContextFunction(soa.Self()))); if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } return soa.AddLocalReference(result.Get());}
如果ShouldDenyAccessToMember返回true,则getDeclaredMethodInternal会返回null,则上层会抛出异常。
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) { ArtMethod *m = GetMethod(); ...... ObjPtr declaring_class = m->GetDeclaringClass(); if (declaring_class->IsBootStrapClassLoaded()) { ...... // 如果 PREVENT_META_REFLECTION_BLACKLIST_ACCESS 为 Enabled,跳过来自 java.lang.reflect.* 的访问 // 系统对“套娃反射”的限制的关键就在此 ObjPtr proxy_class = GetClassRoot(); if (declaring_class->IsInSamePackage(proxy_class) && declaring_class != proxy_class) { if (Runtime::Current()->isChangeEnabled(kPreventMetaReflectionBlacklistAccess)) { return true; } } } caller = m; return false;}
套娃为什么会失效,原因就在VisitFrame中
3、重点来了--解决方案(适配Anroid11&Android12)
我们解决方法的方向就是破坏调用堆栈,让系统无法识别api的真正调用者。
具体做法:
通过在jni层创建线程来执行真正的反射操作,当然,只是这样还不足以欺骗系统。
通过对线程调用attachthread方法来改变调用堆栈,从而达到欺骗系统的目的。google对attachthread的部分讲解:https://developer.android.com/training/articles/perf-jni?hl=zh-cn
4、源码
话不多说,直接上源码:
1、通过async来创建线程,因为async可以返回future来把异步同步化,线程内执行getDeclaredField_internal
Java_com_macoli_reflect_1helper_NativeReflect_getDeclaredField(JNIEnv *env, jobject t, jclass clz, jstring fieldName) { auto global_clazz = env->NewGlobalRef(clz); jstring global_method_name = static_cast(env->NewGlobalRef(fieldName)) ; //通过async来创建线程,因为async可以返回future来把异步同步化,线程内执行getDeclaredField_internal auto future = std::async(&getDeclaredField_internal, global_clazz, global_method_name); auto result = future.get(); env->DeleteGlobalRef(global_clazz) ; env->DeleteGlobalRef(global_method_name) ; return result ;}
2、真正执行反射操作
关键:attachCurrentThread()来对调用堆栈进行转变。
JNIEnv *attachCurrentThread() { JNIEnv *env; int res = _vm->AttachCurrentThread(&env, nullptr); __android_log_print(ANDROID_LOG_DEBUG, "native", "Found attached %d", res); return env;}void detachCurrentThread() { _vm->DetachCurrentThread();}static jobject getDeclaredField_internal(jobject object, jstring field_name) { JNIEnv *env = attachCurrentThread();//这里是重点 jclass clazz_class = env->GetObjectClass(object); jmethodID methodId = env->GetMethodID(clazz_class, "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;"); jobject res = env->CallObjectMethod(object, methodId, field_name); jobject global_res = nullptr; if (res != nullptr) { global_res = env->NewGlobalRef(res); } detachCurrentThread(); return global_res;}
测试:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.reflect_btn).setOnClickListener(v -> { try { Method m = ReflectHelper.getDeclaredMethod(Looper.class , "setTraceTag" , new Class[]{long.class}) ; m.invoke(Looper.getMainLooper() , 1000) ; Log.d("gggl" , "setTraceTag " + m.toString()) ; Class> activityThreadCls = Class.forName("android.app.ActivityThread") ; Field f = ReflectHelper.getDeclaredField(activityThreadCls , "DEBUG_BROADCAST") ; Object tag = f.get(null) ; Log.d("gggl" , f.toString()) ; Log.d("gggl" , tag.toString()) ;// CpuUsageInfo cpuUsageInfo = new CpuUsageInfo() ; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { Constructor cpuUsageInfoConstructor = ReflectHelper.getDeclaredConstructor(CpuUsageInfo.class , new Class[]{long.class , long.class}) ; Object cpuUsageObj = cpuUsageInfoConstructor.newInstance(100 , 100) ; Method getTotalM = CpuUsageInfo.class.getDeclaredMethod("getTotal") ; Object r = getTotalM.invoke(cpuUsageObj) ; Log.d("gggl" , "getTotal = " + r) ; } } catch (Exception e) { e.printStackTrace(); } }); }
华为mate 40 pro 测试结果:
总结:
源码已上传到gitee:https://gitee.com/gggl/reflect_helper
Android11&Android12