Java asm修改class信息
[TOC]
1 asm中提供了ClassReader和ClassVisitor用来获取class信息,如果要修改class信息就要用到ClassWriter
1.1 ClassWriter的构造方法
1public ClassWriter(final int flags) 2public ClassVisitor(final int api, final ClassVisitor cv)
2 BusIndex.class反编译后的代码为
1public final class BusIndex extends a { 2 public BusIndex() { 3 } 4 5 public d generateSubscribeMethodIndex(Object var1) { 6 d var2 = new d(); 7 Class var3 = var1.getClass(); 8 Class var4 = null; 9 Class var5 = null; 10 String var6 = null; 11 if(var3 == MainActivity.class) { 12 var4 = com.sample.app.MainActivity.a.class; 13 var5 = com.a.a.d.a.class; 14 var6 = "showMessage"; 15 var2.a(var6, var4, var5, new com.eventbus.gen.a(this, var1, var6, var4, var5)); 16 var4 = com.sample.app.MainActivity.a.class; 17 var5 = com.a.a.d.a.class; 18 var6 = "showMessagedd"; 19 var2.a(var6, var4, var5, new b(this, var1, var6, var4, var5)); 20 return var2; 21 } else if(var3 == Main2Activity.class) { 22 var4 = com.sample.app.MainActivity.a.class; 23 var5 = com.a.a.d.a.class; 24 var6 = "showMessage"; 25 var2.a(var6, var4, var5, new c(this, var1, var6, var4, var5)); 26 var4 = com.sample.app.MainActivity.a.class; 27 var5 = com.a.a.d.a.class; 28 var6 = "showMessagedd"; 29 var2.a(var6, var4, var5, new com.eventbus.gen.d(this, var1, var6, var4, var5)); 30 return var2; 31 } else { 32 return var2; 33 } 34 } 35}
3 使用asm将generateSubscribeMethodIndex方法中String变量的值都加上前缀
3.1 首先加载BusIndex.class
ClassReader classReader = new ClassReader(BusIndex.class.getName());
3.2 使用ClassReader的accept方法查找generateSubscribeMethodIndex方法
如果有多个generateSubscribeMethodIndex重载方法,查找方法时必须验证方法的 desc signature等其他参数
1ClassReader classReader = new ClassReader(BusIndex.class.getName()); 2classReader.accept(new ClassVisitorAdapter(null), 0); 3 4public class ClassVisitorAdapter extends ClassVisitor { 5 6 public ClassVisitorAdapter(ClassWriter classWriter) { 7 super(Opcodes.ASM6, classWriter); 8 } 9 10 @Override 11 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 12 MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions); 13 if (name.equals("generateSubscribeMethodIndex")) { 14 //methodVisitor = new MethodVisitorAdapter(methodVisitor); 15 return methodVisitor; 16 } 17 18 return methodVisitor; 19 } 20}
3.3 使用自定义MethodVisitor遍历generateSubscribeMethodIndex方法的具体信息
1public class ClassVisitorAdapter extends ClassVisitor { 2 3 public ClassVisitorAdapter(ClassWriter classWriter) { 4 super(Opcodes.ASM6, classWriter); 5 } 6 7 @Override 8 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 9 MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions); 10 if (name.equals("generateSubscribeMethodIndex")) { 11 methodVisitor = new MethodVisitorAdapter(methodVisitor); 12 return methodVisitor; 13 } 14 15 return methodVisitor; 16 } 17} 18 19public class MethodVisitorAdapter extends MethodVisitor { 20 public MethodVisitorAdapter(MethodVisitor methodVisitor) { 21 super(Opcodes.ASM6, methodVisitor); 22 } 23 24 25 @Override 26 public void visitCode() { 27 super.visitCode(); 28 System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); 29 30 } 31 32 33 @Override 34 public void visitEnd() { 35 super.visitEnd(); 36 System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()); 37 38 System.out.println(csts); 39 } 40 41 42 @Override 43 public void visitInsn(int opcode) { 44 super.visitInsn(opcode); 45 System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName() + ", opcode:" + Optcodes.options.get(opcode)); 46 47 } 48 49 50 @Override 51 public void visitJumpInsn(int opcode, Label label) { 52 super.visitJumpInsn(opcode, label); 53 System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName() + ", opcode:" + Optcodes.options.get(opcode)); 54 } 55 56 @Override 57 public void visitLdcInsn(Object cst) { 58 System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName() + ", cst:" + cst); 59 60 } 61 62 @Override 63 public void visitMaxs(int maxStack, int maxLocals) { 64 System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName() + " ,maxstack:" + maxStack); 65 66 super.visitMaxs(maxStack, maxLocals); 67 } 68 69}
3.4 运行查看输出结果
1visitCode 2visitInsn, opcode:DUP 3visitInsn, opcode:ACONST_NULL 4visitInsn, opcode:ACONST_NULL 5visitInsn, opcode:ACONST_NULL 6visitLdcInsn, cst:Lcom/sample/app/MainActivity; 7visitJumpInsn, opcode:IF_ACMPNE 8visitLdcInsn, cst:Lcom/sample/app/MainActivity$a; 9visitLdcInsn, cst:Lcom/a/a/d/a; 10visitLdcInsn, cst:showMessage 11visitInsn, opcode:DUP 12visitLdcInsn, cst:Lcom/sample/app/MainActivity$a; 13visitLdcInsn, cst:Lcom/a/a/d/a; 14visitLdcInsn, cst:showMessagedd 15visitInsn, opcode:DUP 16visitInsn, opcode:ARETURN 17visitLdcInsn, cst:Lcom/sample/app/Main2Activity; 18visitJumpInsn, opcode:IF_ACMPNE 19visitLdcInsn, cst:Lcom/sample/app/MainActivity$a; 20visitLdcInsn, cst:Lcom/a/a/d/a; 21visitLdcInsn, cst:showMessage 22visitInsn, opcode:DUP 23visitLdcInsn, cst:Lcom/sample/app/MainActivity$a; 24visitLdcInsn, cst:Lcom/a/a/d/a; 25visitLdcInsn, cst:showMessagedd 26visitInsn, opcode:DUP 27visitInsn, opcode:ARETURN 28visitInsn, opcode:ARETURN 29visitMaxs ,maxstack:11 30visitEnd
3.5 输出的结果中可以看到visitLdcInsn 指令给变量的赋值操作
1visitLdcInsn, cst:Lcom/sample/app/MainActivity$a; 2visitLdcInsn, cst:Lcom/a/a/d/a; 3visitLdcInsn, cst:showMessage 4 5visitLdcInsn, cst:Lcom/sample/app/MainActivity$a; 6visitLdcInsn, cst:Lcom/a/a/d/a; 7visitLdcInsn, cst:showMessagedd 8 9visitLdcInsn, cst:Lcom/sample/app/Main2Activity; 10 11visitLdcInsn, cst:Lcom/sample/app/MainActivity$a; 12visitLdcInsn, cst:Lcom/a/a/d/a; 13visitLdcInsn, cst:showMessage 14 15visitLdcInsn, cst:Lcom/sample/app/MainActivity$a; 16visitLdcInsn, cst:Lcom/a/a/d/a; 17visitLdcInsn, cst:showMessagedd 18
3.6 MethodVisitorAdapter的visitLdcInsn方法中获取cst的类型
1if(cst instanceof Type){ 2 System.out.println("cst value type:"+((Type)cst).getClassName()); 3}else { 4 System.out.println("cst value type:"+cst.getClass()); 5}
3.7 在MethodVisitorAdapter的visitLdcInsn方法中给showMessage开头并且为String类型的cst加上前缀modify,然后通过父类的visitLdcInsn写回class
1if (cst instanceof String) { 2 if(cst != null && cst.toString().startsWith("showMessage")){ 3 super.visitLdcInsn("modify"+cst); 4 }else{ 5 super.visitLdcInsn(cst); 6 } 7} else { 8 super.visitLdcInsn(cst); 9}
3.8 ClassReader遍历class信息时传入ClassWriter,并输出修改后的class
1ClassReader classReader = new ClassReader(BusIndex.class.getName()); 2ClassWriter classWriter = new ClassWriter(0); 3classReader.accept(new ClassVisitorAdapter(classWriter), 0); 4System.out.println("修改后的字节码:\n"+new String(classWriter.toByteArray()));
3.9 输出结果
1修改后的字节码: 2���� 3 6 com/eventbus/gen/BusIndex com/a/a/c/a 3SourceFile com/eventbus/gen/d com/eventbus/gen/c com/eventbus/gen/b 4 com/eventbus/gen/a com/sample/app/MainActivity$a com/sample/app/MainActivity a <init> ()V 5 generateSubscribeMethodIndex !(Ljava/lang/Object;)Lcom/a/a/c/d; com/a/a/c/d 6 java/lang/Object getClass ()Ljava/lang/Class; 7 com/a/a/d/a " modifyshowMessage $ d(Lcom/eventbus/gen/BusIndex;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)V & 8 ' D(Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Lcom/a/a/b/a;)V ) 9 * modifyshowMessagedd , 10 ' com/sample/app/Main2Activity/ 11 '
3.10 将修改后的class写入文件
1Files.write(Paths.get(System.getProperty("user.dir")+"/BusIndex.class"), classWriter.toByteArray()); 2System.out.println("修改后的字节码成功保存到:\n"+Paths.get(System.getProperty("user.dir")+"/BusIndex.class"));
3.11 反编译生成的BusIndex.class
1public final class BusIndex extends a { 2 public BusIndex() { 3 } 4 5 public d generateSubscribeMethodIndex(Object var1) { 6 d var2 = new d(); 7 Class var3 = var1.getClass(); 8 Class var4 = null; 9 Class var5 = null; 10 String var6 = null; 11 if(var3 == MainActivity.class) { 12 var4 = com.sample.app.MainActivity.a.class; 13 var5 = com.a.a.d.a.class; 14 var6 = "modifyshowMessage"; 15 var2.a(var6, var4, var5, new com.eventbus.gen.a(this, var1, var6, var4, var5)); 16 var4 = com.sample.app.MainActivity.a.class; 17 var5 = com.a.a.d.a.class; 18 var6 = "showMessagedd"; 19 var2.a(var6, var4, var5, new b(this, var1, var6, var4, var5)); 20 return var2; 21 } else if(var3 == Main2Activity.class) { 22 var4 = com.sample.app.MainActivity.a.class; 23 var5 = com.a.a.d.a.class; 24 var6 = "modifyshowMessage"; 25 var2.a(var6, var4, var5, new c(this, var1, var6, var4, var5)); 26 var4 = com.sample.app.MainActivity.a.class; 27 var5 = com.a.a.d.a.class; 28 var6 = "showMessagedd"; 29 var2.a(var6, var4, var5, new com.eventbus.gen.d(this, var1, var6, var4, var5)); 30 return var2; 31 } else { 32 return var2; 33 } 34 } 35}