current position:Home>Android 11 bypasses reflection restrictions

Android 11 bypasses reflection restrictions

2022-01-28 15:07:18 Red orange Darren

1. If the font used in the document is not installed on the system

Tencent video is integrating us replay sdk When I found such a mistake , Lead to the whole db mock Complete failure of function .

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)
 Copy code 

I clearly remember that we introduced a third-party solution , stay 9.0 The above has solved this problem , The general plan is like this :

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);
  }
}
 Copy code 

I was so scared that I hurried to see if there was anything fishy , Found in Android 11 There is a problem in the :

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;]
......
 Copy code 

2. Analyze the cause of the problem

In view of tight time and heavy tasks, try not to affect the progress , I still want to search the Internet , But it's all a bunch of old schemes . I have to go and see why ? Why on earth ? Just a few days ago, I asked my colleagues for a job Android 11 Source code .

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());
}
 Copy code 

If ShouldDenyAccessToMember return true, So it's going to return null, The upper layer will throw an exception that the method cannot find . Here and Android P No different , Just put ShouldBlockAccessToMember Just changed its name . ShouldDenyAccessToMember Will be called to hiddenapi::ShouldDenyAccessToMember, The function is implemented in this way :

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: If the member is public API, Directly through 
  if ((runtime_flags & kAccPublicApi) != 0) {
    return false;
  }

  // 2: Not publicly API( That is to hide API), Get the name of the caller and the visited member  Domain 
  //  Mainly look at this 
  const AccessContext caller_context = fn_get_access_context();
  const AccessContext callee_context(member->GetDeclaringClass());

  // 3: If the caller is trusted , Go straight back to 
  if (caller_context.CanAlwaysAccess(callee_context)) {
    return false;
  }
  // ......
  }
 Copy code 

The original scheme failed and can be used in FirstExternalCallerVisitor Of VisitFrame Find the answer in the method

bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
    ArtMethod *m = GetMethod();
    ......
    ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClass();
    if (declaring_class->IsBootStrapClassLoaded()) {
        ......
        //  If  PREVENT_META_REFLECTION_BLACKLIST_ACCESS  by  Enabled, Skip from  java.lang.reflect.*  The interview of 
        //  System pair “ Dolls reflect ” The key to the limitation of is here 
        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;
}
 Copy code 

3. Solution

  • native hook live ShouldDenyAccessToMember Method , Go straight back to false
  • Break the call stack and go around , send VM Unrecognized caller

We adopt the second scheme , Is there any way to make VM I can't recognize my call stack ? This can be done by JniEnv::AttachCurrentThread(…) Function to create a new Thread To complete . Specifically, we can see here developer.android.com/training/ar… , And then with std::async(…) And std::async::get(..) It's done , Here's the key code :

// java  The layer is directly used  jni  Call this method 
static jobject Java_getDeclaredMethod(
    JNIEnv *env,
    jclass interface,
    jobject clazz,
    jstring method_name,
    jobjectArray params) {
  // ......  Save some conversion code 
  //   First use  std::async  call  getDeclaredMethod_internal  Method 
  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) {
  //  Here are some common  jni  Operation 
  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  The core is here 
  int res = _vm->AttachCurrentThread(&env, nullptr);
  return env;
}
 Copy code 

copyright notice
author[Red orange Darren],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201281507161731.html

Random recommended