Android 绕过反射黑名单的方法
限制原理
Google 从 Android P 开始引入了针对非公开 API 的限制,这一点可以从 Native 相关的源码中找到限制的原理,从而从中找到解决办法,不过非必要原因不太建议去挑战这种限制,毕竟不清楚在后续的版本中会不会做限制,维护起来挺麻烦的。
在 Native 层有几个访问级别:
class HiddenApiAccessFlags { public: enum ApiList { kWhitelist = 0, kLightGreylist, kDarkGreylist, kBlacklist, }; }
另外还有几个对应的响应级别:
enum Action { kAllow, //通过 kAllowButWarn, //通过,但日志警告 kAllowButWarnAndToast, //通过,且日志警告和弹窗 kDeny //拒绝访问 };
这里介绍一下网上的一些解决方式,此外,还可以把我们调用了反射方法的类的类加载器设置为系统类加载器,这样就可以绕过 Native 层的限制了。
系统类伪装
黑名单在系统中有一个 fn_caller_is_trusted 的条件:如果调用者是系统类,那么就允许被调用。即如果我们能以系统类的身份去反射,那么就能畅通无阻:
- 首先通过反射 API 拿到 getDeclaredMethod 方法。getDeclaredMethod 是 public 的,不存在问题;这个通过反射拿到的方法网上称之为元反射方法。
- 然后通过刚刚的元反射方法去反射调用 getDeclardMethod。这里我们就实现了以系统身份去反射的目的——反射相关的 API 都是系统类,因此我们的元反射方法也是被系统类加载的方法;所以我们的元反射方法调用的 getDeclardMethod 会被认为是系统调用的,可以反射任意的方法。
伪代码如下:
// 公开API,无问题 Method metaGetDeclaredMethod = Class.class.getDeclaredMethod("getDeclardMethod"); // 系统类通过反射使用隐藏 API,检查直接通过。 Method hiddenMethod = metaGetDeclaredMethod.invoke(hiddenClass, "hiddenMethod", "hiddenMethod参数列表"); // 正确找到 Method 直接反射调用 hiddenMethod.invoke
豁免条件
隐藏 API 的调用有「豁免」条件,即只要它是豁免的,则即使它在黑名单中,也会被放行。这种方式暴露给了 Java 层,因此可以通过 VMRuntime.setHiddenApiExemptions 方法来实现。再结合上面这个方法,我们只需要通过 「元反射」 来反射调用 VMRuntime.setHiddenApiExemptions 就能将我们自己要使用的隐藏 API 全部都豁免掉了。另外系统在检查豁免时是通过方法签名进行前缀匹配的,而 Java 方法签名都是 L 开头的,因此我们可以把直接传个 L 进去,那么所有的隐藏API全部被赦免了!
源码直接参考网上大佬的开源项目: 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"}); } }
以上就是Android 绕过反射黑名单的方法的详细内容,更多关于Android 绕过反射黑名单的资料请关注我们其它相关文章!
赞 (0)