// 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这个静态方法就可以了。
如果有学到,或者觉得有用,记得给我一个星星。