我正在尝试学习字节码检测的java asm框架,但却无法找到足够的文档或教程。
我研究了ClassReader
,ClassWriter
和ClassVisitor
以及一些更相似的API,但不清楚如何实现这些API以及如何编写相应的适配器。
假设我有一个HelloWorld java类。
public class HelloWorld {
public static void main(String[] args) {
//some code.....
}
}
现在我想插入一个变量" int i = 10;"在字节码中。请告诉我应该写什么适配器/程序。
提前致谢!
答案 0 :(得分:4)
以下是一种向类添加其他字段的方法,例如" int i = 10;"。 假设您使用javaagent执行检测: 1)使用以下作为java代理的premain类
import java.lang.instrument.Instrumentation;
public class SimpleAgent {
public static void premain(String agentArgs, Instrumentation inst) {
ClassTransformer transformer = new ClassTransformer();
inst.addTransformer(transformer);
}
}
2)addTransformer调用ClassTransformer类的transform方法,该方法定义如下
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
public class ClassTransformer implements ClassFileTransformer{
public byte[] transform(ClassLoader loader,
String className,
Class classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] b)
throws IllegalClassFormatException {
try
{
ClassReader cr=new ClassReader(b);
ClassWriter cw = new ClassWriter(cr,ClassWriter.COMPUTE_MAXS);
AddField cp = new AddField(cw);
cr.accept(cp,0);
return cw.toByteArray();
}
catch(Exception e)
{
System.out.println(e);
}
return b;
}
}
3)最后AddField如下是ClassVisitor,它负责向类中添加一个新字段
import static org.objectweb.asm.Opcodes.ASM4;
import org.objectweb.asm.ClassVisitor;
class AddField extends ClassVisitor{
static String className;
static String methName, descrip;
public AddField(ClassVisitor cv) {
super(ASM4, cv);
}
@Override
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
className = name;
cv.visit(version, access, name, signature, superName, interfaces);
}
public void visitEnd() {
cv.visitField(0, "i", "I", null , new Integer(10));
cv.visitEnd();
}
}
4。 ** NEW EDIT **用于将变量添加到方法中。变量必须存储到临时变量中,然后才能使用。以下适配器可用于此目的(查看onMethodEnter):
import static org.objectweb.asm.Opcodes.ASM4;
import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.AdviceAdapter;
public class MethodAdapter extends ClassVisitor {
public MethodAdapter(ClassVisitor cv) {
super(ASM4, cv);
}
@Override
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
cv.visit(version, access, name, signature, superName, interfaces);
}
public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions) {
MethodVisitor mv;
mv = cv.visitMethod(access, name, desc, signature, exceptions);
mv = new AddVariableAdapter(access, name, desc, mv);
return mv;
}
public void visitEnd() {
cv.visitEnd();
}
public class AddVariableAdapter extends AdviceAdapter{
public AddCallAdapter(int access, String name, String desc,
MethodVisitor mv) {
super(ASM4, mv, access, name, desc);
}
protected void onMethodEnter() {
mv.visitIntInsn(BIPUSH, 10); // pushes the number 10 on to the stack
mv.visitVarInsn(ISTORE, 1); // pops the top of the stack into a local variable indexed by 1
/* code to print the local variable
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitVarInsn(ILOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V");*/
}
}
}
答案 1 :(得分:1)
了解如何使用ASM的一个好方法是运行ASMifier工具。
如果您只想知道某些语言构造函数(如变量初始值设定项)是如何转换为字节码的,那么创建一个简单的Java类,编译它,找到它的.class
文件并在其上运行javap
可能会有所帮助。或者用IDE打开它。