如果我在运行前手动更改字节码会发生什么?

时间:2014-08-13 17:29:09

标签: java

如果我在执行前手动将某些内容更改为字节码,我会有点好奇。例如,假设将int类型变量分配到字节类型变量而不强制从程序中某处删除分号或导致编译时错误的任何内容。据我所知,在创建.class文件之前,所有编译时错误都由编译器检查。那么当我成功编译程序后手动更改字节码后更改字节代码会发生什么?有没有机制来处理这个?或者如果没有,那么程序在执行后的行为如何?

编辑: -
作为Hot Licks,Darksonn和manouti已经给出了正确的满意答案。现在我只是为那些寻求这类问题答案的读者做出结论: -

每个 Java虚拟机都有一个类文件验证程序,可确保加载的类文件具有适当的内部结构。如果类文件验证程序发现类文件有问题,则抛出 异常。因为类文件只是一系列二进制数据,所以虚拟机无法知道特定的类文件是由一个善意的Java编译器生成的,还是由于那些一心想破坏虚拟机完整性的黑客破解者。因此,所有JVM实现都有一个类文件验证器,可以在不受信任的类上调用,以确保类可以安全使用。

有关详细信息,请参阅this

4 个答案:

答案 0 :(得分:5)

您当然可以使用十六进制编辑器(例如,免费的“HDD Hex Editor Neo”)或其他工具来修改Java .class文件的字节。但显然,您必须以维护文件“完整性”(表格格式正确等)的方式执行此操作。此外(并且更加棘手),您所做的任何修改都必须通过JVM的“验证程序”进行调整,该验证程序基本上会重新检查编译程序时 javac 验证的所有内容。

验证过程在类加载期间发生,并且非常复杂。基本上,对每个过程进行数据流分析,以确保只有正确的数据类型可以“到达”假定数据类型的点。例如,当加载引用的最终用户假定它是一个String时,您无法更改加载操作以将对HashMap的引用加载到“堆栈”。 (但枚举验证程序所做的所有检查本身就是一项重要任务。即使我为IBM iSeries JVM编写了验证程序,我也记不清其中的一半。)

(如果你问是否可以“越狱”Java .class文件来引入做未经授权的事情的代码,答案是否定的。)

答案 1 :(得分:2)

您最有可能获得java.lang.VerifyError

  

当“验证程序”检测到类文件虽然格式正确但包含某种内部不一致或安全问题时抛出。

答案 2 :(得分:1)

你当然可以做到这一点,甚至还有一些工具可以让它变得更容易,比如http://set.ee/jbe/。 Java运行时将运行修改后的字节码,就像运行编译器发出的字节码一样。您所描述的是binary patch的特定于Java的案例。

分号示例不会成为问题,因为分号只是为了方便编译器而不会出现在字节码中。

答案 3 :(得分:1)

字节码正常执行并执行给定的指令或jvm拒绝它们。

我前段时间使用jasmin直接用java字节码编程,我注意到了一些事情。

如果您编辑的字节码有意义,它将按预期运行。但是有些字节码模式会被VerifyError拒绝。

对于超出范围访问的特定检查,您可以编译带有越界的代码就好了。他们会在运行时为您提供ArrayIndexOutOfBoundsException

int[] arr = new int[20];
for (int i = 0; i < 100; i++) {
    arr[i] = i;
}

但是,你可以构造比那更基本缺陷的字节码。举个例子,我先解释一下。

java字节码与stack一起使用,指令与堆栈中的顶部元素一起使用 堆栈在程序的不同位置自然会有不同的大小,但有时你可能会在字节码中使用goto来使堆栈看起来不同,具体取决于你到达那里的方式。

堆栈可能包含object, int,然后将对象存储在对象数组中,将int存储在int数组中。然后你继续从该字节码中的其他地方继续使用goto,但现在你的堆栈包含int, object,这将导致int被传递给对象数组,反之亦然。

这只是可能发生的事情的一个例子,它会使你的字节码存在根本缺陷。当在运行时加载类时,JVM会检测到这些类型的缺陷,如果某些东西不起作用,则会发出VerifyError。