Android 11 绕过反射限制

2023-11-06

1. 问题出现的背景

腾讯视频在集成我们 replay sdk 的时候发现这么个错误,导致整个 db mock 功能完全失效。

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)


我清晰的记得我们引入了一个第三方解决方案,在 9.0 以上已经解决了这个问题,大致的方案是这样的:
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);
  }
}

吓得我赶紧去看下到底有没有猫腻,发现在 Android 11 上果然有问题:

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

2. 分析问题出现的原因

本着时间紧任务重尽量不影响进度的情况下,我还是想去网上搜索看看,但是发现都是一堆旧的方案。迫不得已去看看到底为什么?到底为什么?刚好前几天找同事要了一份 Android 11 的源码。

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

如果 ShouldDenyAccessToMember 返回 true,那么就会返回 null,上层就会抛出方法找不到的异常。这里和 Android P 没什么不同,只是把 ShouldBlockAccessToMember 改了个名而已。
ShouldDenyAccessToMember 会调用到 hiddenapi::ShouldDenyAccessToMember,该函数是这样实现的:

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:如果该成员是公开API,直接通过
  if ((runtime_flags & kAccPublicApi) != 0) {
    return false;
  }


  // 2:不是公开API(即为隐藏API),获取调用者和被访问成员的 Domain 
  // 主要看这个
  const AccessContext caller_context = fn_get_access_context();
  const AccessContext callee_context(member->GetDeclaringClass());


  // 3:如果调用者是可信的,直接返回
  if (caller_context.CanAlwaysAccess(callee_context)) {
    return false;
  }
  // ......
  }

原来的方案失效了能在 FirstExternalCallerVisitor 的 VisitFrame 方法中找到答案

bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
    ArtMethod *m = GetMethod();
    ......
    ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClass();
    if (declaring_class->IsBootStrapClassLoaded()) {
        ......
        // 如果 PREVENT_META_REFLECTION_BLACKLIST_ACCESS 为 Enabled,跳过来自 java.lang.reflect.* 的访问
        // 系统对“套娃反射”的限制的关键就在此
        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;
}

3. 解决方案

  • native hook 住 ShouldDenyAccessToMember 方法,直接返回 false

  • 破坏调用堆栈绕过去,使 VM 无法识别调用方

我们采用的是第二种方案,有什么方法可以让 VM 无法识别我的调用栈呢?这可以通过 JniEnv::AttachCurrentThread(…) 函数创建一个新的 Thread 来完成。具体我们可以看下这里 https://developer.android.com/training/articles/perf-jni ,然后配合 std::async(…)  与 std::async::get(..)  就能搞定了,下面是关键代码:

// java 层直接用 jni 调用这个方法
static jobject Java_getDeclaredMethod(
    JNIEnv *env,
    jclass interface,
    jobject clazz,
    jstring method_name,
    jobjectArray params) {
  // ...... 省掉一些转换代码
  //  先用 std::async 调用 getDeclaredMethod_internal 方法
  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) {
  // 这里就是一些普通的 jni 操作了
  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 核心在这里
  int res = _vm->AttachCurrentThread(&env, nullptr);
  return env;
}


作者:红橙Darren
链接:https://www.jianshu.com/p/6546ce67c8e0

关注我获取更多知识或者投稿

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 11 绕过反射限制 的相关文章

