导读:
用ASM写的Hello World。在网上搜索ASM有关的文章,最后居然又找回Matrix。。汗
ASM2.0字节码框架介绍
http://www.matrix.org.cn/resource/article/2006-02-20/ASM+Bytecode+Framework_44220.html
package my; import java.lang.reflect.Method; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class Hello { @SuppressWarnings("unchecked") public static void main(String[] args) throws Exception { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); // 类访问开始:必须 cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, "my/Foo", null, "java/lang/Object", null); // 至少提供一个构造函数 MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "", "()V", null, null); // 代码开始:必须 mv.visitCode(); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "", "()V"); mv.visitInsn(Opcodes.RETURN); // 计算栈和局部变量最大空间:必须 mv.visitMaxs(0, 0); // 代码结束:必须 mv.visitEnd(); mv = cw.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); mv.visitCode(); mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn("Hello World!"); mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V"); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); // 类结束:必须 cw.visitEnd(); final byte[] bs = cw.toByteArray(); Class clazz = new ClassLoader() { protected Class findClass(String name) throws ClassNotFoundException { return defineClass(name, bs, 0, bs.length); } }.loadClass("my.Foo"); Method method = clazz.getMethod("main", new Class[] { String[].class }); // 数组参数的方法,反射调用方式看起来比较古怪 method.invoke(null, (Object) new String[0]); for (int i = 0; i < bs.length; i++) System.out.printf("%d:/t%02X/t%c/n", i, bs[i], (char) bs[i]); // OutputStream out = new FileOutputStream("d:/my/Foo.class"); // out.write(bs); // out.close(); } }
借助ASM写了一个Aqua Data Studio 6.0的破解:
做法很简单:
1、找到判断license的方法,修改代码使总返回true
2、将1个license线程kill掉。
将jar拷到安装目录,修改datastudio.bat文件的最后一行为:
java -javaagent:ads.crack.jar -cp "./lib/ads.jar;%ADS_PATH%" com.aquafold.datastudio.DataStudio
由于论坛不支持jar文件上传,将文件扩展名改为ads.crack.jar即可。
有时候,如果想要得到程序中某个Class的所有实例,也可以用asm修改代码得到:
package my; import java.io.FileOutputStream; import java.io.OutputStream; import java.lang.reflect.Field; import java.net.URL; import java.util.List; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; public class HelloModifyASM { public static void main(String[] args) throws Exception { URL url = HelloModifyASM.class.getResource("Foo.class"); ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassVisitor cv = new ClassAdapter(cw) { public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { super.visit(version, access, name, signature, superName, interfaces); // 添加字段:public static List _my_instances; super.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "_my_instances", "Ljava/util/List;", null, null); // 添加静态的初始化块 MethodVisitor mv = super.visitMethod(Opcodes.ACC_STATIC, "", "()V", null,null); mv.visitCode(); mv.visitTypeInsn(Opcodes.NEW, "java/util/ArrayList"); mv.visitInsn(Opcodes.DUP); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "", "()V"); mv.visitFieldInsn(Opcodes.PUTSTATIC, "my/Foo", "_my_instances", "Ljava/util/List;"); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); } public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); // 修改无参的构造函数: if (!"".equals(name) || !"()V".equals(desc)) return mv; return new MethodAdapter(mv) { public void visitInsn(int opcode) { if (opcode == Opcodes.RETURN) { visitFieldInsn(Opcodes.GETSTATIC, "my/Foo", "_my_instances", "Ljava/util/List;"); visitVarInsn(Opcodes.ALOAD, 0); visitMethodInsn(Opcodes.INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z"); } super.visitInsn(opcode); } }; } }; ClassReader cr = new ClassReader(url.openStream()); cr.accept(cv, ClassReader.SKIP_DEBUG); final byte[] bs = cw.toByteArray(); OutputStream out = new FileOutputStream("d:/my/Foo.class"); out.write(bs); out.close(); Class clazz = new ClassLoader(null) { public Class findClass(String name) throws ClassNotFoundException { if (!"my.Foo".equals(name)) return ClassLoader.getSystemClassLoader().loadClass(name); return defineClass(name, bs, 0, bs.length); } }.loadClass("my.Foo"); clazz.newInstance(); clazz.newInstance(); Field field = clazz.getField("_my_instances"); List instances = (List) field.get(null); System.out.println(instances.size()); for (Object obj : instances) { System.out.println(obj); } } }
问题:如果原始类有、或者没有静态初始化块,处理方法就是不同的。
想了一下,也许可以在visitMethod方法中设置标志,再在visitEnd方法中进行补充处理(针对没有的情况)。
另外,如果有多个构造函数,怎样保证插入的代码不会重复执行呢?
甚至,想要在原代码中插入语句,插入位置的寻找也比较费尽,(需要找到不同的RETURN语句的字节码)
本文转自
http://www.matrix.org.cn/thread.shtml?topicId=edd2d10c-a79a-11db-8440-755941c7293d&forumId=1