我需要在已部署的Java程序中更改字符串常量,即编译的.class
文件中的值。它可以重新启动,但不容易重新编译(虽然如果这个问题没有答案,这是一个不方便的选择)。这可能吗?
更新:我只是用十六进制编辑器查看了文件,看起来我可以轻松地在那里更改字符串。这会起作用,即不会使文件的某种签名无效吗?旧字符串和新字符串都是字母数字,如果需要,可以是相同的长度。
更新2:我修好了。因为我需要更改的特定类非常小并且在新版本的项目中没有更改,所以我可以编译它并从那里获取新类。为了教育目的,仍然对不涉及编译的答案感兴趣。
答案 0 :(得分:8)
如果您有此课程的来源,那么我的方法是:
-source
和-target
降级编译器。jar u
或Ant任务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>
这里我也更新了清单。首先放置新类,然后添加原始JAR中的所有文件。 duplicate="preserve"
将确保不会覆盖新代码。
如果代码没有签名,如果新字符串的长度与旧字符串的长度完全相同,您也可以尝试替换字节。 Java对代码进行了一些检查,但有no checksum in the .class files。
你必须保留长度;否则类加载器会感到困惑。
答案 1 :(得分:4)
在常量池中修改字符串(技术上是Utf8项)时所需的唯一额外数据是长度字段(数据前面的2字节大端)。没有需要修改的额外校验和或偏移。
有两点需要注意:
如果你打算做很多事情,最好是获得一个类文件编辑器工具,但是十六进制编辑器对于快速更改很有用。
答案 2 :(得分:2)
您可以使用许多字节码工程库修改.class。例如,使用javaassist。
但是,如果您尝试替换静态最终成员,则可能无法获得所需的效果,因为编译器会将此常量内联到任何位置。
使用javaassist.jar的示例代码
// ConstantHolder.java
public class ConstantHolder {
public static final String HELLO="hello";
public static void main(String[] args) {
System.out.println("Value:" + ConstantHolder.HELLO);
}
}
// ModifyConstant.java
import java.io.IOException;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.NotFoundException;
//ModifyConstant.java
public class ModifyConstant {
public static void main(String[] args) {
modifyConstant();
}
private static void modifyConstant() {
ClassPool pool = ClassPool.getDefault();
try {
CtClass pt = pool.get("ConstantHolder");
CtField field = pt.getField("HELLO");
pt.removeField(field);
CtField newField = CtField.make("public static final String HELLO=\"hell\";", pt);
pt.addField(newField);
pt.writeFile();
} catch (NotFoundException e) {
e.printStackTrace();System.exit(-1);
} catch (CannotCompileException e) {
e.printStackTrace();System.exit(-1);
} catch (IOException e) {
e.printStackTrace();System.exit(-1);
}
}
}
在这种情况下,程序成功地将HELLO的值从“Hello”修改为“Hell”。但是,当您运行ConstantHolder类时,由于编译器的内联,它仍然会打印“Value:Hello”。
希望它有所帮助。
答案 3 :(得分:2)
我最近编写了自己的ConstantPool映射器,因为ASM和JarJar存在以下问题:
我最终得到了以下内容:
public void process(DataInputStream in, DataOutputStream out, Function mapper) throws IOException {
int magic = in.readInt();
if (magic != 0xcafebabe) throw new ClassFormatError("wrong magic: " + magic);
out.writeInt(magic);
copy(in, out, 4); // minor and major
int size = in.readUnsignedShort();
out.writeShort(size);
for (int i = 1; i < size; i++) {
int tag = in.readUnsignedByte();
out.writeByte(tag);
Constant constant = Constant.constant(tag);
switch (constant) {
case Utf8:
out.writeUTF(mapper.apply(in.readUTF()));
break;
case Double:
case Long:
i++; // "In retrospect, making 8-byte constants take two constant pool entries was a poor choice."
// See http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.5
default:
copy(in, out, constant.size);
break;
}
}
Streams.copyAndClose(in, out);
}
private final byte[] buffer = new byte[8];
private void copy(DataInputStream in, DataOutputStream out, int amount) throws IOException {
in.readFully(buffer, 0, amount);
out.write(buffer, 0, amount);
}
然后
public enum Constant {
Utf8(1, -1),
Integer(3, 4),
Float(4, 4),
Long(5, 8),
Double(6,8),
Class(7, 2),
String(8, 2),
Field(9, 4),
Method(10, 4),
InterfaceMethod(11, 4),
NameAndType(12, 4),
MethodHandle(15, 3),
MethodType(16, 2),
InvokeDynamic(18, 4);
public final int tag, size;
Constant(int tag, int size) { this.tag = tag; this.size = size; }
private static final Constant[] constants;
static{
constants = new Constant[19];
for (Constant c : Constant.values()) constants[c.tag] = c;
}
public static Constant constant(int tag) {
try {
Constant constant = constants[tag];
if(constant != null) return constant;
} catch (IndexOutOfBoundsException ignored) { }
throw new ClassFormatError("Unknown tag: " + tag);
}
只是想到我会展示没有图书馆的替代品,因为它是一个非常好的开始黑客攻击的地方。我的代码受javap源代码
的启发答案 4 :(得分:0)
我过去也有类似的问题。我的解决方案是使用提到的字节码工程库之一。 我找不到javaassist,但是有一个名为dirtyJOE的出色工具,可以让您(在很多方面)编辑.class文件中的常量。
这是屏幕截图
您只需导入.class文件,然后单击常量