编译为 .class 并反编译回来时是否丢失了任何信息?
Is any information lost when compiling to a .class and decompiling back?
我认为java对反编译应该是比较友好的,也就是说.class文件本身有相当多的数据,仍然与原始源相当相似。
是否可以从反编译的 .class 文件构建原始源代码,这将是完全等效的,或者是否有任何 java 构造在过程中得到转换?
例如,我可以看到像增强的 for 循环这样的语法糖会导致问题。
我是否可以多次重复这个过程并仍然到达我开始的地方?
有几种反编译器可以执行此操作,这通常取决于代码的复杂性。根据我的经验,泛型有时会干扰反编译,但常规 POJO 可以 return 完全 class。
简单的回答是,是的,信息丢失了。
尤其是评论总是丢失。原始变量名是否丢失取决于编译代码时的编译器选项。使用调试选项 (-g) 编译的代码在反编译时将更具可读性。
反编译时,您得到的源代码在功能上等同,但与反编译时的源代码不完全相同。
考虑这两种方法:
public class MyTest {
public String hi(String name) {
return "Hi " + name;
}
public String howdy(String name) {
return new StringBuilder().append("Hi ").append(name).toString();
}
}
这里是反编译代码:
$ javap -c MyTest.class
Compiled from "MyTest.java"
public class MyTest {
public MyTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public java.lang.String hi(java.lang.String);
Code:
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: ldc #4 // String Hi
9: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: aload_1
13: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
19: areturn
public java.lang.String howdy(java.lang.String);
Code:
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: ldc #4 // String Hi
9: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: aload_1
13: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
19: areturn
}
这只是语言中视觉上不同结构的一个简单示例,它们被编译为完全相同的字节码。因此,严格来说,您不能生成完全相同的代码。反编译器也不会看到局部变量名,除非编译器添加了调试信息。
还有像 ProGuard 这样的字节码混淆工具,声称无法自动反编译。
但是,如果反编译器成功生成了 java 代码,那么对其进行编译和反编译(使用相同的编译器和反编译器)应该会生成相同的 java 代码。
我认为java对反编译应该是比较友好的,也就是说.class文件本身有相当多的数据,仍然与原始源相当相似。
是否可以从反编译的 .class 文件构建原始源代码,这将是完全等效的,或者是否有任何 java 构造在过程中得到转换?
例如,我可以看到像增强的 for 循环这样的语法糖会导致问题。
我是否可以多次重复这个过程并仍然到达我开始的地方?
有几种反编译器可以执行此操作,这通常取决于代码的复杂性。根据我的经验,泛型有时会干扰反编译,但常规 POJO 可以 return 完全 class。
简单的回答是,是的,信息丢失了。
尤其是评论总是丢失。原始变量名是否丢失取决于编译代码时的编译器选项。使用调试选项 (-g) 编译的代码在反编译时将更具可读性。
反编译时,您得到的源代码在功能上等同,但与反编译时的源代码不完全相同。
考虑这两种方法:
public class MyTest {
public String hi(String name) {
return "Hi " + name;
}
public String howdy(String name) {
return new StringBuilder().append("Hi ").append(name).toString();
}
}
这里是反编译代码:
$ javap -c MyTest.class
Compiled from "MyTest.java"
public class MyTest {
public MyTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public java.lang.String hi(java.lang.String);
Code:
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: ldc #4 // String Hi
9: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: aload_1
13: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
19: areturn
public java.lang.String howdy(java.lang.String);
Code:
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V
7: ldc #4 // String Hi
9: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: aload_1
13: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
19: areturn
}
这只是语言中视觉上不同结构的一个简单示例,它们被编译为完全相同的字节码。因此,严格来说,您不能生成完全相同的代码。反编译器也不会看到局部变量名,除非编译器添加了调试信息。
还有像 ProGuard 这样的字节码混淆工具,声称无法自动反编译。
但是,如果反编译器成功生成了 java 代码,那么对其进行编译和反编译(使用相同的编译器和反编译器)应该会生成相同的 java 代码。