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

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 result = hs.NewHandle(
          mirror::Class::GetDeclaredMethodInternal(
              soa.Self(),
              DecodeClass(soa, javaThis),
             soa.Decode(name),
              soa.Decode>(args),
              GetHiddenapiAccessContextFunction(soa.Self())));
      if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
        return nullptr;
      }
      return soa.AddLocalReference(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
     inline bool ShouldDenyAccessToMember(T* member,
                                         const std::function& 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 exemptions_vec;
      int exemptions_length = env->GetArrayLength(exemptions);
      for (int i = 0; i < exemptions_length; i++) {
        jstring exemption = reinterpret_cast(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 hidden_api_exemptions_;
  
  void SetHiddenApiExemptions(const std::vector& exemptions) {
    hidden_api_exemptions_ = exemptions;
  }

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

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

 
template
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(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& 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这个静态方法就可以了。

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

你可能感兴趣的:(Android知识分析,Android系统源码分析,android,反射,java)