本文由 简悦SimpRead 转码,原文地址 androidreverse.org
由于API级别28,Android禁止访问一些隐藏的API函数(见:developer.androi......
绕过反射限制
自API等级28以来,Android禁止访问一些隐藏的API函数(见: developer.android.com/distribute/… )。POC使用的很多必要的函数都被列入了黑名单,当试图通过反射API访问它们时,会出现异常。
绕过API 30之前
随着API等级30的到来,这个功能被Android团队加强了,所以使用Reflection API调用Reflection API函数来访问被禁止的函数和字段的工作方法不再有效。但是在API 30之前,我们仍然可以使用这个解决方案。
自API 30以来的绕过方法(更新:这也适用于API 31, 32, 33)。
正如在 FirstExternalCallerVisitor(android.googlesource.com/platform/ar…中看到的那样,反射调用的调用者是通过走过调用栈来识别的,当发现不符合特定过滤器(例如包过滤器)的函数时就停止。) 如果访问者在应用程序代码的某个函数上停下来,虚拟机就会认为反射调用是由应用程序而不是由系统完成的。虚拟机将检查被调用者是否是一个受限的API函数,并拒绝访问。
绕过这个解决方案的一个可能的方法是打破调用堆栈,使虚拟机无法识别调用者。这可以通过一个通过JniEnv::AttachCurrentThread(...) 函数精心制作的新线程来实现(见:developer.android.com/training/ar…)。清单2由Java代码调用,并将所有必要的java对象转换为全局引用,以便能够在其他JNI环境中使用它们。之后,它通过调用std::async(...) 函数创建并启动一个新线程,并通过调用 std::async::get(...) 函数等待结果。同时, getDeclaredMethod_internal() 函数(清单1)使用_JavaVM::attackCurrentThread()函数将新制作的本地线程附加到虚拟机中。在这一点上,我们在一个新的虚拟机呼叫栈内,没有任何Java调用的历史。下一步是检索getDeclaredMethod()函数,并用给定的参数调用它。最后,我们必须将结果转换为全局引用。至此,我们的Exploit就完成了。
static jobject getDeclaredMethod_internal(
jobject clazz,
jstring method_name,
jobjectArray params) {
JNIEnv *env = attachCurrentThread();
printClassName(clazz, env);
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);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
jobject global_res = nullptr;
if (res != nullptr) {
global_res = env->NewGlobalRef(res);
}
detachCurrentThread();
return global_res;
}
清单1:getDeclaredMethod_internal(...)在一个新的本地线程中被调用,并通过调用attachCurrentThread()附着到虚拟机上。getDeclaredMethod调用发生在一个新的调用堆栈中,因此虚拟机无法识别调用是由App代码进行的(见实现:gleipnir/src/main/cpp/native-lib.cpp:50)。
static jobject Java_getDeclaredMethod(
JNIEnv *env,
jclass interface,
jobject clazz,
jstring method_name,
jobjectArray params) {
__android_log_print(ANDROID_LOG_DEBUG, "native", "Create globals");
auto global_clazz = env->NewGlobalRef(clazz);
jstring global_method_name = (jstring) env->NewGlobalRef(method_name);
int arg_length = env->GetArrayLength(params);
jobjectArray global_params = nullptr;
if (params != nullptr) {
for (int i = 0; i < arg_length; i++) {
jobject element = (jobject) env->GetObjectArrayElement(params, i);
jobject global_element = env->NewGlobalRef(element);
env->SetObjectArrayElement(params, i, global_element);
__android_log_print(ANDROID_LOG_DEBUG, "native", "Element %p", global_element);
}
global_params = (jobjectArray) env->NewGlobalRef(params);
}
__android_log_print(ANDROID_LOG_DEBUG, "native", "Start async");
auto future = std::async(&getDeclaredMethod_internal, global_clazz,
global_method_name,
global_params);
auto result = future.get();
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
return result;
}
清单2:Java_getDeclaredMethod将被App的java代码调用,并创建所有JNI对象的全局引用,以便将它们运送到另一个线程。std::async将返回一个未来,我们用于阻止和收集getDeclaredMethod_internal调用的结果。
API限制 BYPASS库
为了尽可能方便地提供这个解决方案,我创建了一个开源库,将所有的api限制绕过方法整合到一个容易使用的库中。