如何绕过Google针对非 SDK 接口的反射限制

前言

从 Android 9(API 级别 28)开始,Android 平台对应用能使用的非 SDK 接口实施了限制。只要应用引用非 SDK 接口或尝试使用反射或 JNI 来获取其句柄,这些限制就适用。

这些限制旨在帮助提升用户体验和开发者体验,为用户降低应用发生崩溃的风险,同时为开发者降低紧急发布的风险。

但!对开发者来说不一定是一件好事。

谷歌通过维护几个名单来进行对非SDK接口的限制

解决方案

既然谷歌通过维护这样几个名单来限制sdk的使用,那么我们只要通过修改名单就可以实现解除对应限制。

参考谷歌源代码中的测试项目

在Android 10谷歌的测试代码中

https://android.googlesource.com/platform/art/+/refs/heads/android10-release/test/674-hiddenapi/src-art/Main.java#120

最终都是通过调用

VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"}); 

Set exemptions to "L" (matches all classes) if we are testing whitelisting.← 谷歌注释

Method forName, getDeclaredMethod, getMethod, getDeclaredField, getField, getDeclaredConstructor, getConstructor, newInstance;

    private void allowHideMethod() {
        try {
            forName = Class.class.getDeclaredMethod("forName", String.class);
            // invoke = Method.class.getMethod("invoke", Object.class, Object[].class);
            // 反射获取方法
            getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
            getMethod = Class.class.getDeclaredMethod("getMethod", String.class, Class[].class);

            // 反射获取变量
            getDeclaredField = Class.class.getDeclaredMethod("getDeclaredField", String.class);
            getField = Class.class.getDeclaredMethod("getField", String.class);

            // 反射实例化代码
            getDeclaredConstructor = Class.class.getDeclaredMethod("getDeclaredConstructor", Class[].class);
            getConstructor = Class.class.getDeclaredMethod("getConstructor", Class[].class);
            newInstance = Constructor.class.getDeclaredMethod("newInstance", Object[].class);
        } catch (Throwable igone) {
        }

        if (Build.VERSION.SDK_INT > 27) {
            /*
             * 设置豁免所有hide api
             * http://androidxref.com/9.0.0_r3/xref/art/test/674-hiddenapi/src-art/Main.java#100
             * VMRuntime.getRuntime().setHiddenApiExemptions(new String[]{"L"});
             */
            try {
                Class<?> vmRuntimeClass = (Class<?>) forName.invoke(null, "dalvik.system.VMRuntime");
                Method getRuntime = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null);
                Method setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", new Class[]{String[].class});
                Object sVmRuntime = getRuntime.invoke(null);
                setHiddenApiExemptions.invoke(sVmRuntime, new Object[]{new String[]{"L"}});

            } catch (Throwable igone) {
                igone.printStackTrace();
            }
        }
    }
allowHideMethod();
try {

    Class clz = mActivityCustomErrorBinding.etError.getClass();
    while (clz != TextView.class) {
        clz = clz.getSuperclass();
    }
    Field field_mEditor = (Field) getDeclaredField.invoke(clz, "mEditor");
    field_mEditor.setAccessible(true);

    Object mEditor = field_mEditor.get(mActivityCustomErrorBinding.etError);
    Class clz_Editor = Class.forName("android.widget.Editor");
    Method showError = (Method) getDeclaredMethod.invoke(clz_Editor, "showError", null);
    Method hideError = (Method) getDeclaredMethod.invoke(clz_Editor, "hideError", null);
    Log.e(TAG, mEditor.getClass().getName());

    Field field = (Field) getDeclaredField.invoke(clz_Editor, "mErrorPopup");
    field.setAccessible(true);

} catch (ClassNotFoundException | IllegalAccessException | InvocationTargetException e) {
    e.printStackTrace();
}
  

在需要进行反射的地方前面调用disableHideApi()。使用完毕后调用 setHiddenApiExemptions.invoke(sVmRuntime,newObject[]{newString[0]});重置列表即可。

实际上! 通过反射获取的getDeclaredMethod等反射方法进行反射工作,也可以直接绕过对非SDK接口的限制(上面例子其实不用调用disableHideApi()也可以通过)

例如Method setHiddenApiExemptions = (Method)getDeclaredMethod.invoke(vmRuntimeClass,"setHiddenApiExemptions",newClass[]{String[].class});

上面例子中的setHiddenApiExemptions 本来就是属于限制使用的非SDK接口。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值