new ClassWriter(ClassWriter.COMPUTE_MAXS)时,将为你计算局部变量与操作数栈部分的大小。还是必须调用 visitMaxs,但可以使用任何参数:它们将被
忽略并重新计算。使用这一选项时,仍然必须自行计算这些帧。
new ClassWriter(ClassWriter.COMPUTE_FRAMES)时,一切都是自动计算。 不再需要调用 visitFrame,但仍然必须调用 visitMaxs(参数将被忽略并重新计
算)。
//可以从命令行使用ASMifier类。可以写好目标方法然后用这个查看ASM的写法
java -classpath asm.jar:asm-util.jar \
org.objectweb.asm.util.ASMifier \
java.lang.Runnable
//产生缩进后的代码为:
package asm.java.lang;
import org.objectweb.asm.*;
public class RunnableDump implements Opcodes {
public static byte[] dump() throws Exception {
ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_5, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,
"java/lang/Runnable", null, "java/lang/Object", null);
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "run", "()V",
null, null);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
}
字节码指令可分为两类:
一小组指令旨在将值从局部变量传输到操作数堆栈,反之亦然;
其他指令仅作用于操作数堆栈:它们从堆栈中弹出一些值,根据这些值计算结果,然后将其压回堆栈。
ILOAD,LLOAD,FLOAD,DLOAD和ALOAD指令读取局部变量并将其值压入操作数堆栈。他们将必须读取的局部变量的索引i作为参数。
ILOAD用于加载布尔,字节,char,short或int局部变量。
LLOAD,FLOAD和DLOAD分别用于加载long,float或double值(LLOAD和DLOAD实际上加载了两个插槽i和i + 1)。
最后,ALOAD用于加载任何非原始值,即对象和数组引用。
对称地,ISTORE,LSTORE,FSTORE,DSTORE和ASTORE指令从操作数堆栈中弹出一个值,并将其存储在其索引i所指定的局部变量中。
public static void sleep(long d) {
try {
Thread.sleep(d);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
可以被编译为字节码:
TRYCATCHBLOCK try catch catch java/lang/InterruptedException
try:
LLOAD 0
INVOKESTATIC java/lang/Thread sleep (J)V
RETURN
catch:
INVOKEVIRTUAL java/lang/InterruptedException printStackTrace ()V
RETURN
try和catch标签之间的代码对应于try块,而catch标签之后的代码对应于catch块。
TRYCATCHBLOCK行指定一个异常处理程序,该处理程序覆盖try和catch标签之间的范围,该处理程序从catch标签开始,并且其类是InterruptedException的子类的异常。
这意味着,如果在try和catch之间的任何地方抛出了这样的异常,则该堆栈被清除,该异常将被推入该空堆栈,并且在catch处继续执行。
————————————————
版权声明:此段引用了CSDN博主「老马啸西风」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:
https://blog.csdn.net/ryo1060732496/article/details/103655608
//参考:https://www.jianshu.com/p/50882f3af59d
class TryCatchMethodVisitor extends AdviceAdapter {
private int localIndex;
private final Label startLabel = new Label();// try开始
private final Label endLabel = new Label();// try结束
private final Label handlerLabel = new Label();// catch开始处理异常
private String methodDescriptor;
protected TryCatchMethodVisitor(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {
super(api, methodVisitor, access, name, descriptor);
methodDescriptor = descriptor;
System.out.println(name + " " + descriptor);
}
@Override
protected void onMethodEnter() {
super.onMethodEnter();
mv.visitLabel(startLabel);//标记开始
}
@Override
public void visitMaxs(int maxStack, int maxLocals) {
mv.visitLabel(endLabel);//方法结束时标记接收
mv.visitLabel(handlerLabel);//开始处理catch部分
//-----------catch代码开始-----------
localIndex = newLocal(Type.LONG_TYPE);
mv.visitVarInsn(Opcodes.ALOAD, localIndex);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false);
if (String.class.getName().equals(Type.getType(methodDescriptor).getReturnType().getClassName())) {
int index = newLocal(Type.LONG_TYPE);
mv.visitLdcInsn("669");
mv.visitVarInsn(ASTORE, index);
mv.visitVarInsn(ALOAD, index);
mv.visitInsn(Opcodes.ARETURN);
}
//-----------catch代码结束-----------
//
mv.visitTryCatchBlock(startLabel, endLabel, handlerLabel, "java/lang/Exception");
super.visitMaxs(maxStack, maxLocals);
}
}
//方法整体替换
public class MethodReplace {
public static void main(String[] args) throws Exception {
ClassReader classReader = new ClassReader("com.moon.asm.learning.day0302.Person");
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor classVisitor = new MyClassVisitor(Opcodes.ASM9, classWriter);
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES + ClassReader.SKIP_FRAMES);
byte[] bytes = classWriter.toByteArray();
FileUtils.writeByteArrayToFile(new File("d://Person.class"), bytes);
}
private static class MyClassVisitor extends ClassVisitor {
protected MyClassVisitor(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
}
/**
* 笔记ClassReader里面调用完visitMethod方法后得到MethodVisitor会继续调用MethodVisitor里面的方法。
* 因此直接在这里执行MethodVisitor相关方法,返回给ClassReader一个null可以达到方法内容替换的目的
* @return
*/
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if ("replaceAllMethod".equals(name)) {
MethodVisitor methodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
methodVisitor.visitLdcInsn("123");
methodVisitor.visitInsn(Opcodes.ARETURN);
return null;
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
}
}
//替换某个静态方法调用
public class StaticMethodReplace {
public static void main(String[] args) throws Exception {
ClassReader classReader = new ClassReader("com.moon.asm.learning.day0302.Person");
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor classVisitor = new MyClassVisitor(Opcodes.ASM9, classWriter);
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES + ClassReader.SKIP_FRAMES);
byte[] bytes = classWriter.toByteArray();
FileUtils.writeByteArrayToFile(new File("d://Person.class"), bytes);
}
private static class MyClassVisitor extends ClassVisitor {
protected MyClassVisitor(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if ("staticMethodReplace".equals(name)) {
MethodVisitor originalMethodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
return new MyMethodVisitor(Opcodes.ASM9, originalMethodVisitor, access, name, descriptor);
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
}
private static class MyMethodVisitor extends AdviceAdapter {
protected MyMethodVisitor(int api, MethodVisitor methodVisitor, int access, String name, String descriptor) {
super(api, methodVisitor, access, name, descriptor);
}
@Override
public void visitMethodInsn(int opcodeAndSource, String owner, String name, String descriptor, boolean isInterface) {
if ("java/lang/System".equals(owner) && "getProperty".equals(name)) {
mv.visitMethodInsn(opcodeAndSource, "com/moon/asm/learning/day0302/Person", "staticMethodReplaceProxy", descriptor, isInterface);
return;
}
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
}
}
}
//替换某个非静态方法调用
public class VirtualMethodReplace {
public static void main(String[] args) throws Exception {
ClassReader classReader = new ClassReader("com.moon.asm.learning.day0302.Person");
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor classVisitor = new VirtualMethodReplace.MyClassVisitor(Opcodes.ASM9, classWriter);
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES + ClassReader.SKIP_FRAMES);
byte[] bytes = classWriter.toByteArray();
FileUtils.writeByteArrayToFile(new File("d://Person.class"), bytes);
}
private static class MyClassVisitor extends ClassVisitor {
protected MyClassVisitor(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
if ("virtualMethodReplace".equals(name)) {
MethodVisitor originalMethodVisitor = super.visitMethod(access, name, descriptor, signature, exceptions);
return new VirtualMethodReplace.MyMethodVisitor(Opcodes.ASM9, originalMethodVisitor);
}
return super.visitMethod(access, name, descriptor, signature, exceptions);
}
}
private static class MyMethodVisitor extends MethodVisitor {
protected MyMethodVisitor(int api, MethodVisitor methodVisitor) {
super(api, methodVisitor);
}
@Override
public void visitMethodInsn(int opcodeAndSource, String owner, String name, String descriptor, boolean isInterface) {
// public String virtualMethodReplace(String name) {
// return name.substring(2); 修改成---> Person.virtualMethodReplaceProxy(name, 2);
// }
//
// public static String virtualMethodReplaceProxy(String text, int beginIndex) {
// return text.substring(beginIndex);
// }
//调用substring前会执行ALOAD 1, LDC 2,因此调用静态的virtualMethodReplaceProxy将这两个参数给使用掉了
if ("java/lang/String".equals(owner) && "substring".equals(name)) {
descriptor = "(Ljava/lang/String;I)Ljava/lang/String;";//virtualMethodReplaceProxy方法的descriptor
//也可以写成
//descriptor = descriptor.replace("(","(L" + owner +";");
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/moon/asm/learning/day0302/Person", "virtualMethodReplaceProxy",
descriptor, isInterface);
return;
}
super.visitMethodInsn(opcodeAndSource, owner, name, descriptor, isInterface);
}
}
}
//修改父类
//https://blog.csdn.net/ryo1060732496/article/details/103655574
// byte[] b1 = ...
// ClassReader cr = new ClassReader(b1);
// ClassWriter cw = new ClassWriter(cr, 0);//优化把cr出传进去
// ChangeVersionAdapter ca = new ChangeVersionAdapter(cw);
// cr.accept(ca, 0);
// byte[] b2 = cw.toByteArray();
//对于添加字段,方法或指令的转换来说,这不是问题,但是与未优化的情况相比,对于删除或重命名许多类元素的转换,这导致更大的类文件。因此,建议仅将此优化用于“附加”转换。
public class ModifySuperClass {
public static void main(String[] args) throws IOException {
ClassReader classReader = new ClassReader("com.moon.asm.learning.day0302.SuperMan");
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor classVisitor = new ModifySuperClassVisitor(Opcodes.ASM9, classWriter);
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES + ClassReader.SKIP_FRAMES);
byte[] bytes = classWriter.toByteArray();
FileUtils.writeByteArrayToFile(new File("d://SuperMan.class"), bytes);
}
static class ModifySuperClassVisitor extends ClassVisitor {
protected ModifySuperClassVisitor(int api) {
super(api);
}
protected ModifySuperClassVisitor(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
//就这样就把SuperMan的父类修改为Person了
super.visit(version, access, name, signature, "com/moon/asm/learning/day0302/Person", interfaces);
}
}
}
//添加删除类成员
public class ModifyMemberClass {
public static void main(String[] args) throws IOException {
ClassReader classReader = new ClassReader("com.moon.asm.learning.day0302.SuperMan");
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor classVisitor = new ModifySuperClassVisitor(Opcodes.ASM9, classWriter);
classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES + ClassReader.SKIP_FRAMES);
byte[] bytes = classWriter.toByteArray();
FileUtils.writeByteArrayToFile(new File("d://SuperMan.class"), bytes);
}
static class ModifyMemberClassVisitor extends ClassVisitor {
protected ModifyMemberClassVisitor(int api) {
super(api);
}
protected ModifyMemberClassVisitor(int api, ClassVisitor classVisitor) {
super(api, classVisitor);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
// 放在这里其实是不太合适的,最好是在visitEnd中添加成员
// 例如,如果要向类添加字段,则必须在原始方法调用之间插入对visitField的新调用,并且必须将此新调用放入类适配器的visit方法之一。
// 例如,您无法在visit方法中执行此操作,因为这可能会导致对visitField的调用,随后是无效的visitSource,visitOuterClass,visitAnnotation或visitAttribute。
// 出于相同的原因,您不能将此新调用放入visitSource,visitOuterClass,visitAnnotation或visitAttribute方法中。
// 唯一的可能性是visitInnerClass,visitField,visitMethod或visitEnd方法。
// 如果将新调用放入visitEnd方法中,则将始终添加该字段(除非您添加显式条件),因为始终会调用此方法。
//相当于向cv多转发了一部分内容
//public static final int LESS = -1;
cv.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "LESS", "I",
null, -1).visitEnd();
//public static final String text = "Hello";
cv.visitField(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, "text", "Ljava/lang/String;",
null, "Hello").visitEnd();
//添加方法
// public abstract int compareTo(Object o);
cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT, "compareTo",
"(Ljava/lang/Object;)I", null, null).visitEnd();
}
}
}
//Person
public class Person {
public String getName() {
int a = 1;
int b = 2;
int c = a + b;
return "123";
}
public void setName() {
int a = 1;
int b = 2;
int c = a + b;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String replaceAllMethod() {
int a = 10;
int b = a + 2;
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
public String staticMethodReplace() {
return System.getProperty("xxx");
}
public static String staticMethodReplaceProxy(String key) {
return System.getProperty(key);
}
public String virtualMethodReplace(String name) {
return name.substring(2);
}
public static String virtualMethodReplaceProxy(String text, int beginIndex) {
return text.substring(beginIndex);
}
}
轮子
transform框架:
https://github.com/Leaking/Hunter
http://quinnchen.cn/2018/09/13/2018-09-13-asm-transform/
https://github.com/bytedance/ByteX
优秀文章:
分享 4 个案例,一起玩一下 ASM