1. 关于asmtools.jar
用途:使得 “.class文件 -> 字节码指令(类似汇编语言)文件 -> .class文件”,并可以修改“字节码指令文件” 改变一个“.class文件”的运行结果。并重新生成class文件
2. 获取方式asmtools.jar
环境准备:JDK version 8.0, Ant version 1.8 or later, mercurial
下载:hg clone http://hg.openjdk.java.net/code-tools/asmtools
编译:cd asmtools/build && ant 上面是下载打包方式,如果图懒省事,直接使用别人打包好的即可:
3. 使用案例
以郑雨迪老师在“深入拆解JVM”专栏https://time.geekbang.org/column/article/11289 中对boolean类型在jvm中的类型为例示例如何使用:
1
2
3
4
5
6
7
8public class Foo { public static void main(String[] args) { boolean flag = true; if (flag) System.out.println("Hello, Java!"); if (flag == true) System.out.println("Hello, JVM!"); } }
javac Foo.java 命令生成 Foo.class 文件, java Foo 命令运行 Foo.class 文件输出结果:
1
2
3Hello, Java! Hello, JVM!
由 class 文件生成 jasm 文件
如下命令将 class 文件中的内容转换为对应的 jasm 语法
1
2java -jar asmtools.jar jdis Foo.class
执行结果为:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46package foo; super public class Foo version 52:0 { public Method "<init>":"()V" stack 1 locals 1 { aload_0; invokespecial Method java/lang/Object."<init>":"()V"; return; } public static Method main:"([Ljava/lang/String;)V" stack 2 locals 2 { iconst_1; istore_1; iload_1; ifeq L14; getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; ldc String "Hello, Java!"; invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V"; L14: stack_frame_type append; locals_map int; iload_1; iconst_1; if_icmpne L27; getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; ldc String "Hello, JVM!"; invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V"; L27: stack_frame_type same; return; } } // end Class Foo
可以到看到boolean在JVM实际上是作为int处理的
iconst_1 将1压入操作数栈
istore_1 将操作数栈顶保存至局部变量表1位置,
iload_1 再讲局部变量表1 位置加载到操作数栈顶
ifeq L14 判断栈顶位置是否为0 为零则跳转到L14
我们来尝试修改 将压入的1 改为2 看看什么效果
linux下java -jar asmtools.jar jdis Foo.class > Foo.jasm将上述结果输出到文件中。window下直接新建文件将上一个命令输出结果内容拷贝过去即可
将 Foo.class 中的 int i = 1; 修改成 int i = 2;,我们只需要替换 Foo.jasm 文件中的 iconst_1 为 iconst_2
由jsam文件生成class文件
将修改过后的Foo.jasm 执行命令生成class文件
1
2java -jar asmtools.jar jasm Foo.jasm
此时再执行 java Foo 输出:
1
2Hello, Java!
可见字节码已经被修改,至于为何第一个位false第二个为true,解释为:
ifeq 指令的逻辑是 判断栈顶元素是否为0 不为零纪委true了所以改为2 依然为true。
if_cmpne做整数比较,iconst_1是否等于flag(1)比较失败。
扩展
1
2
3
4
5
6
7
8
9
10public class Foo { static boolean boolValue; public static void main(String[] args) { boolValue = true; // 将这个true替换为2或者3,再看看打印结果 if (boolValue) System.out.println("Hello, Java!"); if (boolValue == true) System.out.println("Hello, JVM!"); } }
结果:
当替换为2的时候无输出
当替换为3的时候打印HelloJava及HelloJVM
是因为将boolean 保存在静态域中,指定了其类型为’Z’,当修改为2时取低位最后一位为0,当修改为3时取低位最后一位为1
则说明boolean的掩码处理是取低位的最后一位
4.总结
主要就两条命令
由 class 文件生成 jasm 文件:java -jar asmtools.jar jdis Foo.class > Foo.jasm
由 jasm 文件生成 class 文件:java -jar asmtools.jar jasm Foo.jasm
最后
以上就是激动花瓣最近收集整理的关于asmtools的使用的全部内容,更多相关asmtools内容请搜索靠谱客的其他文章。
发表评论 取消回复