Java位元組碼工具AsmTools介紹
AsmTools是一款位元組碼生成工具。 包括的元件:
- jasm:由JASM格式得到class檔案
- jdis:把class檔案轉為JASM格式
- jcoder:由JCOD格式得到class檔案
- jdec:把class檔案轉為JCOD格式
兩種彙編器/反彙編器(assembler/disassemblers)對應的格式有啥區別?
JASM specifically focuses on representing byte-code instructions in the VM format (while providing minimal description of the structure of the rest of the class file). Generally, JASM is more convenient for semantic changes, like change to instruction flow.
JASM比較貼近原始的位元組碼,和通過javap生成的位元組碼差不多,修改指令的語義也比較容易。 JCOD更好的描述了class檔案的結構資訊。
接下來通過一個簡單類直觀感受下。
使用asmtools彙編/反彙編
class檔案轉為JASM格式:
D:\dev\asmtools-7.0-build>javac Hello.java D:\dev\asmtools-7.0-build>java -jar asmtools.jar jdis Hello.class super public class Hello version 52:0 { public Method "<init>":"()V" stack 1 locals 1 { aload_0; invokespecialMethod java/lang/Object."<init>":"()V"; return; } public static Method main:"([Ljava/lang/String;)V" stack 2 locals 1 { getstaticField java/lang/System.out:"Ljava/io/PrintStream;"; ldcString "Hello"; invokevirtualMethod java/io/PrintStream.println:"(Ljava/lang/String;)V"; return; } } // end Class Hello
使用javap得到的反彙編結果:
D:\dev\asmtools-7.0-build>javap -c Hello.class Compiled from "Hello.java" public class Hello { public Hello(); Code: 0: aload_0 1: invokespecial #1// Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: getstatic#2// Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc#3// String Hello 5: invokevirtual #4// Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }
class檔案轉為JCOD格式:
D:\dev\asmtools-7.0-build>java -jar asmtools.jar jdec Hello.class class Hello { 0xCAFEBABE; 0; // minor version 52; // version [] { // Constant Pool ; // first element is empty Method #6 #15; // #1 Field #16 #17; // #2 String #18; // #3 Method #19 #20; // #4 class #18; // #5 class #21; // #6 Utf8 "<init>"; // #7 Utf8 "()V"; // #8 Utf8 "Code"; // #9 Utf8 "LineNumberTable"; // #10 Utf8 "main"; // #11 Utf8 "([Ljava/lang/String;)V"; // #12 Utf8 "SourceFile"; // #13 Utf8 "Hello.java"; // #14 NameAndType #7 #8; // #15 class #22; // #16 NameAndType #23 #24; // #17 Utf8 "Hello"; // #18 class #25; // #19 NameAndType #26 #27; // #20 Utf8 "java/lang/Object"; // #21 Utf8 "java/lang/System"; // #22 Utf8 "out"; // #23 Utf8 "Ljava/io/PrintStream;"; // #24 Utf8 "java/io/PrintStream"; // #25 Utf8 "println"; // #26 Utf8 "(Ljava/lang/String;)V"; // #27 } // Constant Pool 0x0021; // access #5;// this_cpx #6;// super_cpx [] { // Interfaces } // Interfaces [] { // fields } // fields [] { // methods { // Member 0x0001; // access #7; // name_cpx #8; // sig_cpx [] { // Attributes Attr(#9) { // Code 1; // max_stack 1; // max_locals Bytes[]{ 0x2AB70001B1; } [] { // Traps } // end Traps [] { // Attributes Attr(#10) { // LineNumberTable [] { // LineNumberTable 01; } } // end LineNumberTable } // Attributes } // end Code } // Attributes } // Member ; { // Member 0x0009; // access #11; // name_cpx #12; // sig_cpx [] { // Attributes Attr(#9) { // Code 2; // max_stack 1; // max_locals Bytes[]{ 0xB200021203B60004; 0xB1; } [] { // Traps } // end Traps [] { // Attributes Attr(#10) { // LineNumberTable [] { // LineNumberTable 03; 84; } } // end LineNumberTable } // Attributes } // end Code } // Attributes } // Member } // methods [] { // Attributes Attr(#13) { // SourceFile #14; } // end SourceFile } // Attributes } // end class Hello
從jasm,jcod格式生成class檔案:
D:\dev\asmtools-7.0-build>java -jar asmtools.jar jcoder Hello.jcod D:\dev\asmtools-7.0-build>java -jar asmtools.jar jcoder Hello.jcod D:\dev\asmtools-7.0-build>java Hello Hello D:\dev\asmtools-7.0-build>
修改位元組碼
接下來展示一個使用asmtools修改位元組碼的例子。原始類:
public class Foo { static boolean bv; public static void main(String[] args) { bv = true; if (bv) { System.out.println("Hello, Java!"); } if (bv == true){ System.out.println("Hello, JVM!"); } } }
執行後輸出:
Hello, Java! Hello, JVM!
對應的JASM格式反彙編內容:
super public class Foo version 52:0 { static Field bv:Z; public Method "<init>":"()V" stack 1 locals 1 { aload_0; invokespecialMethod java/lang/Object."<init>":"()V"; return; } public static Method main:"([Ljava/lang/String;)V" stack 2 locals 1 { iconst_1;# 入棧常量1 putstaticField bv:"Z"; getstaticField bv:"Z"; ifeqL18; getstaticField java/lang/System.out:"Ljava/io/PrintStream;"; ldcString "Hello, Java!"; invokevirtualMethod java/io/PrintStream.println:"(Ljava/lang/String;)V"; L18:stack_frame_type same; getstaticField bv:"Z"; iconst_1; if_icmpneL33;# 比較 getstaticField java/lang/System.out:"Ljava/io/PrintStream;"; ldcString "Hello, JVM!"; invokevirtualMethod java/io/PrintStream.println:"(Ljava/lang/String;)V"; L33:stack_frame_type same; return; } } // end Class Foo
可以從中看到java中的true在位元組碼層面使用的int 1表示的。
如果修改下Foo.jasm。
super public class Foo version 52:0 { static Field bv:Z; public Method "<init>":"()V" stack 1 locals 1 { aload_0; invokespecialMethod java/lang/Object."<init>":"()V"; return; } public static Method main:"([Ljava/lang/String;)V" stack 2 locals 1 { iconst_1; putstaticField bv:"Z"; getstaticField bv:"Z"; ifeqL18; getstaticField java/lang/System.out:"Ljava/io/PrintStream;"; ldcString "Hello, Java!"; invokevirtualMethod java/io/PrintStream.println:"(Ljava/lang/String;)V"; L18:stack_frame_type same; getstaticField bv:"Z"; iconst_2;# 修改了這裡 if_icmpneL33; getstaticField java/lang/System.out:"Ljava/io/PrintStream;"; ldcString "Hello, JVM!"; invokevirtualMethod java/io/PrintStream.println:"(Ljava/lang/String;)V"; L33:stack_frame_type same; return; } } // end Class Foo
把第二個iconst_1修改為iconst_2也就是把 1 和2比較,會得到下面的輸出:
$ java -jarasmtools.jar jasm Foo.jasm > Foo.class $ java Foo Hello, Java!
相關閱讀
ofollow,noindex" target="_blank">Java Class檔案結構分析