1

我正在尝试修改已编译的类(源代码不可用),例如,我想将所有java.lang.Objectsome.packageName.SomeClass.

引用我的意思是:

  • 字段类型
  • 方法返回类型
  • 方法参数类型
  • 超类型
  • 方法体中的变量类型
  • 静态类引用(例如java.lang.Object.class
  • 泛型类型参数
  • 等等。

基本上,通过这个例子,修改后的类应该不能java.lang.Object直接访问该类,而只能通过some.packageName.SomeClass. 请注意,示例类可能是来自 jre 或不是来自 jre 的任意类。所提供的替代品将与原件的预期完全相同。

这可以通过使用 BCEL 或 Javassist 实现吗?如果没有,是否有任何其他库可以提供实现此目标的功能?

4

2 回答 2

1

我没试过,但是javassist和ASM当然可以改变类中的常量池。这是此类引用存储在类文件中的位置。

于 2016-05-24T18:06:45.660 回答
1

我使用 ASM,这非常容易。我有一个org.objectweb.asm.commons.Remapper的实现,它将类的名称和描述符更改为新的。

例如,其中一种方法如下所示:

@Override
public String mapDesc(String desc) {
    return super.mapDesc(StringUtil.fixDesc(desc, renamed));
}

描述如下:Lcom/example/Class;. 我输入 fixDesc的“重命名”字段是我制作的包含旧值到新值的类映射的映射。因此,如果我想将com/example/AAA转换为com/example/BBB,我将之前和之后的值输入到地图中,并像这样调用重映射器:

/**
 * Given a map of ClassNodes and mappings, returns a map of class names to
 * class bytes.
 */
public static Map<String, byte[]> process(Map<String, ClassNode> nodes, Map<String, MappedClass> mappings) {
    Map<String, byte[]> out = new HashMap<String, byte[]>();
    RemapperImpl mapper = new RemapperImpl(mappings);
    for (ClassNode cn : nodes.values()) {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
        ClassVisitor remapper = new ClassRemapper(cw, mapper);
        cn.accept(remapper);
        out.put(mappings.get(cn.name).getNewName(), cw.toByteArray());
    }
    return out;
}
于 2016-05-27T19:47:12.703 回答