堆栈高度不一致 0 != 1
Inconsistent stack height 0 != 1
我正在通过十六进制编辑器修改 Java class 字节码,我想强制方法始终 return 为真。
- 将其所有字节码替换为 nop 以保持大小不变(原始大小为 1890)。
- 执行
pop
以恢复堆栈高度,因为它收到一个参数。
- Return 正确,
iconst_1
后跟 ireturn
。
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
结果完全相同。
pop
是不必要的,因为参数最初不在堆栈上。它们仅在使用 *load
指令时被压入堆栈,就好像它们是局部变量一样,这随时可能发生。
pop
从堆栈弹出一个值,但作为参数传递的字符串在 "local variable" 0.
您应该能够安全地忽略 pop
.
此外,您应该可以省略所有 nop
,而只需将指令 0 替换为 iconst_1
,将指令 1 替换为 ireturn
,然后将其余的全部保留方法不变。
这样你会做更少的工作,甚至可能提高性能。
如果您使用的是 Java 7 或更高版本,可能是 JVM 正在使用堆栈映射帧验证您的字节码。 (检查 this question/answer 以了解堆栈映射帧的说明)
如果您正在使用 Java 7,当您 运行 您的 class.
时,尝试在命令行中使用 -XX:-UseSplitVerifier
如果您使用的是java8,那么您还需要修改堆栈图框;这样做并不简单,所以我最好建议您使用像 javassist.
这样的字节码操作库
更新
根据@Holger 的评论,他是对的。但是据我所知,您是用 NOP 填充不需要的操作码,而不是删除它们。
您可能已经知道,机器指令位于名为 code 的属性中;这个属性可以有 "sub-attributes"(code 本身的属性)。其中之一是 StackMapTable 属性,它是 "stack maps".
的 "array"(table)
将此属性替换为零是不够的,您必须:
- 将table(堆栈映射table)的条目数设置为零
- 删除那个 table 上的唯一条目(并偏移以下属性和其他字段)
还想亲手做? :-)
我正在通过十六进制编辑器修改 Java class 字节码,我想强制方法始终 return 为真。
- 将其所有字节码替换为 nop 以保持大小不变(原始大小为 1890)。
- 执行
pop
以恢复堆栈高度,因为它收到一个参数。 - Return 正确,
iconst_1
后跟ireturn
。
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
结果完全相同。
pop
是不必要的,因为参数最初不在堆栈上。它们仅在使用 *load
指令时被压入堆栈,就好像它们是局部变量一样,这随时可能发生。
pop
从堆栈弹出一个值,但作为参数传递的字符串在 "local variable" 0.
您应该能够安全地忽略 pop
.
此外,您应该可以省略所有 nop
,而只需将指令 0 替换为 iconst_1
,将指令 1 替换为 ireturn
,然后将其余的全部保留方法不变。
这样你会做更少的工作,甚至可能提高性能。
如果您使用的是 Java 7 或更高版本,可能是 JVM 正在使用堆栈映射帧验证您的字节码。 (检查 this question/answer 以了解堆栈映射帧的说明)
如果您正在使用 Java 7,当您 运行 您的 class.
时,尝试在命令行中使用-XX:-UseSplitVerifier
如果您使用的是java8,那么您还需要修改堆栈图框;这样做并不简单,所以我最好建议您使用像 javassist.
这样的字节码操作库更新
根据@Holger 的评论,他是对的。但是据我所知,您是用 NOP 填充不需要的操作码,而不是删除它们。
您可能已经知道,机器指令位于名为 code 的属性中;这个属性可以有 "sub-attributes"(code 本身的属性)。其中之一是 StackMapTable 属性,它是 "stack maps".
的 "array"(table)将此属性替换为零是不够的,您必须:
- 将table(堆栈映射table)的条目数设置为零
- 删除那个 table 上的唯一条目(并偏移以下属性和其他字段)
还想亲手做? :-)