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 直接反射调用
代表:FreeReflection
2、源码分析
而目前的Android11和Android12系统修复了这个漏洞,套娃无法在继续使用。
所以我们只能另辟蹊径。
系统在判断调用者的时候是通过调用栈来判断调用者的方向的,所以只要我们在调用栈上做手脚,让系统误以为不是应用层的调用栈,即可绕过@hide限制。
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,则getDeclaredMethodInternal会返回null,则上层会抛出异常。
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,跳过来自 java.lang.reflect.* 的访问
// 系统对“套娃反射”的限制的关键就在此
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;
}
套娃为什么会失效,原因就在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<jstring>(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 {
/** {@hide} */
/*
public void setTraceTag(long traceTag) {
mTraceTag = traceTag;
}
* */
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() ;
/** @hide */
/*public CpuUsageInfo(long activeTime, long totalTime) {
mActive = activeTime;
mTotal = totalTime;
}*/
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
反射Android @hide api 适配文章来源:https://www.toymoban.com/news/detail-507349.html
Android11&Android12文章来源地址https://www.toymoban.com/news/detail-507349.html
到了这里,关于Android反射@hide API 方法、变量,支持Android11和Android12的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!