邪恶的 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 以无法解释的方式运行。这非常危险。