java Unsafe详细解析

Java Unsafe详细解析

简介

Java Unsafe 是 JDK 提供的一个支持直接操作内存、线程、JVM 的类库。由于 Unsafe 操作的是内存,所以它可以绕过 JVM 的安全检查,说白了就是越过了 Java 的限制,直接操作底层内存。不是直接通过 new 实例化对象进行使用,而是通过反射或本地方法调用获取一个实例。

使用

Unsafe 类主要包含以下方法:

  1. 内存存取操作

    • putXXX():存放基本类型到指定的内存地址;
    • getXXX():从指定地址获取指定类型的值。
  2. 对象创建和销毁

    • allocateInstance(Class cls):实例化一个没有调用构造方法的对象;
    • freeMemory(obj):释放对象空间。
  3. 数组操作

    • arrayBaseOffset(Class<?> arrayClass):获取指定类型数组第一个元素的偏移量;
    • arrayIndexScale(Class<?> arrayClass):获取指定类型数组每个元素占用的空间大小;
    • getObject(Object array, long index):获取指定索引位置的元素;
    • putObject(Object array, long index, Object value):设置指定索引位置的元素值。
  4. 线程和锁操作

    • park(boolean isAbsolute, long time):暂停线程运行指定时间,单位为纳秒;
    • unpark(Object thread):继续运行指定的线程;
    • monitorEnter(Object obj):获取对象锁;
    • monitorExit(Object obj):释放对象锁。

示例1:修改String对象值

public class ChangeStringValue {
    public static void main(String[] args) throws Exception {
        String str = "Hello, World!";
        System.out.println("Original string: " + str);

        // 获取Unsafe实例
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);

        // 获取字符数组的首地址和长度,修改数组中某个字符的值
        long stringBaseOffset = unsafe.arrayBaseOffset(char[].class);
        long addressOfFirstChar = unsafe.getLong(str, stringBaseOffset);
        char[] chars = (char[]) unsafe.getObject(str, stringBaseOffset);
        chars[7] = 'J';

        // 重新构造字符串
        String newStr = (String) unsafe.allocateInstance(String.class);
        unsafe.putLong(newStr, stringBaseOffset, addressOfFirstChar);
        System.out.println("New string: " + newStr);
    }
}

运行结果:

Original string: Hello, World!
New string: Hello, Jorld!

此示例展示了可以使用 Unsafe 遍历并修改字符串的字符,以及如何重新构造一个字符串对象。

示例2:破解懒汉式单例模式

public class LazySingleton {
    private static volatile LazySingleton instance;
    private LazySingleton() {}

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

通过指针去创建对象的方式可以避免 synchronized 存在的锁消耗,但也会带来多对象被实例化的风险。这种方式并不能保证只会创建一个对象,只是此处概率较小。

public class ReflectionCreateObj implements Serializable {
    private static LazySingleton instance;

