ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。
其官网:http://asm.ow2.org/。可以下载到最新版的ASM,同时也有一个user guid。
下面介绍ASM的使用。(注意:对于包名,我在贴代码的时候,没有处理,需要自己根据自己的包名修改处理)。
关于java字节码指令以及ASM的一些理论,请看ASM-java字节码控制框架。
生成字节码:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
/**
* 生成一个如下的类:=================================================================
* public interface com.asmx.geners.Comparable extends com.asmx.geners.Mesurable
* {public static final int LESS;
*
* public static final int EQUAL;
*
* public static final int GREATER;
*
* public abstract int compareTo(java.lang.Object); }
*
* @author MiXian
*
*/
public class HelloWorld extends ClassLoader implements Opcodes {
public static void main(String[] args) throws IOException {
// 生成一个类只需要ClassWriter组件即可,0,COMPUTE_MAXS(1),COMPUTE_FRAMES(2)
ClassWriter cw = new ClassWriter(1);
// 通过visit方法确定类的头部信息
// visit{版本,访问性,名称,签名(不是泛型的时候,用null),父类,接口}
cw.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC, "CompareResult", null, "java/lang/Object", null);
// 定义类的属性
// visitField{访问性,名称,类型,签名(不是泛型的时候,用null),值}
cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "LESS", "I", null, new Integer(-1)).visitEnd();
cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "EQUAL", "I", null, new Integer(0))
.visitEnd();
cw.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "GREATER", "I", null, new Integer(1))
.visitEnd();
// 定义类的方法
// visitMethod{访问性,名称,类型,签名(不是泛型的时候,用null),异常}
// cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT,
// "compareTo", "(Ljava/lang/Object;)I", null, null)
// .visitEnd();
cw.visitEnd(); // 使cw类已经完成
// 将cw转换成字节数组写到文件里面去
byte[] data = cw.toByteArray();
FileOutputStream fout = null;
try {
File file = new File("D://CompareResult.class");
fout = new FileOutputStream(file);
fout.write(data);
} finally {
if (fout != null) {
fout.close();
}
}
}
}
修改字节码:
根据方法插入字节码:
原来的类:
public class OriginalMe {
private int i;
void fun() throws InterruptedException {
Thread.sleep(100);
}
}
准备生成的类:
public class OriginalMe {
public static long timer;
private int i;
public void fun() throws InterruptedException{
timer -= System.currentTimeMillis();
Thread.sleep(100);
timer += System.currentTimeMillis();
}
}
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* 为类增加一个static long time域。用来记录这个类执行方法用时。 这里,没有对多线程做处理。
*/
public class AddTimeClassVisitor extends ClassVisitor {
private boolean isInterface;
private String clazz;
public AddTimeClassVisitor(ClassWriter cw) {
super(Opcodes.ASM5, cw);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// 这里处理了包名,一定注意,类的全名包括包名,要符合java规范。包名的根就是source
cv.visit(version, access, name.substring(name.lastIndexOf("/") + 1), signature, superName, interfaces);
// cv.visit(version, access, name, signature, superName, interfaces);
isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
clazz = name;
if (!isInterface) {
// 添加字段
cv.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "timer", "J", null, null);
}
}
// 配套的visitMethod方法
// 要这个字段发挥作用,就要在class访问方法的时候,加入时间统计,所以需要修改其方法。
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
if (!"<clinit>".equals(name) && !name.equals("<init>") && !isInterface && mv != null) {
mv = new AddTimeHelperClassVisitor(clazz, mv);
}
return mv;
}
// 要这个字段发挥作用,就要在class访问方法的时候,加入时间统计,所以需要修改其方法。
private static class AddTimeHelperClassVisitor extends MethodVisitor {
private String clazz;
public AddTimeHelperClassVisitor(String name, MethodVisitor mv) {
super(Opcodes.ASM5, mv);
this.clazz = name;
}
// 访问方法,非抽象方法,就是具体实现的代码。即方法刚刚开始访问的时候。
@Override
public void visitCode() {
mv.visitCode();
// 访问域指令{域的操作码,域所属的类,域名称,类型}
mv.visitFieldInsn(Opcodes.GETSTATIC, clazz, "timer", "J");
// 最后一个参数表示这个方法是不是接口
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
mv.visitInsn(Opcodes.LSUB);
mv.visitFieldInsn(Opcodes.PUTSTATIC, clazz, "timer", "J");
}
// 操作指令地方,检查每条指令,对这些指令进行修改。
@Override
public void visitInsn(int opcode) {
if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) {
mv.visitFieldInsn(Opcodes.GETSTATIC, clazz, "timer", "J");
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
mv.visitInsn(Opcodes.LADD);
mv.visitFieldInsn(Opcodes.PUTSTATIC, clazz, "timer", "J");
}
mv.visitInsn(opcode);
}
}
}
调用方法:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
public class AddRun {
public static void main(String[] args) throws IOException {
FileOutputStream fout = null;
try {
ClassReader cr = new ClassReader("advanced/asms/modifies/OriginalMe");
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor visitor = new AddTimeClassVisitor(cw);
// 使给定的访问者访问Java类的ClassReader
cr.accept(visitor, ClassReader.SKIP_DEBUG);
byte[] data = cw.toByteArray();
File file = new File("D:\\OriginalMe.class");
fout = new FileOutputStream(file);
fout.write(data);
System.out.println("success!");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fout != null) {
fout.close();
}
}
}
}
根据指令插入字节码:
原来的类:
public class OriginField {
private static int count = 0;
static {
another();
}
private static void another() {
++count;
}
private String str;
private int inT;
private char[] charr;
private Object obj;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public int getInT() {
return inT;
}
public void setInT(int inT) {
this.inT = inT;
}
public char getCharr(int i) {
return charr[i];
}
public void setCharr(int i, char c) {
charr[i] = c;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public static int getCount() {
return count;
}
public OriginField(String str, int inT, char[] charr, Object obj) {
super();
this.str = str;
this.inT = inT;
this.charr = charr;
this.obj = obj;
}
}
使用的工具:
package advanced.asms.modifies.fields;
public class ActionUtils {
public static void fieldChange(Object instance, String field, Object oldVaule, Object newValue) {
System.out.println("object" + instance + "|field:" + field + "{old:" + oldVaule + ";new:" + newValue + "}");
}
public static void arrayChange(Object instance, int i, Object value) {
System.out.println("object" + instance + ":array[index" + i + "::{new:" + value + "}]");
}
}
新生成的类:
public class OriginField {
private static int count;
private String str;
private int inT;
private char[] charr;
private Object obj;
static {
advanced.asms.modifies.fields.OriginField.count = 0;
advanced.asms.modifies.fields.OriginField.another();
}
private static void another() {
int i = advanced.asms.modifies.fields.OriginField.count;
advanced.asms.modifies.fields.OriginField.count += 1;
Object localObject;
advanced.asms.modifies.fields.ActionUtils.fieldChange(localObject, "count", i,
advanced.asms.modifies.fields.OriginField.count);
}
public String getStr() {
return this.str;
}
public void setStr(String paramString) {
String str1 = this.str;
this.str = paramString;
advanced.asms.modifies.fields.ActionUtils.fieldChange(this, "str", str1, paramString);
}
public int getInT() {
return this.inT;
}
public void setInT(int paramInt) {
int i = this.inT;
this.inT = paramInt;
advanced.asms.modifies.fields.ActionUtils.fieldChange(this, "inT", i, paramInt);
}
public char getCharr(int paramInt) {
return this.charr[paramInt];
}
public void setCharr(int paramInt, char paramChar) {
this.charr[paramInt] = paramChar;
advanced.asms.modifies.fields.ActionUtils.arrayChange(this, paramInt, paramChar);
}
public Object getObj() {
return this.obj;
}
public void setObj(Object paramObject) {
Object localObject = this.obj;
this.obj = paramObject;
advanced.asms.modifies.fields.ActionUtils.fieldChange(this, "obj", localObject, paramObject);
}
public static int getCount() {
return advanced.asms.modifies.fields.OriginField.count;
}
public OriginField(String paramString, int paramInt, char[] paramArrayOfChar, Object paramObject) {
this.str = paramString;
this.inT = paramInt;
this.charr = paramArrayOfChar;
this.obj = paramObject;
}
}
字节码修改:
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
/**
* 就是检测到域的变化的指令的时候,采取行动。
*/
public class FieldChangeSimpleClassVisitor extends ClassVisitor implements Opcodes {
private static final String METHODOWNER = "advanced.asms.modifies.fields.ActionUtils";
private static final String FIELDCHANGEDESC = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V";
private static final String FIELDCHANGE = "fieldChange";
private static final String ARRAYCHANGE = "arrayChange";
private static final String ARRAYCHANGEDESC = "(Ljava/lang/Object;ILjava/lang/Object;)V";
public FieldChangeSimpleClassVisitor(ClassWriter cw) {
super(Opcodes.ASM5, cw);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
cv.visit(version, access, name.substring(name.lastIndexOf("/") + 1), signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
if ("<clinit>".equals(name) || "<init>".equals(name)) {
return mv;
}
return new Helper(api, mv);
};
private class Helper extends MethodVisitor implements Opcodes {
public Helper(int api, MethodVisitor mv) {
super(api, mv);
}
/**
* 数组变动的时候。
*/
@Override
public void visitInsn(int opCode) {
mv.visitInsn(opCode);
if (isArrayOperatorInstrumentationReq(opCode)) {
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ILOAD, 1);
mv.visitVarInsn(ILOAD, 2);
mv.visitMethodInsn(INVOKESTATIC, METHODOWNER, ARRAYCHANGE, ARRAYCHANGEDESC, false);
}
}
/**
* 域变动的指令
*/
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
// 指令处理,先取出旧值,再写入新值,否则会影响原来指令的处理流程
// 插入指令,这里把旧值保存下来
if (opcode == PUTFIELD || opcode == PUTSTATIC) {
storeOldFieldValue(opcode == PUTSTATIC, owner, name, desc);
}
// 实际处理,这里是原来的指令
mv.visitFieldInsn(opcode, owner, name, desc);
// 将新值和旧值一起调用处理
if (opcode == PUTFIELD) {
triggerFieldAction(owner, name, desc, false);
} else if (opcode == PUTSTATIC) {
triggerFieldAction(owner, name, desc, true);
}
}
private void triggerFieldAction(String owner, String name, String desc, boolean isStatic) {
mv.visitVarInsn(ALOAD, 0);
mv.visitLdcInsn(name);
mv.visitVarInsn(ALOAD, 2);
if (isStatic) {
mv.visitFieldInsn(GETSTATIC, owner, name, desc);
} else {
mv.visitVarInsn(ALOAD, 1);
}
mv.visitMethodInsn(INVOKESTATIC, METHODOWNER, FIELDCHANGE, FIELDCHANGEDESC, false);
}
/**
* 旧值存入栈
*/
private void storeOldFieldValue(boolean isStatic, String owner, String name, String desc) {
if (isStatic) {
// 旧值存入栈
mv.visitFieldInsn(GETSTATIC, owner, name, desc);
mv.visitVarInsn(ASTORE, 2);
} else {
// 旧值存入栈
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, owner, name, desc);
mv.visitVarInsn(ASTORE, 2);
}
}
// 数组的变动的指令们
private boolean isArrayOperatorInstrumentationReq(int opCode) {
return opCode == AALOAD || opCode == AASTORE || opCode == LASTORE || opCode == SASTORE || opCode == IASTORE
|| opCode == DASTORE || opCode == FASTORE || opCode == BASTORE || opCode == CASTORE;
}
}
}
调用:
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
public class Main {
public static void main(String[] args) throws IOException {
FileOutputStream fout = null;
try {
ClassReader cr = new ClassReader("advanced/asms/modifies/fields/OriginField");
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor visitor = new FieldChangeSimpleClassVisitor(cw);
// 使给定的访问者访问Java类的ClassReader
cr.accept(visitor, ClassReader.SKIP_DEBUG);
byte[] data = cw.toByteArray();
File file = new File("D:\\OriginField.class");
fout = new FileOutputStream(file);
fout.write(data);
System.out.println("success!");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fout != null) {
fout.close();
}
}
}
}