Android9,10反射限制问题分析以及解决

news/2024/5/18 9:34:05 标签: android, 反射, java

Android9,10反射限制问题分析


前一段时间在写反射的时候发现Android 9 10发现明明存在的Method却无法获取了。
于是分析了一下。
实际上反射Method最终调用的是native方法

    // private native Method getDeclaredMethodInternal(String name, Class<?>[] args);

于是去网上翻了9.0 10的源码看究竟是如何实现的
10的源码为位置: java_lang_Class.cc.

     static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis,
                                                   jstring name, jobjectArray args) {
      ScopedFastNativeObjectAccess soa(env);
      StackHandleScope<1> hs(soa.Self());
      DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
      DCHECK(!Runtime::Current()->IsActiveTransaction());
      Handle<mirror::Method> result = hs.NewHandle(
          mirror::Class::GetDeclaredMethodInternal<kRuntimePointerSize, false>(
              soa.Self(),
              DecodeClass(soa, javaThis),
             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

ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
        REQUIRES_SHARED(Locks::mutator_lock_) {
      return hiddenapi::ShouldDenyAccessToMember(member,
                                                 GetHiddenapiAccessContextFunction(self),
                                                 hiddenapi::AccessMethod::kReflection);
     }

而ShouldDenyAccessToMember是调用了hiddenapi中的找到这个文件

源码位置 hidden_api.h.

  template<typename T>
     inline bool ShouldDenyAccessToMember(T* member,
                                         const std::function<AccessContext()>& fn_get_access_context,
                                         AccessMethod access_method)
        REQUIRES_SHARED(Locks::mutator_lock_) {
      DCHECK(member != nullptr);
       // Get the runtime flags encoded in member's access flags.
      // Note: this works for proxy methods because they inherit access flags from their
      // respective interface methods.
      const uint32_t runtime_flags = GetRuntimeFlags(member);
      // Exit early if member is public API. This flag is also set for non-boot class
      // path fields/methods.
      if ((runtime_flags & kAccPublicApi) != 0) {
        return false;
      }
      // Determine which domain the caller and callee belong to.
      // This can be *very* expensive. This is why ShouldDenyAccessToMember
      // should not be called on every individual access.
      const AccessContext caller_context = fn_get_access_context();
      const AccessContext callee_context(member->GetDeclaringClass());
      // Non-boot classpath callers should have exited early.
      DCHECK(!callee_context.IsApplicationDomain());
      // Check if the caller is always allowed to access members in the callee context.
      if (caller_context.CanAlwaysAccess(callee_context)) {
        return false;
      }
      // Check if this is platform accessing core platform. We may warn if `member` is
      // not part of core platform API.
      switch (caller_context.GetDomain()) {
        case Domain::kApplication: {
          DCHECK(!callee_context.IsApplicationDomain());
          // Exit early if access checks are completely disabled.
          //这句从Runtime中获取API策略
          EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
          if (policy == EnforcementPolicy::kDisabled) {
            return false;
          }
          // If this is a proxy method, look at the interface method instead.
          member = detail::GetInterfaceMemberIfProxy(member);
          // Decode hidden API access flags from the dex file.
          // This is an O(N) operation scaling with the number of fields/methods
          // in the class. Only do this on slow path and only do it once.
          ApiList api_list(detail::GetDexFlags(member));
          DCHECK(api_list.IsValid());
          // Member is hidden and caller is not exempted. Enter slow path.
          return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
        }
        case Domain::kPlatform: {
          DCHECK(callee_context.GetDomain() == Domain::kCorePlatform);
          // Member is part of core platform API. Accessing it is allowed.
          if ((runtime_flags & kAccCorePlatformApi) != 0) {
            return false;
          }
          // Allow access if access checks are disabled.
          EnforcementPolicy policy = Runtime::Current()->GetCorePlatformApiEnforcementPolicy();
          if (policy == EnforcementPolicy::kDisabled) {
            return false;
          }
          // If this is a proxy method, look at the interface method instead.
          member = detail::GetInterfaceMemberIfProxy(member);
          // Access checks are not disabled, report the violation.
          // This may also add kAccCorePlatformApi to the access flags of `member`
          // so as to not warn again on next access.
          return detail::HandleCorePlatformApiViolation(member,
                                                        caller_context,
                                                        access_method,
                                                        policy);
        }
        case Domain::kCorePlatform: {
          LOG(FATAL) << "CorePlatform domain should be allowed to access all domains";
          UNREACHABLE();
        }
      }
     }

这是一个模版函数,经过分析发现他会调用 Runtime::Current()->GetCorePlatformApiEnforcementPolicy();去获取策略
于是找到Runtime
Runtime源码位置:Runtime.
发现这个方法

      hiddenapi::EnforcementPolicy GetCorePlatformApiEnforcementPolicy() const {
        return core_platform_api_policy_;
      }
      //定义
        // Whether access checks on core platform API should be performed.
      hiddenapi::EnforcementPolicy core_platform_api_policy_;

同时发现一个方法可以设置core_platform_api_policy_

       void SetCorePlatformApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) {
        core_platform_api_policy_ = policy;
      }
      

再回看: hidden_api.

      //枚举类
     enum class EnforcementPolicy {
      kDisabled             = 0,
      kJustWarn             = 1,  // keep checks enabled, but allow everything (enables logging)
      kEnabled              = 2,  // ban dark grey & blacklist
      kMax = kEnabled,
     };
     

通过上面的分析 上面得出结论,可以尝试通过JNI找到这个类的实例,通过仿写一个结构体,然后指针替换赋值一下。
于是准备开始找这个相关联的实例。
Runtime实例链接: VMRuntime

  private static final VMRuntime THE_ONE = new VMRuntime();
      @UnsupportedAppUsage
        @libcore.api.CorePlatformApi
        public static VMRuntime getRuntime() {
            return THE_ONE;
        }
     dalvik.system.VMRuntime

找寻的过程中发现一个有意思的方法好像可以直接设置隐藏API豁免

      /**
         * Sets the list of exemptions from hidden API access enforcement.
         *
         * @param signaturePrefixes
         *         A list of signature prefixes. Each item in the list is a prefix match on the type
         *         signature of a blacklisted API. All matching APIs are treated as if they were on
         *         the whitelist: access permitted, and no logging..
         */
        @libcore.api.CorePlatformApi
        public native void setHiddenApiExemptions(String[] signaturePrefixes);

经过找寻实际调用的位置: dalvik_system_VMRuntime.cc.

     static void VMRuntime_setHiddenApiExemptions(JNIEnv* env,
                                                jclass,
                                                jobjectArray exemptions) {
      std::vector<std::string> exemptions_vec;
      int exemptions_length = env->GetArrayLength(exemptions);
      for (int i = 0; i < exemptions_length; i++) {
        jstring exemption = reinterpret_cast<jstring>(env->GetObjectArrayElement(exemptions, i));
        const char* raw_exemption = env->GetStringUTFChars(exemption, nullptr);
        exemptions_vec.push_back(raw_exemption);
        env->ReleaseStringUTFChars(exemption, raw_exemption);
      }
       Runtime::Current()->SetHiddenApiExemptions(exemptions_vec);
     }

找到实际的实现的位置


//已从黑名单中删除并处理的方法的签名前缀列表
//好像白名单一样。
  // List of signature prefixes of methods that have been removed from the blacklist, and treated
  // as if whitelisted.
  std::vector<std::string> hidden_api_exemptions_;
  
  void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) {
    hidden_api_exemptions_ = exemptions;
  }

  const std::vector<std::string>& GetHiddenApiExemptions() {
    return hidden_api_exemptions_;
  }
  

这个函数的出现 证明我们之前盘的逻辑肯定有一定的疏漏,重新盘一遍发现:
在case Domain::kApplication: 调用的是ShouldDenyAccessToMemberImpl 具体实现
找到这个方法实现
链接: hidden_api.cc.

 
template<typename T>
bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) {
 DCHECK(member != nullptr);
 Runtime* runtime = Runtime::Current();

 EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy();
 DCHECK(policy != EnforcementPolicy::kDisabled)
     << "Should never enter this function when access checks are completely disabled";

 const bool deny_access =
     (policy == EnforcementPolicy::kEnabled) &&
     IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(),
                                api_list.GetMaxAllowedSdkVersion());

 MemberSignature member_signature(member);

 // Check for an exemption first. Exempted APIs are treated as white list.
 if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
   // Avoid re-examining the exemption list next time.
   // Note this results in no warning for the member, which seems like what one would expect.
   // Exemptions effectively adds new members to the whitelist.
   MaybeUpdateAccessFlags(runtime, member, kAccPublicApi);
   return false;
 }

 if (access_method != AccessMethod::kNone) {
   // Print a log message with information about this class member access.
   // We do this if we're about to deny access, or the app is debuggable.
   if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) {
     member_signature.WarnAboutAccess(access_method, api_list, deny_access);
   }

   // If there is a StrictMode listener, notify it about this violation.
   member_signature.NotifyHiddenApiListener(access_method);

   // If event log sampling is enabled, report this violation.
   if (kIsTargetBuild && !kIsTargetLinux) {
     uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
     // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
     static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
     if (eventLogSampleRate != 0) {
       const uint32_t sampled_value = static_cast<uint32_t>(std::rand()) & 0xffff;
       if (sampled_value < eventLogSampleRate) {
         member_signature.LogAccessToEventLog(sampled_value, access_method, deny_access);
       }
     }
   }

   // If this access was not denied, move the member into whitelist and skip
   // the warning the next time the member is accessed.
   if (!deny_access) {
     MaybeUpdateAccessFlags(runtime, member, kAccPublicApi);
   }
 }

 return deny_access;
}

