使用代理修改類

首先,確保正在使用的代理在 Manifest.mf 中具有以下屬性:

Can-Redefine-Classes: true
Can-Retransform-Classes: true

啟動 Java 代理將允許代理訪問類 Instrumentation。使用 Instrumentation,你可以呼叫 addTransformer(ClassFileTransformer 轉換器) 。ClassFileTransformers 將允許你重寫類的位元組。該類只有一個方法,它提供加​​載類的 ClassLoader,類的名稱,它的 java.lang.Class 例項,它的 ProtectionDomain,最後是類本身的位元組。

它看起來像這樣:

byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, 
          ProtectionDomain protectionDomain, byte[] classfileBuffer)

純粹從位元組修改類可能需要很長時間。為了解決這個問題,有一些庫可用於將類位元組轉換為更有用的位元組。

在這個例子中,我將使用 ASM,但其他​​替代方案,如 Javassist 和 BCEL 具有類似的功能。

ClassNode getNode(byte[] bytes) {
    // Create a ClassReader that will parse the byte array into a ClassNode
    ClassReader cr = new ClassReader(bytes);
    ClassNode cn = new ClassNode();
    try {
        // This populates the ClassNode
        cr.accept(cn, ClassReader.EXPAND_FRAMES);
        cr = null;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return cn;
}

從這裡可以對 ClassNode 物件進行更改。這使得更改欄位/方法訪問非常容易。再加上 ASM 的 Tree API,修改方法的位元組碼是輕而易舉的。

編輯完成後,你可以使用以下方法將 ClassNode 轉換回位元組,並在 transform 方法中返回它們 :

public static byte[] getNodeBytes(ClassNode cn, boolean useMaxs) {
    ClassWriter cw = new ClassWriter(useMaxs ? ClassWriter.COMPUTE_MAXS : ClassWriter.COMPUTE_FRAMES);
    cn.accept(cw);
    byte[] b = cw.toByteArray();
    return b;
}