Winse Blog

走走停停, 熙熙攘攘, 忙忙碌碌, 不知何畏.

Java反编译工具使用记录

工具

差异

JD-Core还是比较与时俱进的,对泛型/foreach等新的语法都支持。Jad则只支持到only 45.3(1.1), 46.0 and 47.0(1.3)。
任何事物都不是完美的,结合两个工具来看反编译后的源码能更好的回归源码的真相。

  • JD-Core解析双for循环是存在问题!结合jad可能更好的明白源码。
  • 内部类解析不够好,JD-Core相对错误少一些

对比

这里就[for循环]/[双层for循环]/[内部类]进行对比。

单for循环

源码

1
2
3
4
5
6
7
List<String> list = Arrays.asList("abc", "bcd");

public void testForEach() {
  for (String ele : list) { // for编译成字节码后使用iterator的形式
      System.out.println(ele);
  }
}

JD-Core v0.3.5 JD-GUI v0.6.2

1
2
3
4
5
6
List<String> list = Arrays.asList(new String[] { "abc", "bcd" });

public void testForEach() {
for (String ele : this.list)
  System.out.println(ele);
}

Jad v1.5.8e2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public JavaDecompilerTest()
{
  list = Arrays.asList(new String[] {
      "abc", "bcd"
  });
}

public void testForEach()
{
  String ele;
  for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(ele))
      ele = (String)iterator.next();

}

List list;

双层for循环

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
{
  Map<String, String> map = new HashMap<String, String>();
  map.put("12", "23");
  list.add(map);
}

public void testForIndex() {
  for (int i = 0; i < list.size(); i++) {
      Map<String, String> map = list.get(i);
      for (String key : map.keySet())
          System.out.println(map.get(key));
  }
}

public void testForEach() {
  for (Map<String, String> map : list) {
      for (String key : map.keySet())
          System.out.println(map.get(key));
  }
}

JD-Core v0.3.5 JD-GUI v0.6.2

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
  List<Map<String, String>> list;

  public JavaDecompilerTest2()
  {
  this.list = new ArrayList();

  Map map = new HashMap();
  map.put("12", "23");
  this.list.add(map);
  }

  public void testForIndex() {
  for (int i = 0; i < this.list.size(); i++) {
    Map map = (Map)this.list.get(i);
    for (String key : map.keySet())
      System.out.println((String)map.get(key));
  }
  }

  public void testForEach()
  {
  Iterator localIterator2;
  for (Iterator localIterator1 = this.list.iterator(); localIterator1.hasNext(); 
    localIterator2.hasNext())
  {
    Map map = (Map)localIterator1.next();
    localIterator2 = map.keySet().iterator(); continue; String key = (String)localIterator2.next();
    System.out.println((String)map.get(key));
  }
  }

Jad v1.5.8e2.

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
public JavaDecompilerTest2()
{
  list = new ArrayList();
  Map map = new HashMap();
  map.put("12", "23");
  list.add(map);
}

public void testForIndex()
{
  for(int i = 0; i < list.size(); i++)
  {
      Map map = (Map)list.get(i);
      String key;
      for(Iterator iterator = map.keySet().iterator(); iterator.hasNext(); System.out.println((String)map.get(key)))
          key = (String)iterator.next();

  }

}

public void testForEach()
{
  for(Iterator iterator = list.iterator(); iterator.hasNext();)
  {
      Map map = (Map)iterator.next();
      String key;
      for(Iterator iterator1 = map.keySet().iterator(); iterator1.hasNext(); System.out.println((String)map.get(key)))
          key = (String)iterator1.next();

  }

}

List list;

内部类

源码

1
2
3
4
5
6
7
public class JavaDecompilerTest3 {

  private class Test2 {}
  
  Test2 test = new Test2();
  
}

JD-Core v0.3.5 JD-GUI v0.6.2

1
2
3
4
5
6
7
8
9
10
11
public class JavaDecompilerTest3
{
  JavaDecompilerTest3.Test2 test = new JavaDecompilerTest3.Test2(null);

  private class Test2
  {
  private Test2()
  {
  }
  }
}

Jad v1.5.8e2.

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
public class JavaDecompilerTest3
{
  private class Test2
  {

      final JavaDecompilerTest3 this$0;

