邪恶的 Java 攻击反射

即使在 JDK 默认库中,Reflection API 也可用于更改私有和最终字段的值。这可以用来操作一些众所周知的类的行为,我们将会看到。

什么是不可能的

让我们先开始,唯一的限制意味着我们不能用 Reflection 改变的唯一字段。这就是 Java SecurityManager。它在 java.lang.System 中声明为

private static volatile SecurityManager security = null;

但是如果我们运行此代码,它将不会在 System 类中列出

for(Field f : System.class.getDeclaredFields())
    System.out.println(f);

这是因为 sun.reflect.Reflection 中的 fieldFilterMap 保存了地图本身和 System.class 中的安全字段,并保护它们不受任何反射访问。所以我们无法停用 SecurityManager

疯狂的弦乐

每个 Java String 由 JVM 表示为 String 类的实例。但是,在某些情况下,JVM 通过对字符串使用相同的实例来节省堆空间。这种情况发生在字符串文字中,也适用于通过调用 String.intern()实习的字符串。因此,如果你的代码中多次使用 hello,则它始终是相同的对象实例。

字符串应该是不可变的,但可以使用邪恶反射来改变它们。下面的示例显示了我们如何通过替换其 value 字段来更改 String 中的字符。

public class CrazyStrings {
    static {
        try {
            Field f = String.class.getDeclaredField("value");
            f.setAccessible(true);
            f.set("hello", "you stink!".toCharArray());
        } catch (Exception e) {
        }
    }
    public static void main(String args[])  {
        System.out.println("hello");
    }
}

所以这段代码将打印出“你发臭!”

1 = 42

Integer 类可以使用相同的想法

public class CrazyMath {
    static {
        try {
            Field value = Integer.class.getDeclaredField("value");    
            value.setAccessible(true);          
            value.setInt(Integer.valueOf(1), 42);      
        } catch (Exception e) {
        }
    }
    public static void main(String args[])  {
        System.out.println(Integer.valueOf(1));
    }
}

一切都是真的

根据这个 stackoverflow 帖子, 我们可以使用反射做一些非常邪恶的事情。

public class Evil {    
    static {
        try {
            Field field = Boolean.class.getField("FALSE");
            field.setAccessible(true);
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
            field.set(null, true);
        } catch (Exception e) {
        }
    }
    public static void main(String args[]){
        System.out.format("Everything is %s", false);
    }
}

请注意,我们在这里所做的将导致 JVM 以无法解释的方式运行。这非常危险。