以可读格式打开Java字节代码并进行编辑

Viv*_*vek 2 java jvm bytecode javac .class-file

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

我试过这个javap实用程序.但通过javap,我无法更改我的.class文件,它似乎也没有显示原始字节代码

此外,我已经看到像JD Decompiler这样的编译器,它给了我来自.classFile 的代码.但我对源代码不感兴趣,我想查看字节码.

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

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

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

Pet*_*rey 9

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

  • 它不允许你做很多你不能用Java做的事情.
  • 它更难,更难使用.
  • 如果打破它,你不会得到很多反馈.

它的价值不在于字节代码只是为Java代码编译的,这就是为什么它们做同样的事情.JVM可以通过多种方式自由地优化它,这使得如果您想了解JVM的工作原理或代码运行方式,理解字节代码并不是非常有用.


感谢@Antimony建议用于手动编写的字节代码.虽然它们中的一些是有效的,但它们要么相当先进或模糊,要么有另一种方法来实现同样的目的.

一些想法

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

Thread.currentThread().stop(checkedException);
Run Code Online (Sandbox Code Playgroud)

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

   if (false) throw new CheckedException();
} catch(CheckedException ce) {
Run Code Online (Sandbox Code Playgroud)

在Java中使用标识符无效

有许多有效但不太有用的字符可供我使用其中之一

if( ? ? ? == ? ? ? || ¢ + ?== ?)
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

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

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.
Run Code Online (Sandbox Code Playgroud)

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

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

MyClass mc = condition ? new MyClass(a) : new MyClass(a, b);
Run Code Online (Sandbox Code Playgroud)

在构造函数上进行异常处理.

不知道你在这里是什么意思,因为你可以在构造函数中捕获或抛出异常.

分配最终字段不止一次或根本不分配

您可以使用反射,并将最终字段设为默认值.即一个领域不可能没有价值.

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

这是一个有趣的,但不是我需要的.

使用laxer类型检查

字节代码允许不同的类型检查规则,但它们很容易与不那么有用的VerifyErrors相悖.你可以做到这一点,但很难得到正确的恕我直言.

从静态初始化器中抛出异常

你可以抛出未经检查的异常,并抛出已检查的异常,你可以使用上面的技巧,但我通常用AssertionError等包装.

使用invokedynamic

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

使用子程序

有趣的是,我没有试过这个,但我不确定他们会给方法调用带来什么好处.

使用异常控制流程

没错,但这更容易混淆JIT优化器,因此代码可能会因此而变慢,因此可能无法为您提供您可能希望的优势.

使用旧的超级调用语义

你可以,但我不确定它是多么有用.

使用线程监视器

您可以使用

Unsafe.enterMonitor();
Unsafe.exitMonitor();
Unsafe.tryMonitorEnter();
Run Code Online (Sandbox Code Playgroud)

字段的初始值.

我同意编译器没有像我期望的那样使用这些,但是我不确定它们如果有的话会有什么不同.