踏上 Java 字节码世界之旅?本文涵盖了入门所需了解的所有内容。
早在 1995 年,Java 编程的创建者 Sun Microsystems语言,提出了大胆的主张。他们说 Java 可以让你“编写一次,随处运行”。这意味着编译后的二进制文件将能够在任何系统架构上运行,这是 C 无法做到的,并且至今仍然是编写 Java 的核心租户。
为了实现这种跨平台功能,Java 采用了编译时采用独特的方法。 Java 不是将源代码直接转换为机器代码(机器代码特定于每个系统架构),而是将其程序编译为称为字节码的中间形式。字节码是一组指令,既不依赖于特定的机器语言,也不依赖于任何特定的硬件架构。这种抽象是Java可移植性的关键。
解释和执行Java字节码指令的程序称为Java虚拟机(JVM)。 JVM 将每个字节码指令翻译成其运行所在的特定系统架构本机的机器代码。此过程通常称为“即时”(JIT) 编译,允许 Java 字节码在任何给定平台上尽可能高效地执行。
字节码是不过,它不仅仅对 JVM 有用。由于 Java 类的字节码有助于逆向工程、性能优化、安全研究和其他静态分析功能,因此 JDK 附带了实用程序来帮助您和我检查它。
看一下以下示例字节码,请考虑以下来自 `java.lang.Boolean`、`booleanValue` 和 `valueOf(boolean)` 的两个方法,它们分别对 `boolean` 原始类型进行拆箱和装箱:
public boolean booleanValue() { return value; } public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); }
使用 ` javap` 命令是 JDK 附带的,我们可以看到每个命令的字节码。您可以通过使用“-c”命令和类的完全限定名称运行“javap”来完成此操作,如下所示:
javap -c java.lang.Boolean
结果是“中所有公共方法的字节码” java.lang.Boolean`。这里我只复制了 `booleanValue` 和 `valueOf(boolean)` 的字节码:
public boolean booleanValue(); code: 0: aload_0 1: getfield #7 // Field value:Z 4: ireturn public static java.lang.Boolean valueOf(boolean); Code: 0: iload_0 1: ifeq 10 4: getstatic #27 // Field TRUE:Ljava/lang/Boolean; 7: goto 13 10: getstatic #31 // Field FALSE:Ljava/lang/Boolean; 13: areturn
乍一看,这是一门全新的需要学习的语言。然而,当您了解每条指令的作用以及 Java 使用堆栈进行操作时,它很快就会变得简单。
以 `booleanValue` 的三个字节码指令为例:
`aload_n` 表示将对局部变量的引用放入堆栈。在类实例中,`aload_0` 引用 `this`。
`getfield` 表示从 `this`(堆栈中最下面的一项)读取成员变量并将其放置将值入栈
`#7`指引用在常量池中的索引
`//字段值:Z`告诉我们`#7`指的是一个名为`value`的`boolean`类型的字段(Z)
`ireturn`意味着弹出一个原始值从堆栈中取出并返回它
长话短说,这三个指令查找实例的“value”字段并返回它。
作为第二个示例,采用看下一个方法, `valueOf(boolean)`:
`iload_n` 意味着将一个原始局部变量放入堆栈。 `iload_0` 指的是第一个方法参数(因为第一个方法参数是一个原语)
`ifeq n` 表示将值从堆栈中弹出并查看是否为 true;如果是,则继续到下一行,否则跳转到行 `n`
`getstatic #n` 表示将静态成员读取到堆栈
`#27` 指的是常量池中静态成员的索引
`// 字段 TRUE:Ljava/lang/Boolean` 告诉我们 `#27` 指的是什么,类型为 `Boolean
`goto n` 的静态成员名为 `TRUE` 表示现在跳转到字节码中的 `n` 行
`areturn` 表示从堆栈中弹出引用并返回它
换句话说,这些说明说,如果为 true,则采用第一个方法参数,然后返回`Boolean.TRUE`;否则,返回 `Boolean.FALSE`。
我之前提到过这对于逆向工程、性能优化和安全研究很有帮助。现在让我们扩展一下这些内容。
使用第三方库或闭源组件时,字节码分析成为一个强大的工具。反编译字节码可以让您了解这些库的内部工作原理,有助于集成、故障排除并确保兼容性。
在遇到专有或闭源 Java 代码的情况下,读取字节码可能是唯一可行的方法的方式来了解其功能。字节码分析允许您进行逆向工程并理解闭源应用程序的行为,从而促进互操作性或定制。
举一个现实生活中的例子,我最近尝试将第三方包缠结分析工具集成到我们的 Ci 系统中。不幸的是,该供应商是闭源的,并且只有有关如何通过其专有 UI 访问该库的文档。通过分析字节码,我能够对底层分析引擎的预期输入和输出进行逆向工程。
以上是如何阅读 Java 字节码既有趣又有利可图的详细内容。更多信息请关注PHP中文网其他相关文章!