Unsafe Java World

Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。

是否支持获取Unsafe工具类:

import java.lang.reflect.Field;

import sun.misc.Unsafe;

public class UnsafeSupport {

    public static final Unsafe UNSAFE;

    static {
        UNSAFE = findUnsafe();
    }

    private static Unsafe findUnsafe() {
        try {
            return Unsafe.getUnsafe();
        } catch (SecurityException ignore) {
            // Ignore, try next
        }

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

        } catch (Exception ignore) {
            // Ignore, try hardcore version! 🙂
        }

        for (Field field : Unsafe.class.getDeclaredFields()) {
            try {
                field.setAccessible(true);
                if (Unsafe.class.isAssignableFrom(field.getType())) {
                    return (Unsafe) field.get(Unsafe.class);
                }


            } catch (Exception ignore) {
                // Again just go for ignoring it 😀
            }
        }

        throw new NotFeelingUnsafeTodayException();
    }
}

根据操作码生成类:

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import unsafejavaworld.unsafejavaworld.UnsafeSupport;

import java.lang.reflect.Field;
import java.util.Map;

public class BytecodeInstantiationHack
        implements Opcodes {

    public static void main(String[] args) throws Exception {
        ClassWriter cw = new ClassWriter(0);

        // public final class Loader implements java.util.Map
        cw.visit(V1_6, ACC_PUBLIC + ACC_FINAL, "Loader", null,
                "java/lang/Object", new String[]{"java/util/Map"});

        // public Loader() {
        //   super();
        // }
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        mv.visitInsn(RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        // public java.lang.Object get(java.lang.Object) {}
        mv = cw.visitMethod(ACC_PUBLIC, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null);
        mv.visitCode();

        // new String bytecode (uninitialized instance of String)
        mv.visitTypeInsn(NEW, "java/lang/String");

        // Call to String constructor String(String)
        mv.visitInsn(DUP);
        mv.visitVarInsn(ALOAD, 1);
        mv.visitTypeInsn(CHECKCAST, "Ljava/lang/String;");
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "(Ljava/lang/String;)V");
        //

        /*/ Call to Object constructor Object()
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        /*/

        // return string variable
        mv.visitInsn(ARETURN);
        mv.visitMaxs(3, 3);
        mv.visitEnd();
        cw.visitEnd();

        // Get generated bytecode
        byte[] classData = cw.toByteArray();

        // Use Unsafe::defineClass to load the generated class
        ClassLoader classLoader = BytecodeInstantiationHack.class.getClassLoader();
        Class loaderClass = UnsafeSupport.UNSAFE
                .defineClass("Loader", classData, 0, classData.length, classLoader, null);

        // Instantiate our generated adapter class
        Map<Object, String> instantiationAdapter = (Map<Object, String>) loaderClass.newInstance();

        // Create new String using our adapter
        String foo = instantiationAdapter.get("Test");

        // Get accessor to the internal char[] field
        Field valueField = String.class.getDeclaredField("value");
        valueField.setAccessible(true);

        // Show content of the String instance
        System.out.println("Value before Field::set: " + valueField.get(foo));
        //System.out.println("Value before Field::set: " + foo);

        // Rewrite the value to "Something"->char[]
        valueField.set(foo, "Something".toCharArray());

        // Show content after changing the String instance
        System.out.println("Value after Field::set: " + valueField.get(foo));
        System.out.println("Value after Field::set: " + foo);
    }
}

不调用构造方法生成实例:

import unsafejavaworld.unsafejavaworld.UnsafeSupport;

import java.lang.reflect.Field;

public class UnsafeAllocateInstanceInstantiationHack {

    public static void main(String[] args) throws Exception {
        // Instantiate String without calling the constructor
        String foo = (String) UnsafeSupport.UNSAFE.allocateInstance(String.class);

        // Get accessor to the internal char[] field
        Field valueField = String.class.getDeclaredField("value");
        valueField.setAccessible(true);

        // Rewrite the value to "Something"->char[]
        valueField.set(foo, "Something".toCharArray());
        
        // Show content of the String instance
        System.out.println("Value before Field::set: " + valueField.get(foo));
        System.out.println("Value before Field::set: " + foo);

        // Show content after changing the String instance
        System.out.println("Value after Field::set: " + valueField.get(foo));
        System.out.println("Value before Field::set: " + foo);
    }
}

