From 0c15b6485cfeff42a438e8387d209055ad7d7704 Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 18 Mar 2015 00:06:33 -0400 Subject: added full rename support for classes buried in generic signatures oi, what a pain in the ass... --- src/cuchaz/enigma/bytecode/ClassRenamer.java | 378 ++++++++++++++++++++------- 1 file changed, 280 insertions(+), 98 deletions(-) (limited to 'src') diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index d7acd309..8d25e722 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java @@ -12,6 +12,7 @@ package cuchaz.enigma.bytecode; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,13 +30,19 @@ import javassist.bytecode.InnerClassesAttribute; import javassist.bytecode.LocalVariableTypeAttribute; import javassist.bytecode.MethodInfo; import javassist.bytecode.SignatureAttribute; +import javassist.bytecode.SignatureAttribute.ArrayType; +import javassist.bytecode.SignatureAttribute.BaseType; import javassist.bytecode.SignatureAttribute.ClassSignature; +import javassist.bytecode.SignatureAttribute.ClassType; import javassist.bytecode.SignatureAttribute.MethodSignature; +import javassist.bytecode.SignatureAttribute.NestedClassType; import javassist.bytecode.SignatureAttribute.ObjectType; +import javassist.bytecode.SignatureAttribute.Type; +import javassist.bytecode.SignatureAttribute.TypeArgument; +import javassist.bytecode.SignatureAttribute.TypeVariable; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassNameReplacer; import cuchaz.enigma.mapping.Translator; -import cuchaz.enigma.mapping.Type; public class ClassRenamer { @@ -43,26 +50,26 @@ public class ClassRenamer { Class { @Override - public void rename(SignatureAttribute attribute, ReplacerClassMap map) { - renameClassSignatureAttribute(attribute, map); + public String rename(String signature, ReplacerClassMap map) { + return renameClassSignature(signature, map); } }, Field { @Override - public void rename(SignatureAttribute attribute, ReplacerClassMap map) { - renameFieldSignatureAttribute(attribute, map); + public String rename(String signature, ReplacerClassMap map) { + return renameFieldSignature(signature, map); } }, Method { @Override - public void rename(SignatureAttribute attribute, ReplacerClassMap map) { - renameMethodSignatureAttribute(attribute, map); + public String rename(String signature, ReplacerClassMap map) { + return renameMethodSignature(signature, map); } }; - public abstract void rename(SignatureAttribute attribute, ReplacerClassMap map); + public abstract String rename(String signature, ReplacerClassMap map); } private static class ReplacerClassMap extends HashMap { @@ -79,66 +86,12 @@ public class ClassRenamer { public String get(Object obj) { if (obj instanceof String) { return get((String)obj); - } else if (obj instanceof ObjectType) { - return get((ObjectType)obj); } return null; } - public String get(String typeName) { - - // javassist doesn't give us the class framing, add it - typeName = "L" + typeName + ";"; - - String out = getFramed(typeName); - if (out == null) { - return null; - } - - // javassist doesn't want the class framing, so remove it - out = out.substring(1, out.length() - 1); - - return out; - } - - public String getFramed(String typeName) { - Type type = new Type(typeName); - Type renamedType = new Type(type, m_replacer); - if (!type.equals(renamedType)) { - return renamedType.toString(); - } - return null; - } - - public String get(ObjectType type) { - - // we can deal with the ones that start with a class - String signature = type.encode(); - /* - if (signature.startsWith("L") || signature.startsWith("[")) { - - // TEMP: skip special characters for now - if (signature.indexOf('*') >= 0 || signature.indexOf('+') >= 0 || signature.indexOf('-') >= 0) { - System.out.println("Skipping translating: " + signature); - return null; - } - - // replace inner class / with $ - int pos = signature.indexOf("$"); - if (pos >= 0) { - signature = signature.substring(0, pos + 1) + signature.substring(pos, signature.length()).replace('/', '$'); - } - - return getFramed(signature); - } else if (signature.startsWith("T")) { - // don't need to care about template names - return null; - } else { - */ - // TEMP - System.out.println("Skipping translating: " + signature); - return null; - //} + public String get(String className) { + return m_replacer.replace(className); } } @@ -248,7 +201,10 @@ public class ClassRenamer { if (attribute instanceof SignatureAttribute) { // this has to be handled specially because SignatureAttribute.renameClass() is buggy as hell SignatureAttribute signatureAttribute = (SignatureAttribute)attribute; - type.rename(signatureAttribute, map); + String newSignature = type.rename(signatureAttribute.getSignature(), map); + if (newSignature != null) { + signatureAttribute.setSignature(newSignature); + } } else if (attribute instanceof CodeAttribute) { // code attributes have signature attributes too (indirectly) CodeAttribute codeAttribute = (CodeAttribute)attribute; @@ -267,56 +223,282 @@ public class ClassRenamer { } } - private static void renameClassSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { + private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { + + // adapted from LocalVariableAttribute.renameClass() + ConstPool cp = attribute.getConstPool(); + int n = attribute.tableLength(); + byte[] info = attribute.get(); + for (int i = 0; i < n; ++i) { + int pos = i * 10 + 2; + int index = ByteArray.readU16bit(info, pos + 6); + if (index != 0) { + String signature = cp.getUtf8Info(index); + String newSignature = renameLocalVariableSignature(signature, map); + if (newSignature != null) { + ByteArray.write16bit(cp.addUtf8Info(newSignature), info, pos + 6); + } + } + } + } + + private static String renameLocalVariableSignature(String signature, ReplacerClassMap map) { + + // for some reason, signatures with . in them don't count as field signatures + // looks like anonymous classes delimit with . in stead of $ + // convert the . to $, but keep track of how many we replace + // we need to put them back after we translate + int start = signature.lastIndexOf('$') + 1; + int numConverted = 0; + StringBuilder buf = new StringBuilder(signature); + for (int i=buf.length()-1; i>=start; i--) { + char c = buf.charAt(i); + if (c == '.') { + buf.setCharAt(i, '$'); + numConverted++; + } + } + signature = buf.toString(); + + // translate + String newSignature = renameFieldSignature(signature, map); + if (newSignature != null) { + + // put the delimiters back + buf = new StringBuilder(newSignature); + for (int i=buf.length()-1; i>=0 && numConverted > 0; i--) { + char c = buf.charAt(i); + if (c == '$') { + buf.setCharAt(i, '.'); + numConverted--; + } + } + assert(numConverted == 0); + newSignature = buf.toString(); + + return newSignature; + } + + return null; + } + + private static String renameClassSignature(String signature, ReplacerClassMap map) { try { - ClassSignature classSignature = SignatureAttribute.toClassSignature(attribute.getSignature()); - // TODO: do class signatures + return getSignature(renameType(SignatureAttribute.toClassSignature(signature), map)); } catch (BadBytecode ex) { - throw new Error("Unable to parse class signature: " + attribute.getSignature(), ex); + throw new Error("Can't parse field signature: " + signature); } } - private static void renameFieldSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { + private static String renameFieldSignature(String signature, ReplacerClassMap map) { try { - ObjectType fieldSignature = SignatureAttribute.toFieldSignature(attribute.getSignature()); - String newSignature = map.get(fieldSignature); - if (newSignature != null) { - attribute.setSignature(newSignature); - } + return getSignature(renameType(SignatureAttribute.toFieldSignature(signature), map)); } catch (BadBytecode ex) { - throw new Error("Unable to parse field signature: " + attribute.getSignature(), ex); + throw new Error("Can't parse class signature: " + signature); } } - private static void renameMethodSignatureAttribute(SignatureAttribute attribute, ReplacerClassMap map) { + private static String renameMethodSignature(String signature, ReplacerClassMap map) { try { - MethodSignature methodSignature = SignatureAttribute.toMethodSignature(attribute.getSignature()); - // TODO: do method signatures + return getSignature(renameType(SignatureAttribute.toMethodSignature(signature), map)); } catch (BadBytecode ex) { - throw new Error("Unable to parse method signature: " + attribute.getSignature(), ex); + throw new Error("Can't parse method signature: " + signature); } } - private static void renameLocalVariableTypeAttribute(LocalVariableTypeAttribute attribute, ReplacerClassMap map) { - // adapted from LocalVariableAttribute.renameClass() - ConstPool cp = attribute.getConstPool(); - int n = attribute.tableLength(); - byte[] info = attribute.get(); - for (int i = 0; i < n; ++i) { - int pos = i * 10 + 2; - int index = ByteArray.readU16bit(info, pos + 6); - if (index != 0) { - String desc = cp.getUtf8Info(index); - try { - ObjectType fieldSignature = SignatureAttribute.toFieldSignature(desc); - String newDesc = map.get(fieldSignature); - if (newDesc != null) { - ByteArray.write16bit(cp.addUtf8Info(newDesc), info, pos + 6); - } - } catch (BadBytecode ex) { - throw new Error("Unable to parse field signature: " + desc, ex); + private static ClassSignature renameType(ClassSignature type, ReplacerClassMap map) { + + // NOTE: don't have to translate type parameters + + // translate superclass + ClassType superclassType = type.getSuperClass(); + if (superclassType != ClassType.OBJECT) { + ClassType newSuperclassType = renameType(superclassType, map); + if (newSuperclassType != null) { + superclassType = newSuperclassType; + } + } + + // translate interfaces + ClassType[] interfaceTypes = type.getInterfaces(); + if (interfaceTypes != null) { + interfaceTypes = Arrays.copyOf(interfaceTypes, interfaceTypes.length); + for (int i=0; i