2

我想使用某种编辑器打开由 Java 编译器生成的字节码(Java 二进制文件),它允许我查看字节码(原始,但人类可以理解,采用此处定义的格式)并直接在.class文件中修改内容。

我已经尝试过该javap实用程序。但是通过javap,我无法更改我的 .class 文件,而且它似乎也没有显示原始字节码

此外,我还看到过像JD Decompiler这样的反编译器,它为我提供了.class文件中的代码。但是我对源代码不感兴趣,我想查看字节码。

另外,我尝试过名为dirtyJOE & JBE的GUI 编辑器。这个编辑器很好,完成了我一半的目的。我可以看到文件中的各个字段.class并编辑我的.class文件。但这似乎也像一个翻译器,将字节码解码并显示在 UI 上以便于理解。它没有向我显示原始字节码。

甚至可以转到原始字节码吗?

我可能听起来很愚蠢,但我想确认这一点,以正确理解 Java 编译器和 JVM。

4

2 回答 2

9

可以转储原始字节码,javap -v但要以您可以编辑的格式获取类文件中的所有信息,我使用 ASM。您不使用它进行编辑的原因是;

  • 它不允许你做很多你在 Java 中做不到的事情。
  • 它很难使用。
  • 如果你打破它,你不会得到太多反馈。

字节码只是 Java 代码的编译版本,这毫无价值,这就是为什么它们做同样的事情的原因。JVM 可以自由地以多种方式对其进行优化,如果您想了解 JVM 的工作方式或代码的运行方式,那么理解字节码并不是很有用。


感谢@Antimony 建议使用手动编写的字节码。虽然其中一些是有效的,但它们要么相当先进,要么晦涩难懂,要么具有实现相同目标的替代方法。

一些想法

抛出已检查的异常而不声明它们,

Thread.currentThread().stop(checkedException);

捕获检查的异常而不声明它们,

   if (false) throw new CheckedException();
} catch(CheckedException ce) {

在 Java 中使用无效的标识符

有这么多有效但不是那么有用的字符可以使用我会使用其中一个

if( ⁀ ‿ ⁀ == ⁀ ⁔ ⁀ || ¢ + ¢== ₡)

for (char c‮h = 0; c‮h < Character.MAX_VALUE; c‮h++)
    if (Character.isJavaIdentifierPart(c‮h) && !Character.isJavaIdentifierStart(c‮h))
        System.out.printf("%04x <%s>%n", (int) c‮h, "" + c‮h);

由于标识符中的不可见字符导致文本向后打印,因此是有效代码。

http://vanillajava.blogspot.co.uk/2012/09/hidden-code.html

http://vanillajava.blogspot.co.uk/2012/08/uses-for-special-characters-in-java-code.html

在构造函数调用之前访问字段

我很确定这会导致VerifyError(或者在我尝试时确实如此)。相反,您可以使用

Object o = Unsafe.allocateInstance(clazz); // create without calling a constructor.

有条件地调用不同的构造函数

同样,我已经能够在对象创建和实例调用之间放置代码,但可能有一种方法可以让它工作。在Java中你会写

MyClass mc = condition ? new MyClass(a) : new MyClass(a, b);

对构造函数进行异常处理。

不确定您在这里的意思,因为您可以在构造函数中捕获或抛出异常。

多次分配最终字段或根本不分配

您可以改用反射并为最终字段提供默认值。即一个字段不可能没有价值。

有一个带有非strictfp 方法的strictfp 构造函数

这是一个有趣的,但不是我曾经需要的。

使用更宽松的类型检查

字节码确实允许不同的类型检查规则,但它们很容易与不太有用的 VerifyErrors 发生冲突。你可以做到这一点,但它很难得到正确的恕我直言。

从静态初始化程序抛出异常

您已经可以抛出未检查的异常,并且要抛出已检查的异常,您可以使用上面的技巧,但是我通常用 AssertionError 或类似的方法包装。

使用调用动态

您可以使用 MethodHandles,但它们在 Java 7 中相当笨重。我希望在 Java 8 中它们会更自然地使用。

使用子程序

有趣的是,我还没有尝试过,但我不确定它们比方法调用有什么优势。

使用异常控制流

没错,但这更有可能混淆 JIT 优化器,因此代码可能会变慢,因此可能不会给您带来您希望的优势。

使用旧的超级调用语义

你可以,但我不确定这有多大用处。

使用线程监视器

您可以使用

Unsafe.enterMonitor();
Unsafe.exitMonitor();
Unsafe.tryMonitorEnter();

字段的初始值。

我同意编译器并没有像我预期的那样使用这些,但是我再次不确定如果这样做会有什么不同。

于 2012-10-29T09:33:58.173 回答
2

有几个专为字节码操作而设计的库,例如 ASM,但我没有使用过它们,所以我不能对它们的功能或可用性说太多。

还有汇编器,旨在将人类可读的形式转换为二进制类文件(我什至自己写了一个)。据我所知,没有任何工具可以将类文件转换为汇编格式,但如果你有兴趣,我可以写一个。

对于简单的修改,如果您知道自己在做什么,也可以只使用十六进制编辑器。我以前在十六进制编辑器中编辑过几次课程。

于 2012-10-30T05:01:01.190 回答