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