我需要知道是否使用以下代码和javassist我可以操作代码来替换逻辑运算符“>”用“<”。
这是我想要操作的字节码的类:
public class TryClass {
public void foo(){
int a =0;
if(a>5){
System.out.println("I love apples");
}
else{
System.out.println("I hate apples");
}
}
}
在操作之后,应该打印类的执行: “我喜欢苹果” 代替: “我讨厌苹果”
答案 0 :(得分:2)
字节代码级别本身没有<
或>
,例如if_icmple
用于比较 - 这意味着它是一个实际的字节代码指令。因此javassist
应该能够做到这一点,它被称为构建时字节码检测,在网上有很多关于此的文章。
Here is one for example
答案 1 :(得分:1)
在方法体中更改表达式的常用模式是使用ExprEditor和Expr的特定子类,其中包括以下类型的表达式(在撰写本文时):
然而,这些都不包括比较表达式。您可以通过查看ExprEditor::loopBody
:
if (c < Opcode.GETSTATIC) // c < 178
/* skip */;
跳过比较操作码,例如if_icmple
= 164。
由于Javassist的高级检测API,它经常被使用,但它在javassist.bytecode
包下也有一个字节码级API。这意味着您可以查看方法字节码中的操作码并交换它们。
首先我们需要确定我们希望改变的foo
方法和表达式的字节码(例如使用javap):
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmple 18
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: ldc #3 // String I love apples
12: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
15: goto 26
18: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
21: ldc #5 // String I hate apples
23: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
26: return
正如我们所看到的,a > 5
被编译成if_icmple
(&lt; =)比较并转移到else块,这是编译器处理if表达式的常见模式。
要翻转示例中的表达式,我们只需要将if_icmple
与if_icmpgt
交换。
以下代码演示了如何使用Javassist字节码API执行此操作:
CtClass cc = ClassPool.getDefault().get("TryClass");
CtMethod fooMethod = cc.getDeclaredMethod("foo");
CodeIterator codeIterator = fooMethod.getMethodInfo().getCodeAttribute().iterator();
while (codeIterator.hasNext()) {
int pos = codeIterator.next();
int opcode = codeIterator.byteAt(pos);
if(opcode == Opcode.IF_ICMPLE) {
codeIterator.writeByte(Opcode.IF_ICMPGT, pos);
break;
}
}
TryClass test = (TryClass) cc.toClass().newInstance();
test.foo();
但是,此代码不会进行任何其他检查以确保更改预期的表达式。一些建议可能是在比较期间检查堆栈中的哪些操作数。如果其中一个从局部变量槽加载,您可以使用LocalVariableTable(如果可用)CodeAttribute中的信息来匹配变量名称。