CAS 操作:

import unsafejavaworld.unsafejavaworld.UnsafeSupport;
import sun.nio.ch.DirectBuffer;

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class CompareAndSwap {

    public static void main(String[] args) throws Exception {
        // Create Filechannel
        FileChannel fc = new RandomAccessFile("/tmp/counters", "rw").getChannel();

        // Map file into memory
        MappedByteBuffer map = fc.map(FileChannel.MapMode.READ_WRITE, 0, 1024);

        // Get address of the mapped DirectByteBuffer
        long address = ((DirectBuffer) map).address();

        // Give me 1000 unique sequence numbers
        for (int i = 0; i < 100; i++) {
            long next = getAndIncrement(address);
            System.out.println(next);
        }

        // Close Filechannel
        fc.close();
    }

    private static long getAndIncrement(long address) {
        // Increment and return value
        return UnsafeSupport.UNSAFE.getAndAddLong(null, address, 1);
    }
}

不受控制的抛出异常:

import unsafejavaworld.unsafejavaworld.UnsafeSupport;

public class SneakyThrow {

    public static void main(String[] args){
        // Using sun.misc.Unsafe
        // unsafeThrow();

        // Using Thread.stop
        //threadStop();

        // Using Reflection
        // newInstance();

        // Using Generics
        // SneakyThrow.<RuntimeException> genericRethrow();
    }

    private static void unsafeThrow() {
        UnsafeSupport.UNSAFE.throwException(new Exception("Unsafe::throwException"));
    }

    private static void threadStop() {
        Thread.currentThread().stop(new Exception("Thread::stop"));
    }

    private static void newInstance() {
        Holder.exception = new Exception("Holder::newInstance");
        try {
            Holder.class.newInstance();
        } catch (InstantiationException e) {
        } catch (IllegalAccessException e) {
        }
    }

    private static <T extends Throwable> RuntimeException genericRethrow() throws T {
        throw (T) new Exception("Generic::rethrow");
    }

    static class Holder {
        public static Exception exception;

        public Holder() throws Exception {
            throw exception;
        }
    }
}

修改基本类型值:

import java.lang.reflect.Field;

public class ObviousImmutableInstances {

    public static void main(String[] args) throws Exception {
        // Creating Integer value object
        Integer i = 5;

        // Retrieving the value field inside Integer
        Field value = Integer.class.getDeclaredField("value");
        value.setAccessible(true);

        // Rewrite internal value to 6
        value.set(i, 6);

        // Sum up
        Integer sum = 3 + 2;
        System.out.println("3 + 2 = " + sum);

        // Compare
        Integer five = 5;
        Integer six = 6;
        System.out.println("5 equals 6 is " + five.equals(six));
    }
}


//3 + 2 = 6
//5 equals 6 is true

直接操作内存:

import unsafejavaworld.unsafejavaworld.UnsafeSupport;

import static org.junit.Assert.assertEquals;

public class SimpleOffheap {

    public static void main(String[] args) {
        // Allocate a native memory address
        long address = UnsafeSupport.UNSAFE.allocateMemory(1024);

        // Write 128 long values
        for (int i = 0; i < 1024; i += 8)
            UnsafeSupport.UNSAFE.putLong(address + i, i);

        // Read 128 long values and make sure they are the same
        for (int i = 0; i < 1024; i += 8) {
            long aLong = UnsafeSupport.UNSAFE.getLong(address + i);
            assertEquals(aLong, i);
            System.out.println(aLong);
        }

        // Free allocates memory
        UnsafeSupport.UNSAFE.freeMemory(address);
    }
}

Exploit 的使用

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import unsafejavaworld.unsafejavaworld.UnsafeSupport;

import java.lang.reflect.Field;
import java.util.Map;

public class ExploitingSecurityChecks {

