使用 asm java 时出现验证错误
Getting verify error when working with asm java
基本上我想添加一个简单的 System.out.println("hey");
在一个方法的末尾。我用的是树API。但是我确实不断收到此错误:
java.lang.VerifyError: Expecting a stackmap frame at branch target 38
这是我的代码:
public class MethodNodeCustom extends MethodNode {
public MethodNodeCustom(int paramInt, String paramString1, String paramString2, String paramString3, String[] paramArrayOfString) {
this(327680, paramInt, paramString1, paramString2, paramString3, paramArrayOfString);
return;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public MethodNodeCustom(int paramInt1, int paramInt2, String paramString1, String paramString2, String paramString3,
String[] paramArrayOfString) {
super(paramInt1);
this.access = paramInt2;
this.name = paramString1;
this.desc = paramString2;
this.signature = paramString3;
this.exceptions = new ArrayList((paramArrayOfString == null) ? 0 : paramArrayOfString.length);
int i = ((paramInt2 & 0x400) != 0) ? 1 : 0;
if (i == 0)
this.localVariables = new ArrayList(5);
this.tryCatchBlocks = new ArrayList();
if (paramArrayOfString != null)
this.exceptions.addAll(Arrays.asList(paramArrayOfString));
this.instructions = new InsnList();
}
@Override
public void visitEnd() {
AbstractInsnNode label = instructions.getLast();
instructions.remove(instructions.getLast());
instructions.remove(instructions.getLast());
visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", Type.getDescriptor(PrintStream.class));
visitLdcInsn("Cracked by damm ass pro skills");
visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
visitInsn(Opcodes.RETURN);
instructions.add(label);
super.visitEnd();
}
}
这是我的 class 节点:
public class ClassNodeCustom extends ClassNode {
public ClassNodeCustom() {
super(ASMContentHandler.ASM4);
}
@SuppressWarnings("unchecked")
@Override
public MethodVisitor visitMethod(int paramInt, String paramString1, String paramString2, String paramString3, String[] paramArrayOfString) {
MethodNode localMethodNode = new MethodNodeCustom(paramInt, paramString1, paramString2, paramString3, paramArrayOfString);
this.methods.add(localMethodNode);
return localMethodNode;
}
}
这就是我 "inject" 代码的方式(我直接从 jar 加载它,这就是它使用 zipFile 的原因)
InputStream in = zipFile.getInputStream(entry);
ClassReader cr = new ClassReader(in);
ClassNodeCustom node = new ClassNodeCustom();
cr.accept(node, 0);
ClassWriter cw = new ClassWriter(0);
node.accept(cw);
就像我说的,每当我 运行 它时,我都会收到验证错误,我有什么办法可以解决它,或者我有什么更聪明的方法来 "inject" 该代码吗?
如果您在方法末尾添加代码,则在编译 Java 代码时,您是在其最后一条指令之后添加它,该指令始终是 goto、switch、throw 或 return 语句。即使在编译方法时没有像
这样的显式 return 语句
void foo() { }
你实际上是在编译
void foo() { return; }
其中最后的 return 是隐含的。通过添加,您将方法更改为
void foo() {
return;
System.out.println("hey");
}
这种无法访问的代码被javac禁止,但在字节码中是完全合法的。但是,对于无法访问的代码,您需要在其前面添加一个 stack map frame 来描述此时堆栈的状态和局部变量数组。此时添加空框架的描述很容易,但我假设您想在 return 语句之前添加代码。
要实现这一点,ASM offers an AdviceAdapter
允许您在 return 语句之前添加代码。据我所知,树 API 没有任何相似之处,但您可以简单地在任何方法的指令列表中查找 return 节点并在其之前添加代码。
基本上我想添加一个简单的 System.out.println("hey");
在一个方法的末尾。我用的是树API。但是我确实不断收到此错误:
java.lang.VerifyError: Expecting a stackmap frame at branch target 38
这是我的代码:
public class MethodNodeCustom extends MethodNode {
public MethodNodeCustom(int paramInt, String paramString1, String paramString2, String paramString3, String[] paramArrayOfString) {
this(327680, paramInt, paramString1, paramString2, paramString3, paramArrayOfString);
return;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public MethodNodeCustom(int paramInt1, int paramInt2, String paramString1, String paramString2, String paramString3,
String[] paramArrayOfString) {
super(paramInt1);
this.access = paramInt2;
this.name = paramString1;
this.desc = paramString2;
this.signature = paramString3;
this.exceptions = new ArrayList((paramArrayOfString == null) ? 0 : paramArrayOfString.length);
int i = ((paramInt2 & 0x400) != 0) ? 1 : 0;
if (i == 0)
this.localVariables = new ArrayList(5);
this.tryCatchBlocks = new ArrayList();
if (paramArrayOfString != null)
this.exceptions.addAll(Arrays.asList(paramArrayOfString));
this.instructions = new InsnList();
}
@Override
public void visitEnd() {
AbstractInsnNode label = instructions.getLast();
instructions.remove(instructions.getLast());
instructions.remove(instructions.getLast());
visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", Type.getDescriptor(PrintStream.class));
visitLdcInsn("Cracked by damm ass pro skills");
visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
visitInsn(Opcodes.RETURN);
instructions.add(label);
super.visitEnd();
}
}
这是我的 class 节点:
public class ClassNodeCustom extends ClassNode {
public ClassNodeCustom() {
super(ASMContentHandler.ASM4);
}
@SuppressWarnings("unchecked")
@Override
public MethodVisitor visitMethod(int paramInt, String paramString1, String paramString2, String paramString3, String[] paramArrayOfString) {
MethodNode localMethodNode = new MethodNodeCustom(paramInt, paramString1, paramString2, paramString3, paramArrayOfString);
this.methods.add(localMethodNode);
return localMethodNode;
}
}
这就是我 "inject" 代码的方式(我直接从 jar 加载它,这就是它使用 zipFile 的原因)
InputStream in = zipFile.getInputStream(entry);
ClassReader cr = new ClassReader(in);
ClassNodeCustom node = new ClassNodeCustom();
cr.accept(node, 0);
ClassWriter cw = new ClassWriter(0);
node.accept(cw);
就像我说的,每当我 运行 它时,我都会收到验证错误,我有什么办法可以解决它,或者我有什么更聪明的方法来 "inject" 该代码吗?
如果您在方法末尾添加代码,则在编译 Java 代码时,您是在其最后一条指令之后添加它,该指令始终是 goto、switch、throw 或 return 语句。即使在编译方法时没有像
这样的显式 return 语句void foo() { }
你实际上是在编译
void foo() { return; }
其中最后的 return 是隐含的。通过添加,您将方法更改为
void foo() {
return;
System.out.println("hey");
}
这种无法访问的代码被javac禁止,但在字节码中是完全合法的。但是,对于无法访问的代码,您需要在其前面添加一个 stack map frame 来描述此时堆栈的状态和局部变量数组。此时添加空框架的描述很容易,但我假设您想在 return 语句之前添加代码。
要实现这一点,ASM offers an AdviceAdapter
允许您在 return 语句之前添加代码。据我所知,树 API 没有任何相似之处,但您可以简单地在任何方法的指令列表中查找 return 节点并在其之前添加代码。