    static {
        Field field;
        try {
            field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            Unsafe unsafe = (Unsafe) field.get(null);

            // 获取LazySingleton对象的引用地址
            long instanceOffset = unsafe.objectFieldOffset(
                    ReflectionCreateObj.class.getDeclaredField("instance"));

            // 修改对象引用值
            UnsafeReflectionTest uninstallObj = new UnsafeReflectionTest();
            unsafe.putOrderedObject(uninstallObj, instanceOffset, LazySingleton.getInstance());
        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static LazySingleton getInstance() {
        return instance;
    }
}

此示例展示了通过 Unsafe 修改了 LazySingleton 对象的引用地址,破解了懒汉式单例模式。这种方式可以恶意绕过系统的业务逻辑,窃取并篡改敏感数据。

阅读剩余 63%

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java Unsafe详细解析 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • 用c语言实现一个电话薄(附完整代码)

    下面是用C语言实现一个电话薄的攻略。 1. 确定数据结构 首先需要确定电话薄的数据结构,可以使用结构体类型来表示每个联系人,包括姓名和电话号码两个元素。代码如下: struct Contact { char name[20]; char phone[12]; }; 2. 实现主函数 主函数负责用户的交互,可以使用一个数组来存储电话薄中的联系人信息。具体实现过…

    C 2023年5月23日
    00
  • C语言 按值函数调用

    C语言 按值函数调用 在 C 语言中,函数可以按值调用(也称为传值调用)或按引用调用(也称为传引用调用)。 按值调用函数时,函数会使用参数的值进行计算,并在函数中对其进行修改不会影响原始值。 下面是一个按值调用的例子: #include <stdio.h> // 定义一个函数,输入两个整数并返回它们的和 int add(int a, int b)…

    C 2023年5月9日
    00
  • php中JSON的使用与转换

    当我们需要在不同的应用程序之间传输数据时,使用JSON(JavaScript对象表示)是一种非常流行的格式。PHP中的JSON函数使得解析和生成JSON数据非常容易。下面是使用和转换JSON数据的完整攻略。 1. 安装JSON扩展 在使用JSON之前,在PHP中安装JSON扩展是必要的。可以通过以下命令来检测JSON扩展是否已经安装。 php -m | gr…

    C 2023年5月23日
    00
  • phpcms缓存使用总结(memcached、eaccelerator、shm)

    PHPcms缓存使用总结 PHPcms 是一个基于 PHP 的开源 CMS(内容管理系统),支持各种数据库,并拥有完善的权限管理、缓存等功能。缓存是提高 PHP 程序性能的重要手段之一,下面我们就来详细讲解一下 PHPcms 缓存的使用总结。 1. 缓存类型介绍 PHPcms 有多种缓存类型可供选择,包括:memcached、eaccelerator、shm…

    C 2023年5月22日
    00
  • C语言实现数独程序的示例代码

    下面是关于“C语言实现数独程序的示例代码”的完整攻略: 一、编写数独程序的流程 1. 确定程序输入和输出 数独程序的输入应该是一个9×9的矩阵,即数独的谜题,其中0表示未知格子。程序的输出应该是一个解开谜题后的9×9矩阵。 2. 确定算法 数独程序的算法一般有两种,分别是暴力求解和回溯法。 2.1 暴力求解 暴力求解是指从左到右、从上到下依次填数,直到填到空…

    C 2023年5月23日
    00
  • PHP设计模式概论【概念、分类、原则等】

    PHP设计模式概论 概念 设计模式是指在面向对象编程中用于解决特定问题的重复使用的经验总结。设计模式不是一个可直接转换成代码的解决方案,而是定义了一组通用的原则和规范,它们可以用于设计任何系统。 分类 设计模式可以分为三类:创建型、结构型和行为型。 创建型模式 创建型模式主要用于对象的创建,包括“工厂模式”、“抽象工厂模式”、“单例模式”、“原型模式”、“建…

    C 2023年5月22日
    00
  • Win10蓝屏代码0xc0000034怎么办?

    Win10蓝屏代码0xc0000034的解决方法 当Windows10出现蓝屏并显示错误代码0xc0000034时,我们可以按照以下步骤来解决这个问题。 步骤1:检查硬件问题 此错误通常是由于硬件问题引起的。首先,我们需要检查相关硬件是否正常工作,特别是新安装的硬件或故障的硬件。 步骤2:尝试系统修复 在出现蓝屏之后,我们可以尝试使用Windows的自带工具…

    C 2023年5月23日
    00
  • C语言深度解剖篇之关键字以及补充内容

    C语言深度解剖篇之关键字以及补充内容 介绍 在C语言中,关键字具有特殊含义,是编译器中预定义的标识符。在编写程序时,需要注意不能使用关键字作为变量名或函数名,否则会导致编译错误。 常用关键字 下面是一些常见的C语言关键字: auto: 声明自动变量 break: 中断当前循环语句或switch语句 const: 声明常量,值不能被修改 continue: 继…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部