我正在通过十六进制编辑器修改Java类字节码,我想强制一个方法始终返回true。
pop
以恢复堆栈高度,因为它收到一个参数。iconst_1
后跟ireturn
返回true。public static boolean test(java.lang.String);
descriptor: (Ljava/lang/String;)Z
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=5, locals=12, args_size=1
0: nop
1: nop
2: nop
3: nop
4: nop
[...]
1886: nop
1887: nop
1888: pop
1889: iconst_1
1890: ireturn
但是在执行时,我收到以下错误
java.lang.VerifyError: (class: com/example/test/TestBytecode, method: test signature: (Ljava/lang/String;)Z) Inconsistent stack height 0 != 1
注意:无论是否有pop
,结果都完全相同。
答案 0 :(得分:4)
pop
是不必要的,因为参数最初不在堆栈中。它们仅在使用*load
指令时被压入堆栈,就好像它们是局部变量一样,这可能随时发生。
答案 1 :(得分:2)
pop
从堆栈中弹出一个值,但作为参数传递的字符串位于“局部变量”0中。
您应该能够安全地省略pop
。
此外,您应该可以省略所有nop
,而只需将指令0替换为iconst_1
,将指令1替换为ireturn
,并保留方法的其余部分不变。
这样你就可以减少工作量,甚至可能提高性能。
答案 2 :(得分:1)
如果您使用的是Java 7或更高版本,JVM可能正在使用堆栈映射帧验证您的字节码。 (检查this问题/答案是否有关于堆栈映射框的说明)
如果您使用的是Java 7,请在运行课程时在命令行中使用-XX:-UseSplitVerifier
。
如果您使用的是java 8,那么您还将修改堆栈映射帧;这样做并不简单,所以我建议你使用像javassist这样的字节码操作库。
更新
基于@Holger的评论,他是对的。但是据我所知,你用NOP填充不需要的操作码而不是删除它们。
您可能已经知道,机器说明位于名为代码的属性中;此属性可以具有“子属性”(代码本身的属性)。其中一个是属性 StackMapTable ,它是“堆栈映射”的“数组”(表)。
用零替换此属性是不够的,您必须:
还想手工做吗? : - )