在已编译的类中更改字符串常量

Bar*_*lom 12 java constants bytecode-manipulation .class-file

我需要在已部署的Java程序中更改字符串常量,.class即已编译的文件内的值.它可以重新启动,但不容易重新编译(虽然如果这个问题没有答案,这是一个不方便的选择).这可能吗?

更新:我只是用十六进制编辑器查看了该文件,看起来我可以轻松地在那里更改字符串.这会工作,即不会使文件的某种签名无效吗?旧字符串和新字符串都是字母数字,如果需要,可以是相同的长度.

更新2:我修复了它.因为我需要更改的特定类非常小并且在新版本的项目中没有更改,所以我可以编译它并从那里获取新类.出于教育目的,仍然对不涉及编译的答案感兴趣.

Aar*_*lla 8

如果你有这个类的来源,那么我的方法是:

  • 获取JAR文件
  • 获取单个类的源代码
  • 在类路径上使用JAR编译源代码(这样,您不必编译任何其他内容; JAR已经包含二进制文件并不会造成损害).您可以使用最新的Java版本; 只是使用-source和降级编译器-target.
  • 使用新jar u任务或Ant任务替换JAR中的类文件

Ant任务的示例:

        <jar destfile="${jar}"
            compress="true" update="true" duplicate="preserve" index="true"
            manifest="tmp/META-INF/MANIFEST.MF"
        >
            <fileset dir="build/classes">
                <filter />
            </fileset>
            <zipfileset src="${origJar}">
                <exclude name="META-INF/*"/>
            </zipfileset>
        </jar>
Run Code Online (Sandbox Code Playgroud)

在这里,我还更新了清单.首先放置新类,然后添加原始JAR中的所有文件.duplicate="preserve"将确保不会覆盖新代码.

如果代码没有签名,如果新字符串的长度与旧字符串的长度完全相同,您也可以尝试替换字节.Java对代码进行了一些检查,但.class文件中没有校验和.

你必须保持长度; 否则类加载器会混淆.

  • @Aaron,这是不正确的。只要您更新字符串开头的长度字段,一切都会正常工作。这不像类加载器有硬编码的魔法偏移。 (3认同)
  • 如果更改字符串的长度,则需要在字符串后插入/删除尽可能多的字节,因为类读取器将读取字符串然后期待下一个有效项 -&gt; 崩溃 (2认同)

Ant*_*ony 5

修改常量池中的字符串(技术上是 Utf8 项)时唯一需要的额外数据是长度字段(数据前的 2 个字节大端)。没有需要修改的额外校验和或偏移量。

有两个注意事项:

  • 字符串可以用在其他地方。例如,“代码”用于方法代码属性,因此更改它会破坏文件。
  • 该字符串以修改后的 Utf8 格式存储。所以基本平面之外的空字节和 unicode 字符的编码方式不同。长度字段是字节数,而不是字符数,限制为 65535。

如果您打算经常这样做,最好使用类文件编辑器工具,但十六进制编辑器对于快速更改很有用。