如何在运行时禁用 JVM 字节码验证?

问题描述 投票:0回答:1

我确实知道可以通过指定参数

-Xverify:none
来禁用验证,但是如果没有这个起始参数,我该如何做到这一点,以便 JVM 不会阻止我使用
redefineClasses
在运行时注入代码?如果不是这样,JVM 只会给我 jvmtiError
JVMTI_ERROR_FAILS_VERIFICATION = 62

我搜索了整个互联网,但找不到我所期望的(也许我错过了正确的解决方案)。我想要在运行时修改 JVM 参数,注入

ClassLoader
并删除验证代码,或者只是简单地调用某些方法或修改某些字段的值,这至少可以在 Java 8 上工作。

java c java-native-interface code-injection jvmti
1个回答
0
投票

即使验证器处于“打开”状态,您也可以在运行时重新定义类并注入代码。不过,您注入的字节码必须通过验证程序。这通常并不是特别困难。这听起来像是一个 X/Y 问题:根本问题是您注入字节码的代码写得很糟糕(生成验证者不喜欢的东西),因此您会遇到 FAILS_VERIFICATION 错误,并且您现在正在询问有关如何让验证者闭嘴。您应该询问有关字节码生成代码的问题。您可能认为如果验证器打开,就不可能注入代码。这是错误的——验证器只是检查 JVM 加载的字节码是否满足某些先决条件。例如,如果验证器打开,则无法注入该字节码序列 - 不是因为“验证器阻止注入”,而是因为该代码无法通过验证器。如果您尝试注入它,或者只是像平常一样从磁盘加载它,这并不重要:

ICONST_0 // push int constant value '0' on the stack
ICONST_1 // push int constant value '1' on the stack
FADD     // pop 2 floats off the stack, add them, push result on stack

这是不允许的,因为验证者可以知道在

FADD
点,堆栈上应该有2个浮点数,但是有2个整数,这是不行的:JVM不会让你处理整数作为浮点数,即使在机器代码中这也会做一些事情(即,将 int 视为浮点数并以这种方式添加)。一个问题是 JVM 无法控制这里实际发生的事情 - 它取决于体系结构,而这不是 java 的本意。之所以称为验证,是因为绝大多数违规行为都会让您访问内存中代码不应该访问的内容,例如将 int 压入堆栈,然后将其弹出到 Object 类型的字段中,让您进行“指针算术” ’这是‘不安全’的,原因我希望不必在这里展开。

简单的答案:停止做不安全的事情

但是...我必须

如果您注入的只是java代码,那么只需生成非验证器破坏代码。请注意,有很多事情人们认为无法以验证者安全的方式完成,但可以,因此在你真正承诺“这不能完成”之前,你可能需要提出第二个问题来仔细检查无需关闭验证器'。但是,假设你真的“必须”..

抱歉。你不能。

Java 正在将许多过去可在运行时配置的东西移至命令行参数中。明确提出“安全”目标,即如果您能够绕过它,OpenJDK 团队就会认为这是“安全漏洞”。这就是为什么模块不允许允许在没有命令行--add-opensarg的情况下进行反射。有很多方法可以解决这个问题,但是每个 JDK 版本都会关闭其中的一些方法,并且每种方法要么不起作用,要么会在命令行。

如果有一种方法可以进行字节码注入,并且不需要通过验证器

没有

向 JVM 提供一堆命令行选项来表明系统操作员是这样想的,那么这将是一个漏洞即将关闭。毕竟,如果您能够在 OpenJDK API 和设计的支持下做到这一点,那么 OpenJDK 为删除库所依赖的大量功能而烦扰 Java 社区而付出的巨大努力绝对没有任何意义。他们重写,或者更糟糕的是,毁掉他们的 API/发布重大更新。这不是“绝对的证明”,但希望应该足够了:OpenJDK 所祝福的唯一方法(不会被视为在下一个 java 版本中关闭的漏洞)无需验证且无需验证的注入方式如果 OpenJDK 团队只是编造借口来惹恼社区并毁掉产品,那么命令行选项是可能的。有可能,但是..那会非常奇怪。 这个问题可能归结为:当然,无论如何,没问题。只要告诉我 java.exe 0days,我将处理新的 java 版本来堵住这些漏洞。那是..可能不是一个有效的问题,或者如果是的话,不太可能给你任何答案。此类黑客攻击将与 OpenJDK 协调,或者在黑帽市场上出售。未发布在 Stack Overflow 上。

© www.soinside.com 2019 - 2024. All rights reserved.