    public static void main(String[] args) throws Exception {
        ClassWriter cw = new ClassWriter(0);

        // public final class sun.reflect.Exploit
        //            extends sun.reflect.MagicAccessorImpl
        //            implements java.util.Map
        cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, "sun/reflect/Exploit",
                null, "sun/reflect/MagicAccessorImpl", new String[] { "java/util/Map" });

        // public Exploit() {
        //   super();
        // }
        MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        mv.visitInsn(Opcodes.RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();

        // public java.lang.Object get(java.lang.Object) {}
        mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null);
        mv.visitCode();
        mv.visitVarInsn(Opcodes.ALOAD, 0);

        // new String(char[])
        mv.visitTypeInsn(Opcodes.NEW, "java/lang/String");
        mv.visitInsn(Opcodes.DUP);

        // Build package-private String constructor call
        // Parameter 1: char[]
        mv.visitVarInsn(Opcodes.ALOAD, 1);
        mv.visitTypeInsn(Opcodes.CHECKCAST, "[C");

        // Parameter 2: true
        mv.visitInsn(Opcodes.ICONST_1);

        // Call new String(char[], boolean)
        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/String", "<init>", "([CZ)V");
        mv.visitInsn(Opcodes.ARETURN);
        mv.visitMaxs(5, 5);
        mv.visitEnd();
        cw.visitEnd();

        // Get generated bytecode
        byte[] classData = cw.toByteArray();

        // Get classloader from sun.reflect.MagicAccessorImpl
        ClassLoader classLoader = Class.forName("sun.reflect.MagicAccessorImpl").getClassLoader();

        // Use Unsafe::defineClass to load the generated class
        Class loaderClass = UnsafeSupport.UNSAFE
                .defineClass("sun/reflect/Exploit", classData, 0, classData.length, classLoader, null);

        // Instantiate our generated adapter class
        Map<char[], String> instantiationAdapter = (Map<char[], String>) loaderClass.newInstance();

        // Create char[] to pass to String constructor
        char[] data = "Test".toCharArray();

        // Create new String using our adapter
        String foo = instantiationAdapter.get(data);

        // Show content of the String instance
        System.out.println("Value of foo: " + foo);

        // Get accessor to the internal char[] field
        Field valueField = String.class.getDeclaredField("value");
        valueField.setAccessible(true);

        // Proof the shared instance
        System.out.println("char[] is shared: " + (data == valueField.get(foo)));
    }
}

重写Hashcode

import unsafejavaworld.unsafejavaworld.UnsafeSupport;

public class ResettingHashCode {

    public static void main(String[] args) {
        UnsafeRandomInt rand = new UnsafeRandomInt();
        for (int i = 0; i < 10; i++) {
        	System.out.println(rand.next31());
            System.out.println(Integer.toHexString(rand.next31()));
        }

    }

    static class UnsafeRandomInt {
        public int next31() {
            UnsafeSupport.UNSAFE.putInt(this, 1L, 0);
            return hashCode();
        }
    }
}

修改父类信息:

import unsafejavaworld.unsafejavaworld.UnsafeSupport;

public class ReplaceSuperclass {

    public static void main(String[] args) throws Exception {
        // Reading the address of the String class
        long strClassAddress = normalize(UnsafeSupport.UNSAFE.getInt("", 8L));

        // Reading the address of the Integer class
        long intClassAddress = normalize(UnsafeSupport.UNSAFE.getInt(new Integer(0), 8L));

        // Write String class address as superclass to Integer class
        UnsafeSupport.UNSAFE.putAddress(intClassAddress + 32, strClassAddress);

        // Creating a new Integer object
        Integer intValue = new Integer(666);

        // Casting the Integer to it's now String superclass
        String value = (String) (Object) intValue;

        // Proof it works 🙂
        System.out.println("String (Integer) value is: " + value);

        Class<?> clazz = Integer.class;
        while (clazz != null) {
            System.out.println("Class: " + clazz);
            clazz = clazz.getSuperclass();
        }
    }

    private static long normalize(long value) {
        if (value >= 0) {
            return value;
        }
        return (~0L >>> 32) & value;
    }
}