[Android翻译]所有Android版本的Android API限制绕过 - AndroidReverse

832 阅读3分钟

本文由 简悦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)。

正如在 FirstExternalCallerVisitorandroid.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限制绕过方法整合到一个容易使用的库中。

Github和更多

github.com/ChickenHook…

视频描述

www.youtube.com/watch?v=91a…