这是我写的一个库,突破反射限制的 -> JJReflection 支持android9-12 采用策略:System.loadLibrary + native线程 两种方式 优先使用System.loadLibrary方式,如果失败了则追加使用native线程方式
Android9+
原因
反射的时候增加了方法签名校验机制,如果该方法签名不在 割免列表 中,都会被拒绝访问。(HiddenApi都不在割免列表中)
解决方法
将方法签名加入到 割免列表 就能突破限制了。(class的签名都是以L开头的,将L加入到豁免列表,就能所有起飞了。)
元反射:
通过反射 getDeclaredMethod 方法,getDeclaredMethod 是 public 的,不存在问题。 反射调用 getDeclardMethod 是以系统身份去反射的系统类,因此元反射方法也被系统类加载的方法;所以我们的元反射方法调用的 getDeclardMethod 会被认为是系统调用的,可以反射任意的方法
源码分析
java_lang_Class.cc#Class_getDeclaredMethodInternal
-> ShouldBlockAccessToMember
-> hidden_api.h#GetMemberAction
-> hidden_api.cc#GetMemberActionImpl
Class_getDeclaredMethodInternal:
if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
ShouldBlockAccessToMember:
hiddenapi::Action action = hiddenapi::GetMemberAction(member, self, IsCallerTrusted, hiddenapi::kReflection);
return action == hiddenapi::kDeny;
GetMemberActionImpl:
Runtime* runtime = Runtime::Current();
if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
action = kAllow;
MaybeWhitelistMember(runtime, member);
return kAllow;
}
bool MemberSignature::IsExempted(const std::vector<std::string>& exemptions) {
for (const std::string& exemption : exemptions) {
if (DoesPrefixMatch(exemption)) {
return true;
}
}
return false;
}
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;
}
}
return pos == prefix.length();
}
android11+
原因
元反射失效了,因为增加了 调用者上下文判断 机制。机制给 调用者 跟 被调用的方法 增加了domian,调用者domain要等于或者小于 被调用的方法domian,才能允许访问。
解决方法
1、寻找domain小的调用栈,在那里进行 割免列表 的添加。
System.loadLibrary(java.lang.System 类在 /apex/com.android.runtime/javalib/core-oj.jar 里边),所以System.loadLibrary的调用栈的domain很低的。而在System.loadLibrary中最终会调到JNI_OnLoad函数,所以可以在JNI_OnLoad函数里边进行割免操作。
参考:https://bbs.pediy.com/thread-268936.htm
2、让系统无法识别调用栈,系统就会给一个最小的domain我们。
在native层创建一个新线程,然后获取该子线程的JniEnv,通过该JniEnv来进行割免操作。
参考:https://www.jianshu.com/p/6546ce67c8e0
3、利用BootClassLoader – FreeReflection库(https://github.com/tiann/FreeReflection)
DexFile设置父ClassLoader为null(BootClassLoader,它用于加载一些Android系统框架的类,如果用BootClassLoader它来加载domain肯定很低),然后再进行割免操作。
(个人觉得,直接用还是可以的。对其进行改造确实麻烦,还得生成dex然后再转base64啥的。)
源码分析
java_lang_Class.cc#Class_getDeclaredMethodInternal
-> ShouldDenyAccessToMember
-> hidden_api.h#ShouldDenyAccessToMember
-> hidden_api.cc#ShouldDenyAccessToMemberImpl
Class_getDeclaredMethodInternal:
...
if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference<jobject>(result.Get());
enum class Domain : char {
kCorePlatform = 0,
kPlatform,
kApplication,
};
ShouldDenyAccessToMember:
...
const AccessContext caller_context = fn_get_access_context();
const AccessContext callee_context(member->GetDeclaringClass());
if (caller_context.CanAlwaysAccess(callee_context)) {
return false;
}
GetReflectionCaller:
static hiddenapi::AccessContext GetReflectionCaller(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
struct FirstExternalCallerVisitor : public StackVisitor {
explicit FirstExternalCallerVisitor(Thread* thread)
: StackVisitor(thread, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
caller(nullptr) {
}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod *m = GetMethod();
if (m == nullptr) {
caller = nullptr;
return false;
} else if (m->IsRuntimeMethod()) {
return true;
}
ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClass();
if (declaring_class->IsBootStrapClassLoaded()) {
if (declaring_class->IsClassClass()) {
return true;
}
ObjPtr<mirror::Class> lookup_class = GetClassRoot<mirror::MethodHandlesLookup>();
if ((declaring_class == lookup_class || declaring_class->IsInSamePackage(lookup_class))
&& !m->IsClassInitializer()) {
return true;
}
ObjPtr<mirror::Class> proxy_class = GetClassRoot<mirror::Proxy>();
if (declaring_class->IsInSamePackage(proxy_class) && declaring_class != proxy_class) {
if (Runtime::Current()->isChangeEnabled(kPreventMetaReflectionBlacklistAccess)) {
return true;
}
}
}
caller = m;
return false;
}
ArtMethod* caller;
};
FirstExternalCallerVisitor visitor(self);
visitor.WalkStack();
ObjPtr<mirror::Class> caller = (visitor.caller == nullptr)
? nullptr : visitor.caller->GetDeclaringClass();
return caller.IsNull() ? hiddenapi::AccessContext( true)
: hiddenapi::AccessContext(caller);
}
DexFile domain赋值逻辑:
hidden_api.cc#InitializeDexFileDomain:
Domain dex_domain = DetermineDomainFromLocation(dex_file.GetLocation(), class_loader);
dex_file.SetHiddenapiDomain(dex_domain);
hidden_api.cc#DetermineDomainFromLocation:
if (ArtModuleRootDistinctFromAndroidRoot()) {
if (LocationIsOnArtModule(dex_location.c_str())
|| LocationIsOnConscryptModule(dex_location.c_str())) {
return Domain::kCorePlatform;
}
if (LocationIsOnApex(dex_location.c_str())) {
return Domain::kPlatform;
}
}
if (LocationIsOnSystemFramework(dex_location.c_str())) {
return Domain::kPlatform;
}
if (class_loader.IsNull()) {
return Domain::kPlatform;
}
return Domain::kApplication;
参考
FreeReflection
Android 11 绕过反射限制
安卓hiddenapi访问绝技
|