邪惡的 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 以無法解釋的方式執行。這非常危險。