使用代理修改類
首先,確保正在使用的代理在 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;
}