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

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

  • 13
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android OTA(Over-The-Air)升级是指通过网络传输直接在设备上进行系统升级的一种方法。下面简要介绍Android OTA升级的流程以及常见问题分析Android OTA升级流程通常包括以下几个步骤: 1. 系统准备:开发者根据新版本的要求,为设备准备升级所需的系统文件。这些文件包括操作系统文件、应用程序文件以及升级脚本等。 2. 设备请求:设备通过连接到网络,并向服务器请求最新的系统升级。服务器根据设备的型号和当前系统版本,判断是否需要进行升级。 3. 传输升级文件:如果服务器确认设备需要进行升级,那么就会将升级文件传输给设备。这通常是通过HTTP或FTP等协议进行数据传输。 4. 升级验证:设备接收到升级文件后,会进行文件校验,以确保文件的完整性和一致性。 5. 升级安装:设备在校验通过后,会进行系统升级安装。这通常会涉及到文件解压、系统分区扩展等操作。 6. 安装后处理:升级完成后,设备会重新启动并进行一些后期处理工作,例如数据迁移、应用优化等。 常见问题分析: 1. 升级失败:升级过程中可能出现各种原因导致升级失败,如网络中断、升级文件损坏、设备存储空间不足等。解决方法可以包括重新尝试升级、检查网络连接、清理设备存储空间等。 2. 兼容性问题:新版本的系统可能对设备硬件或软件要求更高,而一些老旧设备可能无法满足这些要求。这可能导致升级后设备性能下降或功能不可用。解决方法可以是提供适配的系统版本或更新设备硬件。 3. 数据丢失:升级过程中可能导致设备的数据丢失,包括联系人、短信、应用程序数据等。为了避免这种问题,可以提醒用户备份数据或提供数据迁移工具。 4. 升级时间过长:升级过程可能需要较长时间,尤其是在升级文件较大或设备性能较低的情况下。解决方法可以是优化升级文件的大小和传输速度,或提供快速升级选项。 总结:Android OTA升级通过网络直接在设备上进行系统升级,流程包括准备、请求、传输、验证、安装和处理。常见问题包括升级失败、兼容性问题、数据丢失和升级时间过长等,需要通过合适的解决方法来处理。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值