1.build.gradle中添加asm的jar依赖
dependencies {
implementation files('libs/asm-9.2.jar')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation group: 'io.appium', name: 'java-client', version: '7.5.1'
}
2.一个测试的例子,主要是动态修改一个class文件Person.class,class的原始内容如下:
public class Person {
private boolean passAuth = false;
public Person() {
}
public boolean validation() {
System.out.println("我是超级大黑客");
return this.passAuth;
}
}
如果是正常的加载这个文件,进行validation方法的调用,那应该是输出文本“我是超级大黑客”,下面通过ASM进行修改。
3.修改的ASM的代码如下:
public class FirstAsmTest {
public static void main(String[] args) throws Exception {
// 生成二进制字节码
MyClassLoader cl = new MyClassLoader();
File file = new File("/Users/zhangqi59/AndroidStudioProjects/JavApplication/" +
"javaDemo/src/main/java/com/justalk/javademo/asm/Person.class");
// System.out.println(file.exists());
// System.out.println(file.getAbsolutePath());
Path p = Paths.get(file.getAbsolutePath());
// System.out.println(p.getFileName());
ClassReader cr = new ClassReader(Files.newInputStream(p));
ClassWriter cw = new ClassWriter(cr,Opcodes.ASM4);
ClassVisitor cv = new ChangeClassAdapter(cw);
cr.accept(cv,0);
byte[] data = cw.toByteArray();
Class<?> personClass = cl.defineClass("com.justalk.javademo.asm.Person", data);
Object person = personClass.newInstance();
Method validation = personClass.getMethod("validation");
validation.invoke(person);
FileOutputStream fos = new FileOutputStream("./Person.class");
fos.write(data);
System.out.println("-----------------------");
Person person1 = new Person();
boolean validation1 = person1.validation();
System.out.println("validation1:"+validation1);
}
private static byte[] generate() {
ClassWriter cw = new ClassWriter(0);
// 定义对象头:版本号、修饰符、全类名、签名、父类、实现的接口
cw.visit(Opcodes.V1_8, ACC_PUBLIC, "com/dadiyang/asm/HelloWorld",
null, "java/lang/Object", null);
// 添加方法:修饰符、方法名、描述符、签名、抛出的异常
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main",
"([Ljava/lang/String;)V", null, null);
// 执行指令:获取静态属性
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
// 加载常量 load constant
mv.visitLdcInsn("HelloWorld!");
// 调用方法
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
// 返回
mv.visitInsn(RETURN);
// 设置栈大小和局部变量表大小
mv.visitMaxs(2, 1);
// 方法结束
mv.visitEnd();
// 类完成
cw.visitEnd();
// 生成字节数组
return cw.toByteArray();
}
public static byte[] dump() throws Exception {
ClassWriter classWriter = new ClassWriter(0);
FieldVisitor fieldVisitor;
MethodVisitor methodVisitor;
AnnotationVisitor annotationVisitor0;
classWriter.visit(V1_7, ACC_PUBLIC | ACC_SUPER, "com/justalk/javademo/asm/Test", null, "java/lang/Object", null);
classWriter.visitSource("Test.java", null);
{
fieldVisitor = classWriter.visitField(ACC_PRIVATE, "num1", "I", null, null);
fieldVisitor.visitEnd();
}
{
fieldVisitor = classWriter.visitField(ACC_PUBLIC | ACC_STATIC, "NUM1", "I", null, null);
fieldVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(3, label0);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
Label label1 = new Label();
methodVisitor.visitLabel(label1);
methodVisitor.visitLineNumber(5, label1);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitInsn(ICONST_1);
methodVisitor.visitFieldInsn(PUTFIELD, "com/justalk/javademo/asm/Test", "num1", "I");
methodVisitor.visitInsn(RETURN);
Label label2 = new Label();
methodVisitor.visitLabel(label2);
methodVisitor.visitLocalVariable("this", "Lcom/justalk/javademo/asm/Test;", null, label0, label2, 0);
methodVisitor.visitMaxs(2, 1);
methodVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "func", "(II)I", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(8, label0);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ILOAD, 1);
methodVisitor.visitVarInsn(ILOAD, 2);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "com/justalk/javademo/asm/Test", "add", "(II)I", false);
methodVisitor.visitInsn(IRETURN);
Label label1 = new Label();
methodVisitor.visitLabel(label1);
methodVisitor.visitLocalVariable("this", "Lcom/justalk/javademo/asm/Test;", null, label0, label1, 0);
methodVisitor.visitLocalVariable("a", "I", null, label0, label1, 1);
methodVisitor.visitLocalVariable("b", "I", null, label0, label1, 2);
methodVisitor.visitMaxs(3, 3);
methodVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "add", "(II)I", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(11, label0);
methodVisitor.visitVarInsn(ILOAD, 1);
methodVisitor.visitVarInsn(ILOAD, 2);
methodVisitor.visitInsn(IADD);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, "com/justalk/javademo/asm/Test", "num1", "I");
methodVisitor.visitInsn(IADD);
methodVisitor.visitInsn(IRETURN);
Label label1 = new Label();
methodVisitor.visitLabel(label1);
methodVisitor.visitLocalVariable("this", "Lcom/justalk/javademo/asm/Test;", null, label0, label1, 0);
methodVisitor.visitLocalVariable("a", "I", null, label0, label1, 1);
methodVisitor.visitLocalVariable("b", "I", null, label0, label1, 2);
methodVisitor.visitMaxs(2, 3);
methodVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "sub", "(II)I", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(14, label0);
methodVisitor.visitVarInsn(ILOAD, 1);
methodVisitor.visitVarInsn(ILOAD, 2);
methodVisitor.visitInsn(ISUB);
methodVisitor.visitFieldInsn(GETSTATIC, "com/justalk/javademo/asm/Test", "NUM1", "I");
methodVisitor.visitInsn(ISUB);
methodVisitor.visitInsn(IRETURN);
Label label1 = new Label();
methodVisitor.visitLabel(label1);
methodVisitor.visitLocalVariable("this", "Lcom/justalk/javademo/asm/Test;", null, label0, label1, 0);
methodVisitor.visitLocalVariable("a", "I", null, label0, label1, 1);
methodVisitor.visitLocalVariable("b", "I", null, label0, label1, 2);
methodVisitor.visitMaxs(2, 3);
methodVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
methodVisitor.visitLabel(label0);
methodVisitor.visitLineNumber(6, label0);
methodVisitor.visitIntInsn(BIPUSH, 100);
methodVisitor.visitFieldInsn(PUTSTATIC, "com/justalk/javademo/asm/Test", "NUM1", "I");
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(1, 0);
methodVisitor.visitEnd();
}
classWriter.visitEnd();
return classWriter.toByteArray();
}
}
/**
* 自定义ClassLoader以支持加载字节数组形式的字节码
* @author dadiyang
*/
class MyClassLoader extends ClassLoader {
public Class<?> defineClass(String name, byte[] b) {
// ClassLoader是个抽象类,而ClassLoader.defineClass 方法是protected的
// 所以我们需要定义一个子类将这个方法暴露出来
return super.defineClass(name, b, 0, b.length);
}
}
class ChangeClassAdapter extends ClassVisitor {
public ChangeClassAdapter(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
// TODO Auto-generated constructor stub
}
public ChangeClassAdapter(ClassVisitor cv) {
super(Opcodes.ASM4);
this.cv = cv;
}
public ChangeClassAdapter(int asm7) {
// TODO Auto-generated constructor stub
super(asm7);
}
@Override
public MethodVisitor visitMethod(
int access,
String name,
String desc,
String signature,
String[] exceptions)
{
//这里mv实际最终拿到的是MethodWriter
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
if (cv != null && "validation".equals(name)) {
//validation方法重新构造
// mv.visitCode();
// mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
// mv.visitLdcInsn("i hacker");
// mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
// mv.visitInsn(Opcodes.ICONST_1);
// mv.visitInsn(Opcodes.IRETURN);
// mv.visitMaxs(2, 1);
// mv.visitEnd();
mv.visitCode();
Label label0 = new Label();
mv.visitLabel(label0);
mv.visitLineNumber(8, label0);
mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
mv.visitLdcInsn("我是超级大黑客");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
Label label1 = new Label();
mv.visitLabel(label1);
mv.visitLineNumber(14, label1);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, "com/justalk/javademo/asm/Person", "passAuth", "Z");
mv.visitInsn(IRETURN);
Label label2 = new Label();
mv.visitLabel(label2);
mv.visitLocalVariable("this", "Lcom/justalk/javademo/asm/Person;", null, label0, label2, 0);
mv.visitMaxs(2, 1);
mv.visitEnd();
//注意这里的返回值;只有返回null,才意味着修改是生效的;具体可以看一下源码,方法里直接有说明;
return null;
}
return mv;
}
}