Android Method for Bypassing Reflection Blacklist

  • 2021-12-11 18:46:40
  • OfStack

Limitation principle

Google has introduced restrictions on non-public API from Android P. This point can find the principle of restrictions from the related source code of Native, so as to find solutions. However, it is not recommended to challenge this restriction for unnecessary reasons. After all, it is not clear whether restrictions will be made in subsequent versions, which is quite troublesome to maintain.

There are several access levels at the Native layer:


class HiddenApiAccessFlags {
  public:
  enum ApiList {
    kWhitelist = 0,
    kLightGreylist,
    kDarkGreylist,
    kBlacklist,
  };
}

There are also several corresponding response levels:


enum Action {
  kAllow,     // Pass 
  kAllowButWarn, // Passed, but log warning 
  kAllowButWarnAndToast, // Pass, and log warnings and pop-ups 
  kDeny  // Access denied 
};

Here, we introduce some solutions on the Internet. In addition, we can set the class loader of the class that we called the reflection method as the system class loader, so that we can bypass the limitation of Native layer.

System class camouflage

The blacklist has one fn_caller_is_trusted condition in the system: if the caller is a system class, it is allowed to be called. That is, if we can reflect as a system class, then we can be unimpeded:

Firstly, the getDeclaredMethod method is obtained by reflecting API. getDeclaredMethod is public and there is no problem; This method obtained by reflection is called meta-reflection method on the Internet. Then call getDeclardMethod by reflecting the meta-reflection method just now. Here, we realize the purpose of reflecting as a system-API related to reflection is a system class, so our meta-reflection method is also a method loaded by the system class; Therefore, getDeclardMethod called by our meta-reflection method will be considered as a system call and can reflect any method.

The pseudo code is as follows:


//  Open API , no problem 
Method metaGetDeclaredMethod = Class.class.getDeclaredMethod("getDeclardMethod");
//  System classes use hiding through reflection  API , check straight through. 
Method hiddenMethod = metaGetDeclaredMethod.invoke(hiddenClass, "hiddenMethod", "hiddenMethod Parameter list ");
//  Find it correctly  Method  Direct reflection call 
hiddenMethod.invoke

Exemption conditions

The call to hide API has an "exemption" condition, that is, as long as it is exempted, it will be released even if it is on the blacklist. This approach is exposed to the Java layer, so it can be implemented by the VMRuntime. setHiddenApiExemptions method. Combined with the above method, we only need to reflect and call VMRuntime. setHiddenApiExemptions through "meta-reflection" to save all the hidden API we want to use. In addition, when the system checks the exemption, it matches the prefix through the method signature, and the Java method signature starts with L, so we can send an L directly, and then all the hidden API are pardoned!

The source code directly refers to the open source project of online bosses: FreeReflection.


public final class BootstrapClass {

  private static final String TAG = "BootstrapClass";

  private static Object sVmRuntime;
  private static Method setHiddenApiExemptions;

  static {
    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.w(TAG, "reflect bootstrap failed:", e);
      }
    }
  }

  /**
   * make the method exempted from hidden API check.
   *
   * @param method the method signature prefix.
   * @return true if success.
   */
  public static boolean exempt(String method) {
    return exempt(new String[]{method});
  }

  /**
   * make specific methods exempted from hidden API check.
   *
   * @param methods the method signature prefix, such as "Ldalvik/system", "Landroid" or even "L"
   * @return true if success
   */
  public static boolean exempt(String... methods) {
    if (sVmRuntime == null || setHiddenApiExemptions == null) {
      return false;
    }

    try {
      setHiddenApiExemptions.invoke(sVmRuntime, new Object[]{methods});
      return true;
    } catch (Throwable e) {
      return false;
    }
  }

  /**
   * Make all hidden API exempted.
   *
   * @return true if success.
   */
  public static boolean exemptAll() {
    return exempt(new String[]{"L"});
  }
}

The above is Android to bypass the reflection blacklist method of the details, more information about Android to bypass the reflection blacklist please pay attention to other related articles on this site!


Related articles: