Weishu's Notes

Weishu's Notes

马上订阅 Weishu's Notes RSS 更新: https://weishu.me/atom.xml

一种绕过Android P对非SDK接口限制的简单方法

2018年6月7日 21:14

众所周知,Android P 引入了针对非 SDK 接口(俗称为隐藏API)的使用限制。这是继 Android N上针对 NDK 中私有库的链接限制之后的又一次重大调整。从今以后,不论是native层的NDK还是 Java层的SDK,我们只能使用Google提供的、公开的标准接口。这对开发者以及用户乃至整个Android生态,当然是一件好事。但这也同时意味着Android上的各种黑科技有可能会逐渐走向消亡。

作为一个有追求的开发者,我们既要尊重并遵守规则,也要有能力在必要的时候突破规则的束缚,带着镣铐跳舞。恰好最近有人反馈 VirtualXposed 在 Android P上无法运行,那么今天就来探讨一下,如何突破Android P上针对非SDK接口调用的限制。

系统是如何实现这个限制的?

知己知彼,百战不殆。既然我们想要突破这个限制,自然先得弄清楚,系统是如何给我们施加这个限制的。

文档 中说,通过反射或者JNI访问非公开接口时会触发警告/异常等,那么不妨跟踪一下反射的流程,看看系统到底在哪一步做的限制(以下的源码分析大可以走马观花的看一下,需要的时候自己再仔细看)。我们从 java.lang.Class.getDeclaredMethod(String) 看起,这个方法在Java层最终调用到getDeclaredMethodInternal 这个native方法,看一下这个方法的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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)));
if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
}

注意那个 ShouldBlockAccessToMember 调用了吗?如果它返回false,那么直接返回nullptr,上层就会抛 NoSuchMethodXXX 异常;也就触发系统的限制了。于是我们继续跟踪这个方法,这个方法的实现在 java_lang_Class.cc,源码如下:

1
2
3
4
5
6
7
8
9
ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {

hiddenapi::Action action = hiddenapi::GetMemberAction(
member, self, IsCallerTrusted, hiddenapi::kReflection);
if (action != hiddenapi::kAllow) {
hiddenapi::NotifyHiddenApiListener(member);
}
return action == hiddenapi::kDeny;
}

毫无疑问,我们应该继续看 hidden_api.cc 里面的 GetMemberAction方法 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
template<typename T>
inline Action GetMemberAction(T* member,
Thread* self,
std::function<bool(Thread*)> fn_caller_is_trusted,

AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {

DCHECK(member != nullptr);...

剩余内容已隐藏

查看完整文章以阅读更多