随机推荐

  • 如何使用 wget 下载一个目录下的所有文件

    今天想要下载编译原理的 虎书 上的资料 使用wget但只是下载了一个index html 如下 于是我就参考资料 写此博客以记录 方法如下 wget r np nH R index html http url including files
  • npm和cnpm下载安装及VUE的创建

    npm和cnpm下载安装及VUE的创建 1 node js下载 node js官网 http nodejs cn download 下载安装后cmd输入以下命令查看版本 2 配置npm 打开node js的安装目录 我这里是D nodejs
  • 毕设-仓库管理的设计与实现

    package com ken wms common controller import com ken wms common service Interface CustomerManageService import com ken w
  • 用js来实现自定义弹框

    前言 个人作业上传 大家可参考但不可转载 实现将弹框的样式统一封装在一个对象中方便后续的修改
  • 开关电源环路稳定性分析(05)-传递函数

    大家好 这里是大话硬件 经过前面4篇文章的梳理 估计很多人已经等不及了 什么时候可以开始环路的分析 为了尽快进入到大家关心的部分 这一讲我们正式进入环路分析的第一部分 传递函数 传递函数 简单的理解就是输入和输出之间的关系 为了方便我们仅仅
  • 【linux】linux 基础正则表达式、字符串截取、比较、分支、while循环

    1 概述 正则表达式用来在文件中匹配符合条件的字符串 正则是包含匹配 grep awk sed等命令可以支持正则表达式 通配符用来匹配符合条件的文件名 通配符是完全匹配 ls find cp这些命令不支持正则表达式 所以只能使用shell自
  • IDEA中设置vue vue3+ts项目的@跳转

    网上基本都是这个方法但是试了对我不适用 idea vue项目通过 跳转 vue设置完 映射路径之后在IDEA中无法跳转 兜兜转转原来只需 在tsconfig json中加入如下配置就行 compilerOptions baseUrl pat
  • leetcode 5749. 邻位交换的最小次数

    邻位交换的最小次数 给你一个表示大整数的字符串 num 和一个整数 k 如果某个整数是 num 中各位数字的一个 排列 且它的 值大于 num 则称这个整数为 妙数 可能存在很多妙数 但是只需要关注 值最小 的那些 例如 num 54893
  • 使用stylecop 规范C#编码

    可直接在VS操作完成 简单易懂 第一步 打开VS 第二步 安装软件 第三步 规则修改 第四步 规则生效 stylecop 是代码静态检查分析的一大利器 可以自定义检查规则 安装操作使用方便 相信很多写C 的朋友都会使用的到 下面详细介绍安装
  • XP下采用DirectShow采集摄像头

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家提出意见 一起讨论 需要示例源码的请独自联系我 前提 摄像头能正常工作 摄像头有创建directshow filter 即 大家可以对比我的另一
  • 数据规约

    主成分的计算步骤 主成分的代码实现 设置工作空间 把 数据及程序 文件夹拷贝到F盘下 再用setwd设置工作空间 setwd F 数据及程序 chapter4 示例程序 数据读取 inputfile lt read csv data pri
  • z系列主板能装服务器系统吗,Intel Z390主板搭配8代酷睿现身:还能安装WIN7系统吗?...

    Intel今年为发烧友带来了最多18核心的Core X系列 搭配X299顶级主板 主流领域则有最多6核心的八代酷睿Coffee Lake S 搭配Z370主板 但坑爹的是 尽管八代和六七代酷睿都是LGA1151接口 但却被故意整成不兼容 因
  • 基于power bi上手业务数据可视化

    分析背景 偶然得到一份关于某连锁火锅品牌在2020年1月 8月的线上平台业务数据 如下图 心想正好利用这份数据 模拟实际业务中基于数据库与bi工具 实践开发可视化图表 一开始考虑用tableau 因为在大学跟刚工作的时候曾系统学习使用过 但
  • 栈的链性表的c语言实现方式 linkstack.h 和 linkstack.c

    linkstack h 文件 ifndef LINK STACK H define LINK STACK H include
  • 数据库 关系代数 投影概念理解

    关系R上的投影是从R 中选择出若干属性列组成新的关系 记作 A R t A t R 其中A 为R 中的属性列 投影操作是从列的角度进行的运算 例3 查询学生的姓名和所在系 即求Student关系在学生姓名和所在系两个属性上的投影 Sname
  • k8s集群新增节点

    如何动态的为k8s集群增加worknode节点 本文将详细介绍 kubeadm搭建k8集群详见 https blog csdn net wangqiubo2010 article details 101203625 一 VMWare xSp
  • 每日算法题(Day5)----取石子

    题目描述 有一种有趣的游戏 玩法如下 玩家 2 人 道具 N 颗石子 规则 游戏双方轮流取石子 每人每次取走若干颗石子 最少取 1 颗 最多取 K 颗 石子取光 则游戏结束 最后取石子的一方为胜 假如参与游戏的玩家都非常聪明 问最后谁会获胜
  • Linux Kafka 2.11-1.1.1 安装搭建

    Kafka是最初由Linkedin公司开发 是一个分布式 支持分区的 partition 多副本的 replica 基于zookeeper协调的分布式消息系统 它的最大的特性就是可以实时的处理大量数据以满足各种需求场景 比如基于hadoop
  • iframe无边框实现

  • Android 11 绕过反射限制

    1 问题出现的背景 腾讯视频在集成我们 replay sdk 的时候发现这么个错误 导致整个 db mock 功能完全失效 Accessing hidden field Landroid database sqlite SQLiteCurs