到这里我们可以发现,原来在这里做了一次排除,如果存在GetHiddenApiExemptions()这个集合里的类将通过反射限制检查

bool MemberSignature::IsExempted(const std::vector<std::string>& exemptions) {
  for (const std::string& exemption : exemptions) {
    if (DoesPrefixMatch(exemption)) {
      return true;
    }
  }
  return false;
}


if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
    // Avoid re-examining the exemption list next time.
    // Note this results in no warning for the member, which seems like what one would expect.
    // Exemptions effectively adds new members to the whitelist.
    MaybeUpdateAccessFlags(runtime, member, kAccPublicApi);
    return false;
  }

那么得出结论,我们最简单的实现突破限制的解决方案出来了。
只要反射运行时实例调用setHiddenApiExemptions将要突破的隐藏AP类加进去就可以了。

当然根据下面这个函数我们可以分析出我们只需要把L字符串当作排出类的字符串设置进来就可以解除隐藏API限制了

bool MemberSignature::DoesPrefixMatch(const std::string& prefix) const {
  size_t pos = 0;
  for (const char* part : GetSignatureParts()) {
    size_t count = std::min(prefix.length() - pos, strlen(part));
    if (prefix.compare(pos, count, part, 0, count) == 0) {
      pos += count;
    } else {
      return false;
    }
  }
  // We have a complete match if all parts match (we exit the loop without
  // returning) AND we've matched the whole prefix.
  return pos == prefix.length();
}

