2

I am trying to modify a compiled class (source code is not available) which, for example, I want to change all references to java.lang.Object to some.packageName.SomeClass.

By references I mean:

  • Field types
  • Methods return types
  • Methods argument types
  • Supertype
  • Variables types in method bodies
  • Static class references (e.g. java.lang.Object.class)
  • Generic type parameters
  • Etc.

Basically, by this example, the modified class should't be able to access the java.lang.Object class directly, but only through some.packageName.SomeClass. Please note that the example class may be any arbitrary class either from the jre or not. The supplied substitute will behave exactly as the original is expected.

Is this possible by using BCEL or Javassist? If not, is there any other library which provides functionality for accomplish this goal?

Juan Garcia
  • 714
  • 6
  • 23
  • Why would anyone want to modify a compiled class? – Vishal May 24 '16 at 18:23
  • @Vishal Kamat, if you can't think why, it means you don't need to. It may be useful in some cases, for example adding an extra method and implementing a interface on a class that you don't have the source code. In my case is just a sandboxing test. – Juan Garcia May 24 '16 at 18:32
  • In that case AOP is your friend. Lots of stuff in it to do just what you mentioned – Vishal May 25 '16 at 03:50
  • @Vishal Kamat, not in my case. I need to change the class before sending it to another VM. AOP in Java is mostly implement by Javassist, ASM and probably BCEL anyway, because they need in fact to modify compiled classes, either if the do ahead of time or just before loading them. AOP doesn't fit my needs for the current project (AOP frameworks, cause class modification could be considered a form of AOP in the most abstract way) – Juan Garcia May 25 '16 at 04:43

2 Answers2

2

I use ASM and this is incredibly easy. I have an implementation of org.objectweb.asm.commons.Remapper that changes names and descriptors of classes to new ones.

For example one of the methods looks like this:

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

A description looks like this: Lcom/example/Class;. The field 'renamed' that I feed into fixDesc is a map of class mappings that I've made that contain the old to new values. So if I want to make com/example/AAA into com/example/BBB I feed the before and after values into the map and call the remapper like so:

/**
 * 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;
}
Display Name
  • 942
  • 1
  • 10
  • 20
1

I haven't tried, but javassist and ASM certainly can change constant pool in the class. This is the place where such references are stored in the class file.

Nikem
  • 5,716
  • 3
  • 32
  • 59