反射大家都会用,但是系统为了隐藏一些方法调用,将方法分为各种权限,以往我们看源码可以通过一些反射调用的方式调用函数,但是后来加入了api等级,如果在黑名单中的 api,通过反射也会调用不到,那么为什么呢?
- 下面是普通反射的代码
Class<?> aClass = Class.forName("com.basic.myapplication.Person");
Object object = aClass.newInstance();
Method go = aClass.getDeclaredMethod("go");
Method run = aClass.getMethod("run");
go.setAccessible(true);
go.invoke(object);
run.invoke(object);
api 28 以后系统的一些属性通过反射也获取不到了,那么一定是通过限制反射来处理的,接下来看看反射中做了哪些限制? 下面是在 Android Code Search 上 Master的代码,应该是android 12 + 的代码了。
getDeclaredMethod() 调用了 getDeclaredMethodInternal() ,它是一个 native 方法。
@FastNative
private native Method getDeclaredMethodInternal(String name, Class<?>[] args);
- android9 的 getDeclaredMethodInternal() 在 native 层的代码
static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis,
jstring name, jobjectArray args) {
// ...
if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
}
Class_getDeclaredMethodInternal 中,调用了函数 ShouldDenyAccessToMember() ,翻译一下就是是否拒绝成员访问。这里面可能就是对反射做出的限制。
- ShouldDenyAccessToMember() 返回 true 则 deny 掉了此次调用 false 则可以正常调用。
template<typename T>
ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return hiddenapi::ShouldDenyAccessToMember(member,
GetHiddenapiAccessContextFunction(self),
hiddenapi::AccessMethod::kReflection);
}
- art/runtime/hidden_api.cc -> ShouldDenyAccessToMember()
template<typename T>
bool ShouldDenyAccessToMember(T* member,
const std::function<AccessContext()>& fn_get_access_context,
AccessMethod access_method) {
DCHECK(member != nullptr);
// 首先先检查是否有sdk检查器,检查通过 BootStrapClassLoaded 加载的 dex 文件的api。
Runtime* runtime = Runtime::Current();
if (UNLIKELY(runtime->IsAotCompiler())) {
if (member->GetDeclaringClass()->IsBootStrapClassLoaded() &&
runtime->GetClassLinker()->DenyAccessBasedOnPublicSdk(member)) {
return true;
}
}
// 获取运行时标志
const uint32_t runtime_flags = GetRuntimeFlags(member);
// 如果是 public 的 api 则直接返回 false 不拦截
if ((runtime_flags & kAccPublicApi) != 0) {
return false;
}
// 判断上下文 作用域
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());
// 检查是否总是允许调用方访问被调用方上下文中的成员是否总是允许访问 如果允许则直接返回false
if (caller_context.CanAlwaysAccess(callee_context)) {
return false;
}
// 接下来查看访问的是哪些api了 会根据每个api的特性 来确定是否允许访问
switch (caller_context.GetDomain()) {
case Domain::kApplication: {
DCHECK(!callee_context.IsApplicationDomain());
// Exit early if access checks are completely disabled.
EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy();
if (policy == EnforcementPolicy::kDisabled) {
return false;
}
// If this is a proxy method, look at the interface method instead.
member = detail::GetInterfaceMemberIfProxy(member);
// 获取 dex 文件中的所有 api
ApiList api_list(detail::GetDexFlags(member));
DCHECK(api_list.IsValid());
// Member is hidden and caller is not exempted. Enter slow path.
// 判断 api 是否是允许访问,如果不允许则返回 true 阻止下一步的调用
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();
}
}
}
ShouldDenyAccessToMember() 函数就是通过判断系统中各种api的限制,和访问用户的来源来判断是否开放api,根据api列表判断访问的api是否可以开放等。其中如果通过 hook 将整个 方法的返回值直接返回 false ,则访问api就不受限制了。或者修改Hidden api 成员来跳过检查。有大神做过native的hook绕过检测
FreeReflection is a library that lets you use reflection without any restriction above Android P (includes Q and R).