谈谈 Unsafe 在 Java 中的作用

网站建设3年前发布
21 0 0

最近在 Kotlin 项目中发现,定义的 data class​(成员变量都声明不可空)经过在 Gson​ 解析后,可以得到成员变量为空的对象,而不是得到解析失败,那么就很容易造成后续代码的非预期运行,因为成员变量都按不可空的情况来处理,最终喜提 NullPointerException。,在 Gson​ 的代码中找到实例化对象的地方,经过几种构造方式失败后最终会使用 Unsafe 的来构造实例。,Unsafe 是位于 ​​sun.misc​​ 包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升 Java 运行效率、增强 Java 语言底层资源操作能力方面起到了很大的作用。Unsafe 使 Java 语言拥有了类似 C 语言指针一样操作内存空间的能力,对 Unsafe 的使用一定要慎重。,Gson  采用的便是其对象操作的能力,使用 ​​allocateInstance​​ 方法,达到绕过构造方法创建对象。,通过 Unsafe#allocateInstance​ 实例化的对象绕过了构造函数,在 Koltin 中要额外注意,因为 Kotlin 对非空变量的赋值都会经过  Intrinsics.checkParameterIsNotNull 的处理,而此时构造函数的一系列判断均被绕过,导致上下文不一致。,Unsafe 为单例实现,并且 getUnsafe()​ 静态方法仅在调用的类为引导类加载器 BootstrapClassLoader 加载时才合法,直接反射获取 Unsafe 实例吧!,在 Android P 版本之后 限制隐藏 API 的调用,作为一个 「「合格」」 的开发者应该尊重官方的规则,也有利于项目的长期维护。但偶尔也要试试打破规则!,通常调用隐藏 API 都是通过反射的方式,但是反射的调用也被拦截。,源码分析可以找到 java.lang.Class#getDeclaredMethod()​ 最终会调用 native 方法 getDeclaredMethodInternal。,当 「「ShouldBlockAccessToMember」」 返回 true 时,那么直接返回 nullptr,上层就会抛 ​​NoSuchMethodXXX​​ 异常,触发了系统限制。,主要判断逻辑中三个条件有一处通过就不会触发系统限制,fn_caller_is_trusted 便是判断调用者的 Class 是否通过 BootClassLoader 加载,所以系统可以直接调用隐藏 API,系统 Class 均由 BootClassLoader 加载。,通过 BootClassLoader 加载的类,其 ClassLoader 则为 null,那么只要将一个业务中已加载 Class 的 ClassLoader 设置为 null ,该 Class 便可以通过反射调用隐藏 API 了。,反射是直接修改 Class.classLoader​ 是行不通的,因为该字段在深灰名单中,会抛 NoSuchFiledException。,通过 Unsafe 拿到 Class 中 classloader 的偏移量,将偏移量处置为 null。,Class 在内存中的结构如下,前两项变量继承于 Object,分别都是 4 个字节,所以 classloader 的偏移量为 8。,果然偏移量为 8,输出的是 classloader 信息,设置为 null,再次 getClassLoader 已经变成 BootClassLoader。

© 版权声明

相关文章