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