      private Test2()
      {
          this$0 = JavaDecompilerTest3.this;
          super();
      }

      Test2(Test2 test2)
      {
          this();
      }
  }


  public JavaDecompilerTest3()
  {
      test = new Test2(null);
  }

  Test2 test;
}

后记

工具javap可以查看class的方法签名,通过jd-gui和jad可以反编译得到源码,如果代码没有混淆的话,多半就能了解代码的功能了。

读jdk7的try-resource的字节码

先了解下字节码的规范:Compiling for the Java Virtual Machine

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
$ javap -public -c -v ByteCodeTest.class
Classfile /E:/test/target/test-classes/com/ByteCodeTest.class
  Last modified 2016-5-6; size 992 bytes
  MD5 checksum ee7912d638a98f9a4a61d726e08c08f0
  Compiled from "ByteCodeTest.java"
public class com.ByteCodeTest
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // com/ByteCodeTest
   #2 = Utf8               com/ByteCodeTest
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Methodref          #3.#9          // java/lang/Object."<init>":()V
   #9 = NameAndType        #5:#6          // "<init>":()V
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lcom/ByteCodeTest;
  #14 = Utf8               tryResourceWithFinally
  #15 = Utf8               Exceptions
  #16 = Class              #17            // java/io/FileNotFoundException
  #17 = Utf8               java/io/FileNotFoundException
  #18 = Class              #19            // java/io/IOException
  #19 = Utf8               java/io/IOException
  #20 = Utf8               RuntimeVisibleAnnotations
  #21 = Utf8               Lorg/junit/Test;
  #22 = Class              #23            // java/io/FileInputStream
  #23 = Utf8               java/io/FileInputStream
  #24 = String             #25            //
  #25 = Utf8
  #26 = Methodref          #22.#27        // java/io/FileInputStream."<init>":(Ljava/lang/String;)V
  #27 = NameAndType        #5:#28         // "<init>":(Ljava/lang/String;)V
  #28 = Utf8               (Ljava/lang/String;)V
  #29 = Fieldref           #30.#32        // java/lang/System.out:Ljava/io/PrintStream;
  #30 = Class              #31            // java/lang/System
  #31 = Utf8               java/lang/System
  #32 = NameAndType        #33:#34        // out:Ljava/io/PrintStream;
  #33 = Utf8               out
  #34 = Utf8               Ljava/io/PrintStream;
  #35 = String             #36            // ing
  #36 = Utf8               ing
  #37 = Methodref          #38.#40        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #38 = Class              #39            // java/io/PrintStream
  #39 = Utf8               java/io/PrintStream
  #40 = NameAndType        #41:#28        // println:(Ljava/lang/String;)V
  #41 = Utf8               println
  #42 = Methodref          #43.#45        // java/io/InputStream.close:()V
  #43 = Class              #44            // java/io/InputStream
  #44 = Utf8               java/io/InputStream
  #45 = NameAndType        #46:#6         // close:()V
  #46 = Utf8               close
  #47 = Methodref          #48.#50        // java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
  #48 = Class              #49            // java/lang/Throwable
  #49 = Utf8               java/lang/Throwable
  #50 = NameAndType        #51:#52        // addSuppressed:(Ljava/lang/Throwable;)V
  #51 = Utf8               addSuppressed
  #52 = Utf8               (Ljava/lang/Throwable;)V
  #53 = Utf8               in
  #54 = Utf8               Ljava/io/InputStream;
  #55 = Utf8               StackMapTable
  #56 = Utf8               SourceFile
  #57 = Utf8               ByteCodeTest.java
{
  public com.ByteCodeTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/ByteCodeTest;

  public void tryResourceWithFinally() throws java.io.FileNotFoundException, java.io.IOException;
    descriptor: ()V
    flags: ACC_PUBLIC
    Exceptions:
      throws java.io.FileNotFoundException, java.io.IOException
    RuntimeVisibleAnnotations:
      0: #21()
    Code:
      stack=3, locals=4, args_size=1
         0: aconst_null
         1: astore_1
         2: aconst_null
         3: astore_2
         4: new           #22                 // class java/io/FileInputStream
         7: dup
         8: ldc           #24                 // String
        10: invokespecial #26                 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
        13: astore_3
        14: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
        17: ldc           #35                 // String ing
        19: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        22: aload_3
        23: ifnull        66
        26: aload_3
        27: invokevirtual #42                 // Method java/io/InputStream.close:()V
        30: goto          66
        33: astore_1
        34: aload_3
        35: ifnull        42
        38: aload_3
        39: invokevirtual #42                 // Method java/io/InputStream.close:()V
        42: aload_1
        43: athrow
        44: astore_2
        45: aload_1
        46: ifnonnull     54
        49: aload_2
        50: astore_1
        51: goto          64
        54: aload_1
        55: aload_2
        56: if_acmpeq     64
        59: aload_1
        60: aload_2
        61: invokevirtual #47                 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
        64: aload_1
        65: athrow
        66: return
      Exception table:
         from    to  target type
            14    22    33   any
             4    44    44   any
      LineNumberTable:
        line 14: 0
        line 15: 14
        line 16: 22
        line 17: 66
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      67     0  this   Lcom/ByteCodeTest;
           14      28     3    in   Ljava/io/InputStream;
      StackMapTable: number_of_entries = 6
        frame_type = 255 /* full_frame */
          offset_delta = 33
          locals = [ class com/ByteCodeTest, class java/lang/Throwable, class java/lang/Throwable, class java/io/InputStream ]
          stack = [ class java/lang/Throwable ]
        frame_type = 250 /* chop */
          offset_delta = 8
        frame_type = 65 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 9 /* same */
        frame_type = 9 /* same */
        frame_type = 249 /* chop */
          offset_delta = 1
}
SourceFile: "ByteCodeTest.java"


  public void tryResourceWithFinally() throws FileNotFoundException, IOException {
    try (InputStream in = new FileInputStream("")) {
      System.out.println("ing");
    }
  }

