-
-
[原创] 一个通用的纯 Java 安卓隐藏 API 限制绕过方案
-
发表于:
2021-4-24 20:49
14048
-
[原创] 一个通用的纯 Java 安卓隐藏 API 限制绕过方案
文章同时发表在我个人博客上。转载请务必注明出处。
2018年发布的 Android P 中引入了对隐藏API的限制,这对整个Android生态来说当然是一件好事,但也严重限制了以往我们通过反射等手段实现的“黑科技”(如插件化等),所以开发者们纷纷寻找手段绕过这个限制。比如 Canyie 就曾经提出了两个绕过方法,其中一个便是几乎完美的双重反射(即“元反射”,现在来看叫“套娃反射”比较好);而在即将发布的Android R中把这个方法封杀了。
这是 Canyie 在其博客上^1提到的安卓隐藏 API 的问题。博客上还提出了一个看似可行的解决方案:设置类的 classloader
为 null
成为 BootClassPath
中的类以解除限制。此法看似可行,然并非万全:
- 首先在有隐藏 API 限制的情况下修改自己的
classloader
非常困难(但是仍可行)
- 类是否有隐藏 API 限制是由其在加载时候就设置好的 Domain 决定,所以在加载类之后再修改
classloader
就没有用了。
- 利用
DexFile
加载一个没有 classloader
的类可以(甚至可以通过 base64 加载一个预制在 java 字符串中的 dex 文件),但是 DexFile
已经 deprecated 掉,并且在不日加入豪华隐藏 API 列表^2。
- 谷歌方面已经开始着手不信任
classloader
为 null
的类了^6。
这样看来,纯 Java 绕过隐藏 API 似乎没戏了。只能通过 native 代码,用魔法绕过 dlopen
限制来 dlsym
在 libart.so
中的 _ZN3artL32VMRuntime_setHiddenApiExemptionsEP7_JNIEnvP7_jclassP13_jobjectArray
来设置允许隐藏 API 了。
等等。
我们好像还有一个非常适合玩魔法的 Java 自带的 API:Unsafe
!
这个魔法类顾名思义,是非常不安全的:它可以纯 Java 读写内存!也就是说,有了这个东西,我们就可以读写类中的任意数据成员了!甚至如果拿到 native 指针,还能直接读指针指向的内存内容。那么我们是不是可以藉此来读取类的隐藏函数?答案是肯定的。
但是,ART 中的函数不是存在一个 Java 数组中乖乖等着给你拿的。ART 的模型是 Java 代码中的重要类和 native 代码中的一个类相互 mirror:即共享同一块内存。所以在 Java 中读写这些 Java 类对象的成员相当于同时修改对应的 native 对象成员。一些常见的类就是 Class
、Method
、Field
等。于是为了方便 native 代码访问这些对象,这些类的成员很多都不是以 Java 对象形式存在,而是以 native 指针形式存在。比如一个 Class
对应的 Java 结构如下^3:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public final class Class {
private transient ClassLoader classLoader;
private transient Class componentType;
private transient Object dexCache;
private transient ClassExt extData;
private transient Object [] ifTable;
private transient Classsuper T> superClass;
private transient Object vtable;
private transient long iFields;
private transient long methods;
private transient long sFields;
private transient int accessFlags;
private transient int classFlags;
/ / 还有其他成员,这里就写了
}
|
可以看到有很多 long
成员,这些就是 native 指针了。很可惜,我们想要的 methods
就是以指针形式存在的,而且指针对应的是 ArtMethod
的 native 对象,并非一个对应到 Java 的 mirror 的 Method
。感兴趣的小伙伴可以看看 Executable
的实现,其中有一个 long
成员是 artMethod
对应的就是这个东西。
那么我们需要把这个 methods
指针对应的 artMethod
给一个一个枚举出来,并且想办法把他们转换为 Java 可以用的 Executable
(用 Executable
是这个包含 Constructor
和 Method
)。
要理解这个指针到底存了啥,我们就要回到 native 代码^4。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2021-4-24 20:52
被yujincheng08编辑
,原因: