ART学习系列:Android 10 的Class的GetMethod 过程分析

ART学习系列:Android 10 的Class的GetMethod 过程分析

之前我们分析了Android9 的调用过程,并且尝试着一种方式,可以访问隐藏Api,但是最近发现当APP的target api为 29的时候,该方法失效了,所以猜测,Android 10 的GetMethod 方法过程发生变化, 那么一起分析一下。

从Class.java 层的GetMethod 与Android 9 上的并无区别。

我们从Native 层的方法开始分析。

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

我们发现getDeclaredMethodInternal 的方法内部大部分相同,但是ShouldDenyAccessToMember 这个方法名字与Android9时的方法

发生了变化。 这个勾起我的兴趣。

  ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
      REQUIRES_SHARED(Locks::mutator_lock_) {
    return hiddenapi::ShouldDenyAccessToMember(member,
                                               GetHiddenapiAccessContextFunction(self),
                                               hiddenapi::AccessMethod::kReflection);
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();
  
    // 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.
        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();
      }
    }
  }

看到这个过程,首先是GetRuntimeFlags(member) 返回了该方法的access flag 的状态,是否是public等。

接着看caller 的域是属于Application 还是platform的。

// Check if the caller is always allowed to access members in the callee context.
if (caller_context.CanAlwaysAccess(callee_context)) {
  return false;
}
// Returns true if this domain is always allowed to access the domain of `callee`.
bool CanAlwaysAccess(const AccessContext& callee) const {
   return IsDomainMoreTrustedThan(domain_, callee.domain_);
}
// List of domains supported by the hidden API access checks. Domain with a lower
// ordinal is considered more "trusted", i.e. always allowed to access members of
// domains with a greater ordinal. Access checks are performed when code tries to
// access a method/field from a more trusted domain than itself.
 enum class Domain {
    kCorePlatform = 0,
    kPlatform,
    kApplication,
  };
  
 inline bool IsDomainMoreTrustedThan(Domain domainA, Domain domainB) {
    return static_cast<uint32_t>(domainA) <= static_cast<uint32_t>(domainB);
 }
  

从这个过程来看

在这个系统版本时,Android给分了三个域,coreplatform, platform, application, 权限级别又高至低。

所以得看从那个域调用到那个域里去, 高权限的可以直接访问低权限的。

接下来就是,根据每个域里里的判断条件来抉择。

首先看Application 这个域,第一步判断是否 disable 访问access。

第二部,根据访问Member到ShouldDenyAccessToMemberImpl 方法进行判断。

433  template<typename T>
434  bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) {
435    DCHECK(member != nullptr);
436    Runtime* runtime = Runtime::Current();
437  
438    EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy();
439    DCHECK(policy != EnforcementPolicy::kDisabled)
440        << "Should never enter this function when access checks are completely disabled";
441  
442    const bool deny_access =
443        (policy == EnforcementPolicy::kEnabled) &&
444        IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(),
445                                   api_list.GetMaxAllowedSdkVersion());
446  
447    MemberSignature member_signature(member);
448  
449    // Check for an exemption first. Exempted APIs are treated as white list.
450    if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
451      // Avoid re-examining the exemption list next time.
452      // Note this results in no warning for the member, which seems like what one would expect.
453      // Exemptions effectively adds new members to the whitelist.
454      MaybeUpdateAccessFlags(runtime, member, kAccPublicApi);
455      return false;
456    }
457  
458    if (access_method != AccessMethod::kNone) {
459      // Print a log message with information about this class member access.
460      // We do this if we're about to deny access, or the app is debuggable.
461      if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) {
462        member_signature.WarnAboutAccess(access_method, api_list, deny_access);
463      }
464  
465      // If there is a StrictMode listener, notify it about this violation.
466      member_signature.NotifyHiddenApiListener(access_method);
467  
468      // If event log sampling is enabled, report this violation.
469      if (kIsTargetBuild && !kIsTargetLinux) {
470        uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
471        // Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
472        static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
473        if (eventLogSampleRate != 0) {
474          const uint32_t sampled_value = static_cast<uint32_t>(std::rand()) & 0xffff;
475          if (sampled_value < eventLogSampleRate) {
476            member_signature.LogAccessToEventLog(sampled_value, access_method, deny_access);
477          }
478        }
479      }
480  
481      // If this access was not denied, move the member into whitelist and skip
482      // the warning the next time the member is accessed.
483      if (!deny_access) {
484        MaybeUpdateAccessFlags(runtime, member, kAccPublicApi);
485      }
486    }
487  
488    return deny_access;
489  }

我们看到了这个函数IsSdkVersionSetAndMoreThan

inline bool IsSdkVersionSetAndMoreThan(uint32_t lhs, SdkVersion rhs) {
    return lhs != static_cast<uint32_t>(SdkVersion::kUnset) && lhs > static_cast<uint32_t>(rhs);
}

IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(), api_list.GetMaxAllowedSdkVersion());

此时这个函数的参数有当前应用的tagertSdkVersion 和 api限制列表的的最大允许sdk版本。

也就是如果targetsdkversion如果小于最大允许sdk版本,那么总的deny_access 的值就会变为false,就不会进行下面的check。

接下来,与Android9的版本里的判断一样,有一个GetHiddenApiExemptions的排除Api的判断。

结束这个函数分析。

返回到 ShouldDenyAccessToMemberImpl 函数, 我们看case platform 有一段代码。runtime_flags 有一个CorePlatformApi 的字段。

// Member is part of core platform API. Accessing it is allowed.
if ((runtime_flags & kAccCorePlatformApi) != 0) {
return false;
}

也就是Api的分级增加了一个CorePlatformApi的字段。

接着我们去检查VMRuntime.java的SetHiddenApiExemptions 方法。

@libcore.api.CorePlatformApipublic native void setHiddenApiExemptions(String[] signaturePrefixes);

我们看到该方法上面加上了CorePlatformApi 的flag。

但是上面的分析过程,我们想尝试从域的访问权限,看看能不能进入到core api的域里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值