既然如此实现就很简单了,实现感兴趣的可以自己实现一下。
如果图方便,我这里也有所实现给一个文件链接ReflectionLimit: link.

引入后调用clearLimit这个静态方法就可以了。

如果有学到,或者觉得有用,记得给我一个星星。


http://www.niftyadmin.cn/n/893219.html

相关文章

python:画论文标准图

用于论文等正式场合的图。 图1 论文中采用的图linestyle线的种类&#xff1a; 实线&#xff1a;-虚线&#xff1a;--点线&#xff1a;:杠点线&#xff1a;-. marker参数在折线上打标记&#xff1a; 实心点&#xff1a;.无标记&#xff1a;,实心点&#xff1a;o三角形(上下左右…

flutter 一些小报错记录

错误描述&#xff1a; 已有的页面跳转正常&#xff0c;添加一个新页面后&#xff0c;新页面跳转出现如下错误 Generators for routes are searched for in the following order:1. For the "/" route, the "home" property, if non-null, is used.2. Othe…

深度估计

一.迁移学习[论文][Keras][监督] 1).网络&#xff1a; 图1-1.网络框架。带跳跃连接的编解码器结构。编码器采用在ImageNet上预训练过的DenseNet-169&#xff0c;解码器由卷积和上采样组成。2).损失函数&#xff1a; 每个像素的预测深度与实际深度的L1损失&#xff1a;。由于该…

谈谈自己对于Binder的理解

Binder 是什么&#xff1f; Binder是Android的一种主要跨进程的通讯方式。 Android为什么要用Binder作为跨进程的通讯方式&#xff1f; 一般说到来跨进程的通讯方式常用的有以下几种&#xff1a; 1.socket 比较重量级了一般用在网络通讯 性能差 2.文件 性能差io多 3.共享内存…

Pythorh报错

一. 错误&#xff1a; Traceback (most recent call last):File "train.py", line 419, in <module>main()File "train.py", line 409, in mainsegm_crit1, segm_crit2)File "train.py", line 275, in train_segmenterloss2.backward()Fi…

新年闲聊随记

新年闲聊 2021年了&#xff0c;大家新年好&#xff01;&#xff01; 隔了很久没写文章&#xff0c;至于原因嘛&#xff0c;我升级了&#xff0c;2020年12月15日我当爸爸了。 尽管生育多灾多难&#xff0c;好事多磨吧&#xff0c;孕育的过程中有各种灾难&#xff0c;苦恼&#…

泛型类中泛型T的类型获取

赋值类型 Type superClass getClass().getGenericSuperclass();Type type ((ParameterizedType) superClass).getActualTypeArguments()[0];if (type instanceof ParameterizedType) {this.classType (Class<T>) ((ParameterizedType) type).getRawType();} else {th…

Ablation Studies简化测试

设计了一个深度学习模型后&#xff0c;为了验证添加的功能模块是否起作用、起多大作用&#xff0c;可以用实验来验证&#xff0c;这样的实验称为简化测试。比如这篇论文。 根据奥卡姆剃刀定律&#xff0c;我们应该尽量去掉那些不必要的模块。