Java 类加载器:如何使用不同于查找名称的二进制名称加载 class?
Java Classloader: How to load class with binary name different from looked up name?
编辑:好像有点不一样,先读一下Edit2。
如何加载二进制名称与查找名称不同的 class?
我知道 Java 规范不允许这样做,但我面前有一个应用程序以某种方式做到了这一点。
我有一个专有服务器(Windows 运行 JVM 的 exe),创建它的公司已不存在。因为我终于遇到了一个错误,所以我开始提取 classes 来修复它,同时将它移动到 Linux。 classes 被混淆了,但这不是困扰我的问题。
我操纵他们的顶级类加载器在他们调用 defineClass
后转储每个 class 文件(它从加密存档加载 classes)。在那里我看到它寻找并定义了一个 class P.x 但是转储的 class 文件包含一个 class 二进制名称 P.X (大小写不匹配)(有内部 classes 命名为 P.X$Y,因此它们匹配二进制名称)。大小写不匹配应该在 defineClass()
中抛出 NoClassDefFoundError
,但由于我直接在 defineClass()
之后转储了 class,所以显然不会。
在我理解之前,我有点不敢继续。因为他们可能包含了一些陷阱,比如 classes 不能加载并稍后检查。因此,如果我只是继续而不详细了解其工作原理,服务器可能会 return 错误结果。
从他们打包 JVM 的方式来看,我认为他们没有操纵 JVM。从 OpenJDK 代码来看,这些东西在 C++ 部分,所以他们不能仅仅替换一些 class 文件来实现它。
在不破解JVM的情况下,我只能想到3种方法:
- 我希望它可能是一些秘密的 VM arg,但到目前为止我什么也没发现:https://web.archive.org/web/20100130070337/http://www.md.pp.ru/~eu/jdk6options.html
- 替换
java.lang.ClassLoader
并将传入的名称设置为空
所以只有二进制名称很重要。但这真的有用吗?
- 以某种方式使用
ClassFileTransformer
。但这真的有用吗?
我没有找到他们做 2) 或 3) 的线索。而且我不知道这些是如何工作的,因为代码会查找 P.x 所以即使 P.X 会被加载,所以不确定这些是否有帮助。
有人知道如何做到这一点吗?他们在 Windows.
上使用 32 位 JRE Hotspot 1.6.0_01-b06,混合模式
法律说明:在我的国家,我被明确允许修复一个我花了很多钱买的软件,但它不再受支持。
编辑 1:
我现在将做同样事情的我的小样本注入到他们的代码中。它失败并显示 NoClassDefFoundError
,因此他们不会操纵 JVM 或 java.lang.ClassLoader,因此不会在全局范围内完成此测试。
编辑2:
它可能是文件系统,但正如评论中所建议的那样(我覆盖文件)。
因为我现在可以附加调试器,所以我看到了一些奇怪的东西:进入 defineClass()
的 byte[]
包含正确的二进制名称。
如果 P/x 和 P/X 存在,我会覆盖一个,因为 Windows FS 不区分大小写。呵呵。如果我确定是这样的话,我会回来的。
也许他们绕过了正常的 Classloader#defineClass 实现并使用 sun.misc.Unsafe#defineClass 代替?
问题是 Windows 文件系统不区分大小写(但不是因为像评论中建议的那样加载 classes,而是因为我写了文件转储)。
我把class的名字写出来了,但是因为P.x和P.X存在,所以我用P.X覆盖了P.x。
所以它只寻找我,好像它正在从文件 P.x 加载 P.X,因为在我转储的文件中 P.x 是 class P.X因为那个错误。
谢谢大家,抱歉。
编辑:好像有点不一样,先读一下Edit2。
如何加载二进制名称与查找名称不同的 class?
我知道 Java 规范不允许这样做,但我面前有一个应用程序以某种方式做到了这一点。
我有一个专有服务器(Windows 运行 JVM 的 exe),创建它的公司已不存在。因为我终于遇到了一个错误,所以我开始提取 classes 来修复它,同时将它移动到 Linux。 classes 被混淆了,但这不是困扰我的问题。
我操纵他们的顶级类加载器在他们调用 defineClass
后转储每个 class 文件(它从加密存档加载 classes)。在那里我看到它寻找并定义了一个 class P.x 但是转储的 class 文件包含一个 class 二进制名称 P.X (大小写不匹配)(有内部 classes 命名为 P.X$Y,因此它们匹配二进制名称)。大小写不匹配应该在 defineClass()
中抛出 NoClassDefFoundError
,但由于我直接在 defineClass()
之后转储了 class,所以显然不会。
在我理解之前,我有点不敢继续。因为他们可能包含了一些陷阱,比如 classes 不能加载并稍后检查。因此,如果我只是继续而不详细了解其工作原理,服务器可能会 return 错误结果。
从他们打包 JVM 的方式来看,我认为他们没有操纵 JVM。从 OpenJDK 代码来看,这些东西在 C++ 部分,所以他们不能仅仅替换一些 class 文件来实现它。
在不破解JVM的情况下,我只能想到3种方法:
- 我希望它可能是一些秘密的 VM arg,但到目前为止我什么也没发现:https://web.archive.org/web/20100130070337/http://www.md.pp.ru/~eu/jdk6options.html
- 替换
java.lang.ClassLoader
并将传入的名称设置为空 所以只有二进制名称很重要。但这真的有用吗? - 以某种方式使用
ClassFileTransformer
。但这真的有用吗?
我没有找到他们做 2) 或 3) 的线索。而且我不知道这些是如何工作的,因为代码会查找 P.x 所以即使 P.X 会被加载,所以不确定这些是否有帮助。
有人知道如何做到这一点吗?他们在 Windows.
上使用 32 位 JRE Hotspot 1.6.0_01-b06,混合模式法律说明:在我的国家,我被明确允许修复一个我花了很多钱买的软件,但它不再受支持。
编辑 1:
我现在将做同样事情的我的小样本注入到他们的代码中。它失败并显示 NoClassDefFoundError
,因此他们不会操纵 JVM 或 java.lang.ClassLoader,因此不会在全局范围内完成此测试。
编辑2:
它可能是文件系统,但正如评论中所建议的那样(我覆盖文件)。
因为我现在可以附加调试器,所以我看到了一些奇怪的东西:进入 defineClass()
的 byte[]
包含正确的二进制名称。
如果 P/x 和 P/X 存在,我会覆盖一个,因为 Windows FS 不区分大小写。呵呵。如果我确定是这样的话,我会回来的。
也许他们绕过了正常的 Classloader#defineClass 实现并使用 sun.misc.Unsafe#defineClass 代替?
问题是 Windows 文件系统不区分大小写(但不是因为像评论中建议的那样加载 classes,而是因为我写了文件转储)。
我把class的名字写出来了,但是因为P.x和P.X存在,所以我用P.X覆盖了P.x。
所以它只寻找我,好像它正在从文件 P.x 加载 P.X,因为在我转储的文件中 P.x 是 class P.X因为那个错误。
谢谢大家,抱歉。