From 6e34774a46c8ab9140bf4a197f3131e79543a782 Mon Sep 17 00:00:00 2001 From: Agaricus Date: Tue, 5 Feb 2013 21:09:08 -0800 Subject: [PATCH] Initial implementation of field reflection remapper Remaps the string constant in class.getDeclaredField("fieldname") --- .../specialsource/ReflectionRemapper.java | 91 +++++++++++++++++++ .../net/md_5/specialsource/SpecialSource.java | 2 + 2 files changed, 93 insertions(+) create mode 100644 src/main/java/net/md_5/specialsource/ReflectionRemapper.java diff --git a/src/main/java/net/md_5/specialsource/ReflectionRemapper.java b/src/main/java/net/md_5/specialsource/ReflectionRemapper.java new file mode 100644 index 0000000..3ca6976 --- /dev/null +++ b/src/main/java/net/md_5/specialsource/ReflectionRemapper.java @@ -0,0 +1,91 @@ +package net.md_5.specialsource; + +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; +import org.objectweb.asm.Type; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +public class ReflectionRemapper { + + private JarMapping jarMapping; + private IInheritanceProvider inheritanceProvider; + + public ReflectionRemapper(JarMapping jarMapping, IInheritanceProvider inheritanceProvider) { + this.jarMapping = jarMapping; + this.inheritanceProvider = inheritanceProvider; + } + + @SuppressWarnings("unchecked") + public byte[] remapClassFile(InputStream is) throws IOException { + ClassReader cr = new ClassReader(is); + ClassNode classNode = new ClassNode(); + ClassWriter cw = new ClassWriter(0); + + cr.accept(classNode, ClassReader.SKIP_DEBUG); + + for (MethodNode methodNode : (List) classNode.methods) { + AbstractInsnNode insn = methodNode.instructions.getFirst(); + while (insn != null) { + if (insn.getOpcode() == Opcodes.INVOKEVIRTUAL) { + remapGetDeclaredField(insn); + } + + insn = insn.getNext(); + } + } + + classNode.accept(cw); + + return cw.toByteArray(); + } + + private void remapGetDeclaredField(AbstractInsnNode insn) { + MethodInsnNode mi = (MethodInsnNode) insn; + + System.out.println("methodnode "+mi.owner+" "+mi.name+" "+mi.desc); + if (!mi.owner.equals("java/lang/Class") || !mi.name.equals("getDeclaredField") || !mi.desc.equals("(Ljava/lang/String;)Ljava/lang/reflect/Field;")) { + return; + } + + System.out.println("found getDeclaredField!"); + + if (insn.getPrevious() == null || insn.getPrevious().getOpcode() != Opcodes.LDC) { + System.out.println("- not constant field; skipping"); + return; + } + LdcInsnNode ldcField = (LdcInsnNode) insn.getPrevious(); + System.out.println("field constant = "+ldcField.cst); + if (!(ldcField.cst instanceof String)) { + System.out.println("- not field string; skipping"); + return; + } + String fieldName = (String) ldcField.cst; + + if (ldcField.getPrevious() == null || ldcField.getPrevious().getOpcode() != Opcodes.LDC) { + System.out.println("- not constant class; skipping"); + return; + } + LdcInsnNode ldcClass = (LdcInsnNode) ldcField.getPrevious(); + if (!(ldcClass.cst instanceof Type)) { + System.out.println("- not class type; skipping"); + return; + } + System.out.println("class = "+ldcClass.cst); + String className = ((Type) ldcClass.cst).getInternalName(); + + System.out.println("Remapping "+className+"/"+fieldName); + + String newName = jarMapping.fields.get(className + "/" + fieldName); // TODO: tryClimb etc + if (newName != null) { + // Change the string literal to the correct name + ldcField.cst = newName; + //ldcClass.cst = className; // not remapped here - taken care of by JarRemapper + System.out.println(" -> to "+newName); + } + } +} diff --git a/src/main/java/net/md_5/specialsource/SpecialSource.java b/src/main/java/net/md_5/specialsource/SpecialSource.java index 556f69f..856ebfb 100644 --- a/src/main/java/net/md_5/specialsource/SpecialSource.java +++ b/src/main/java/net/md_5/specialsource/SpecialSource.java @@ -92,6 +92,8 @@ public class SpecialSource { .withRequiredArg() .ofType(File.class); + //acceptsAll(asList("G", "remap-reflect-field"), "Remap reflection calls to getDeclaredField()"); // TODO + acceptsAll(asList("q", "quiet"), "Quiet mode"); } };