异常eclipse提示啥直接抛出来,不要 throws Exception ,不然前面的 astore_1/astore_2 你就看不懂了!!!

解析:要看懂try-catch-finally,必须关注 Exception table

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
any a1 = null
any a2 = null
any a3/in = new FileInputStream("")

System.out.println("ing")

  catch-1(33):
  // noop
  if(a3/in != null){
  in.close()
  }
  rethrow a1

if(a3/in != null){
in.close()
}

catch-2(44):
// noop
if(a3/in != null){
in.close()
}
if(a1 == null){
  // noop
  a1 = a2
  rethrow a1
} 

if (a1 == a2){
  rethrow a1
} 

a1.addSuppressed(a2)
rethrow a1

在添加自定义的finally:

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
$ javap -public -c ByteCodeTest.class
Compiled from "ByteCodeTest.java"
public class com.ByteCodeTest {
  public com.ByteCodeTest();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public void tryResourceWithFinally() throws java.io.FileNotFoundException, java.io.IOException;
    Code:
       0: aconst_null
       1: astore_1
       2: aconst_null
       3: astore_2
       4: new           #22                 // class java/io/FileInputStream
       7: dup
       8: ldc           #24                 // String
      10: invokespecial #26                 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
      13: astore_3
      14: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
      17: ldc           #35                 // String ing
      19: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      22: aload_3
      23: ifnull        79
      26: aload_3
      27: invokevirtual #42                 // Method java/io/InputStream.close:()V
      30: goto          79
      33: astore_1
      34: aload_3
      35: ifnull        42
      38: aload_3
      39: invokevirtual #42                 // Method java/io/InputStream.close:()V
      42: aload_1
      43: athrow
      44: astore_2
      45: aload_1
      46: ifnonnull     54
      49: aload_2
      50: astore_1
      51: goto          64
      54: aload_1
      55: aload_2
      56: if_acmpeq     64
      59: aload_1
      60: aload_2
      61: invokevirtual #47                 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
      64: aload_1
      65: athrow
      66: astore        4
      68: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
      71: ldc           #53                 // String finish
      73: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      76: aload         4
      78: athrow
      79: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
      82: ldc           #53                 // String finish
      84: invokevirtual #37                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      87: return
    Exception table:
       from    to  target type
          14    22    33   any
           4    44    44   any
           0    66    66   any
}


  public void tryResourceWithFinally() throws FileNotFoundException, IOException {
    try (InputStream in = new FileInputStream("")) {
      System.out.println("ing");
    } finally {
      System.out.println("finish");
    }
  }

在异常表里面多了一条记录,比上面的复杂点。

–END