英语原文共 9 页,剩余内容已隐藏,支付完成后下载完整资料
附录1 翻译译文
Java程序设计语言反编译器功能分析
Konstantins Gusarovs*
Riga Technical University, Riga, Latvia
除了新的工件开发,软件工程还包括其他任务。其中一项任务是二进制工件的逆向工程,它可以使用特殊的“反编译器”软件执行此任务。在本文中,作者对基于个人经验和软件开发人员调查结果选择的四种不同Java编程语言反编译器进行了比较。
关键词 - 反编译,Java,逆向工程。
1.介绍
虽然软件开发通常是关于产生新的工件。比如,将用某种编程语言编写的代码转换为二进制分布,但有时需要执行逆向操作,这就是逆向工程。逆向工程是从人为的任何东西中提取知识或设计蓝图的过程。关于软件工程,可以描述为从二进制(编译)文件中提取源代码。虽然乍一看这样的过程可能看起来与版权冲突,但有时是需要执行这样的操作。一个例子可能是修复公司不久前开发的程序或库中的缺陷的必要性,但缺少任何源代码。软件中逆向工程的其他方面可能是从没有可用源代码的给定库/程序中获取某些信息的必要条件——例如,加密密钥等。基本上,与自有产品的逆向工程相关的此类案例是有效的和合法的用例。另一个有效的逆向工程应用的例子是反病毒软件的作者对计算机病毒的研究,这是了解恶意软件如何工作以及如何对其起作用的必要条件。
基于TIOBE,企业开发中使用的最流行的编程语言之一是Java编程语言,它是一种面向对象的编程语言,它使用由基于堆栈的虚拟机执行的字节码指令。Java是围绕字节码而不是汇编语言构建的这一事实提供了几个优点——例如,一旦编写一次的代码就可以在不同的平台上执行,前提是上述平台存在虚拟机实现。从逆向工程的角度来看,这意味着只需要处理字节码,与汇编语言相比,字节码包含的指令更少。因此,为了将Java字节码转换为源代码,此时必须能够为最新的Java版本(10)转换大约200条不同的指令,与英特尔处理器相比较 装配指令集包含大约2000个不同的指令似乎是一个更容易的任务。
这样的任务可以通过名为“反编译器”的软件来执行,它将二进制工件转换为具有一定精度的源代码。Java编程语言存在几个反编译器,本文的目的是比较这些反编译器,以便为软件开发人员提供建议。
此篇文章的结构如下。 在第二节中,给出了所选的Java编程语言反编译器软件列表。 第三节简要介绍了Java编程语言的二进制文件格式和字节码指令。第四节给出了反编译软件使用的Java字节码反编译技术的几个例子。在第五节中,描述了由论文作者开发的测试用例。第VI节显示了测试用例反编译的结果以及对所得结果的简短分析。 第VII节描述了额外的测试结果以及使用本文作者定义的其他标准对反编译器进行比较。最后,在最后一节中给出了结论,并给出了关于Java反编译器软件的建议。
2. java反编译软件
有好几个反编译程序存在Java编程语言中。 为了选择使用一个,有必要进行这些程序的比较。在本节中,作者提供了此类软件的列表。使用者在使用此类软件时的个人经验在其当前工作场所执行的调查结果来构建反编译器软件列表,以确定其他程序员为推荐此类任务而建议使用的内容:
- JD Project是一个模块化反编译器,可以作为独立应用程序运行,也可以集成到开发环境中,例如Eclipse或Intellij IDEA。
- CFR以包含命令行界面(CLI)的库的形式分发,也可以用作其他软件的一部分。
- Procyon是一个可以集成到其他应用程序中并包含CLI的框架。 存在多个图形用户界面(GUI)实现。
- Fernflower是一个Java反编译器Intellij IDEA开发环境。 它以库的形式分发,也具有CLI接口。
上述关于Java反编译器软件的调查是基于两个问题:
- 您熟悉哪些Java反编译器?
- 您建议使用哪种Java反编译器?
表1:
哪个java反编译器你最熟悉?
反编译器 |
总回答数 |
Fernflower |
200 |
JD Project |
158 |
Procyon |
141 |
CFR |
50 |
JAD |
2 |
表2:
哪个java反编译器你推荐使用?
反编译器 |
总回答数 |
Fernflower |
121 |
JD Project |
74 |
Procyon |
44 |
CFR |
8 |
调查结果表明,大多数人都熟悉Fernflower反编译软件并建议使用它,这可以通过它被内置到公司使用的开发环境中来解释,内置的开发环境是Intellij IDEA。
存在Java编程语言反编译器的有其他几种实现方式; 但是,在大多数情况下,这些实现已过时且不受支持。 因此,在本文中,比较了这四个反编译器。
3. java虚拟机二进制文件格式的介绍
Java虚拟机(JVM)使用包含源代码编译结果的二进制.class文件。 这些文件包含有关编译单元(基本上是Java类或接口)的所有必要信息,包括:
- 生成给定.class文件的编译器版本。 这允许JVM检测它是否应该能够加载和执行给定文件。
- 包含各种字符串文字的常量池,类和接口名称,字段和方法名称以及给定编译单元中使用的其他常量。
- 访问标志,用于确定给定编译单元的可见性和类型。 此信息定义文件中包含的实际类是否以及如何实例化和子类化。
- 有关基类和接口的信息,编译单元继承或实现。
- 字段和方法列表及其访问标志和其他修饰符。
- 类的属性,其使用的字段和方法确定有关上述编译单元组件的其他信息。 其中一个属性是方法的实际代码,其他属性表示可以在运行时使用的不同信息,例如注释,它们是附加到给定成员的语法元数据,或者在执行期间可能抛出的选定方法异常列表。code属性包含将在适当的方法调用期间使用的实际字节码指令列表。 正如已经提到的那样,JVM字节码包含大约。 200条指令可分为以下几组:
- 数学运算 - 这些指令用于实际的数学运算表示(例如,DADD指令总结2个双类型变量),以及在堆栈顶部的常量加载(例如,ICONST_0 ... ICONST_5指令允许加载整数 从0到5)。
- 堆栈操作 - JVM是基于堆栈的虚拟机,这意味着它不使用任何类型的寄存器。 相反,所有局部变量都被加载到堆栈上并可以在其上进行处理。这些指令允许写入和读取堆栈顶部包含的信息(例如,ALOAD允许将对象推送到堆栈,而ASTORE获取它并存储在局部变量中),以及在顶部创建新对象。 stack(NEW允许创建一个新对象,而NEWARRAY创建一个给定类型的新数组)。该组中的几个指令也用于复制对象(DUP在堆栈顶部创建变量的副本并将其推到顶部)或从堆栈中删除它们而不存储到任何局部变量(POP)。
- 类型转换说明 - 例如,D2I将堆栈顶部的double类型变量转换为int类型,并将结果推送到堆栈顶部。
- 类型检查指令,允许检查堆栈顶部的变量类型,并将其替换为检查结果(INSTANCEOF)或抛出运行时异常(CHECKCAST)。
- 数字类型比较指令 - 用于LCMP比较堆栈顶部的两个长类型变量。
- 同步指令MONITORENTER和MONITOREXIT,用于获取对给定资源的互斥访问。
- 方法调用指令,例如INVOKEVIRTUAL,允许以不同方式调用方法。
- 允许被调用方法将其调用结果返回给其他正在运行的代码的指令 - 例如,ARETURN允许使用对象作为调用结果,而RETURN表示该方法根本没有返回任何结果。
- 分支指令,用于在代码执行期间更改程序流。 JVM具有条件(例如,如果两个对象通过引用相同而改变流的IFACMP_EQ)和无条件分支指令(GOTO)。 交换语言构造处理也存在特殊指令,例如TABLESWITCH。
- 调试器指令BREAKPOINT不是包含在已编译的Java代码中。 相反,调试器会动态注入此指令。
- ATHROW是指在抛出异常的指令。
- ARRAY LENGTH允许将数组长度放在堆栈顶部的指令。
- NOP是空指令。
class文件中的每个方法都包含有关正在使用的局部变量的信息。 它可能包含也可能不包含变量名称的信息 - 它取决于Java代码的编译方式。 如果在编译期间省略了局部变量名,那么只有局部变量逻辑数(索引)和类型的信息。
4. JVM字节码反编译技术
从上一节可以看出,Java字节码的反编译需要从.class文件中提取其成员(如字段和方法)的信息,将适当的方法字节码转换为源代码,并为所有的访问标志添加附加信息。 类文件的一部分。
在本文中,作者关注字节码转换的可能性,因为其他反编译任务可以通过提取必要的信息和使用简单的转换技术以直接的方式处理。
至于字节码,有必要了解大多数JVM字节码指令也可以以非常简单和直接的方式处理。在这种情况下,作者正在讨论除分支之外的所有字节码指令组。 反编译器还需要在方法调用期间将轨道保持在JVM堆栈的状态,因此可以确定哪些对象正在堆栈上加载并从中读取。这还需要分析局部变量表以确定在方法调用期间使用哪些局部变量。作者希望提供几个关于这些指令组的反编译技术如何工作的例子。
第一个例子如图1所示。在这种情况下,代码本身由三个数学指令组成 - 堆栈顶部的两个加载整数常量,给定整数的第三个和。给定字节码示例中的最后一条指令告诉JVM使用堆栈顶部的变量作为方法的返回值。
图1:JVM字节码使用数学运算
ICONST_1 ICONST_2 IADD IRETURN
要从给定的字节代码片段恢复源代码,反编译器必须跟踪在此字节码执行期间实际发生的情况。可以看到,由于第一个指令调用,整数常量1被放在堆栈的顶部,这导致以下堆栈状态:[1]第二条指令将整数常量2放在堆栈的顶部,因此堆栈变为:[2,1]。 然后,下一条指令移除两个顶部堆栈成员,将它们相加并将此数学运算的结果放在堆栈顶部,因此堆栈将转为[1 2]。最后,个字节码指令告诉JVM使用堆栈顶部的变量作为方法返回值。通过遵循该信息,看出给定的字节码片段对应于图2中所示的Java源代码。
return 1 2;
图2:第一个字节码片段反编译结果
图3显示了一个类似的字节码片段,唯一的例外是使用局部变量而不是整数常量。
ILOAD 1
ILOAD 2 IADD IRETURN
图3:JVM字节码使用数学运算和局部变量
此字节码片段的反编译逻辑与前一个示例中的相同:反编译器应该跟踪每个指令执行时堆栈状态应该变为什么,并使用此信息重建源代码。当在字节码指令中使用局部变量时,反编译器应引用适当的方法的局部变量表来确定它们是否存在时的实际变量名称。图4显示了这个字节码反编译结果的两种可能结果:第一种假设存在局部变量名,而第二种假定在编译过程中删除了这些信息,反编译器必须根据它们的索引生成变量名。
return a b;
return var1 var2;
图4:第二个字节码片段反编译结果
可以看出,在不考虑分支指令的情况下,反编译器的任务是重建在适当的字节码指令调用期间会发生什么,并因其工作而发出适当的语法结构。如果分支指令也出现在字节码中,则需要分析控制流被分支指令重定向到的位置。 图5提供了第一个带分支指令的字节码示例。
ILOAD 1
ILOAD 2 IF_ICMPLT L1 ICONST_1 IRETURN
L1:
ICONST_2 IRETURN
图5:第一个分支的例子
这里,比较两个局部整数变量,如果第二个小于第一个,则出现控制流重定向。 它在分支指令后面向标签,因此反编译器应该能够确定这种分支对应于if语言结构。反过来,在分支指令之后可以找到的指令对应于if语句的else部分。 因此,反编译结果应该如图6所示。
if (a lt; b) {
<strong
剩余内容已隐藏,支付完成后下载完整资料</strong
资料编号:[609246],资料为PDF文档或Word文档,PDF文档可免费转换为Word
课题毕业论文、外文翻译、任务书、文献综述、开题报告、程序设计、图纸设计等资料可联系客服协助查找。