From 2dc7428e37bdd7a119f53d02ce157675509b0d63 Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 23 Feb 2015 23:29:22 -0500 Subject: lots of work in better handling of inner classes also working on recognizing unobfuscated and deobfuscated jars (needed for M3L) --- src/cuchaz/enigma/CommandMain.java | 43 ++++--- src/cuchaz/enigma/Deobfuscator.java | 4 +- src/cuchaz/enigma/MainFormatConverter.java | 2 +- src/cuchaz/enigma/TranslatingTypeLoader.java | 6 +- src/cuchaz/enigma/analysis/JarIndex.java | 67 +++++----- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 43 ++++--- src/cuchaz/enigma/convert/ClassMatcher.java | 6 +- src/cuchaz/enigma/mapping/ClassMapping.java | 41 +++--- src/cuchaz/enigma/mapping/EntryFactory.java | 35 +++--- src/cuchaz/enigma/mapping/Mappings.java | 14 +-- src/cuchaz/enigma/mapping/MappingsRenamer.java | 2 +- src/cuchaz/enigma/mapping/MappingsWriter.java | 4 +- src/cuchaz/enigma/mapping/Translator.java | 154 +++++++++++++++++------ 13 files changed, 261 insertions(+), 160 deletions(-) (limited to 'src') diff --git a/src/cuchaz/enigma/CommandMain.java b/src/cuchaz/enigma/CommandMain.java index 1ec2ad23..0253a92f 100644 --- a/src/cuchaz/enigma/CommandMain.java +++ b/src/cuchaz/enigma/CommandMain.java @@ -51,7 +51,7 @@ public class CommandMain { try { // process the command - String command = getArg(args, 0, "command"); + String command = getArg(args, 0, "command", true); if (command.equalsIgnoreCase("deobfuscate")) { deobfuscate(args); } else if(command.equalsIgnoreCase("decompile")) { @@ -70,46 +70,55 @@ public class CommandMain { System.out.println("Usage:"); System.out.println("\tjava -cp enigma.jar cuchaz.enigma.CommandMain "); System.out.println("\twhere is one of:"); - System.out.println("\t\tdeobfuscate "); - System.out.println("\t\tdecompile "); + System.out.println("\t\tdeobfuscate []"); + System.out.println("\t\tdecompile []"); } private static void decompile(String[] args) throws Exception { - File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); - File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); - File fileJarOut = getWritableFolder(getArg(args, 3, "out folder")); + File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); + File fileJarOut = getWritableFolder(getArg(args, 2, "out folder", true)); + File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); deobfuscator.writeSources(fileJarOut, new ConsoleProgressListener()); } private static void deobfuscate(String[] args) throws Exception { - File fileMappings = getReadableFile(getArg(args, 1, "mappings file")); - File fileJarIn = getReadableFile(getArg(args, 2, "in jar")); - File fileJarOut = getWritableFile(getArg(args, 3, "out jar")); + File fileJarIn = getReadableFile(getArg(args, 1, "in jar", true)); + File fileJarOut = getWritableFile(getArg(args, 2, "out jar", true)); + File fileMappings = getReadableFile(getArg(args, 3, "mappings file", false)); Deobfuscator deobfuscator = getDeobfuscator(fileMappings, new JarFile(fileJarIn)); deobfuscator.writeJar(fileJarOut, new ConsoleProgressListener()); } private static Deobfuscator getDeobfuscator(File fileMappings, JarFile jar) throws Exception { - System.out.println("Reading mappings..."); - Mappings mappings = new MappingsReader().read(new FileReader(fileMappings)); System.out.println("Reading jar..."); Deobfuscator deobfuscator = new Deobfuscator(jar); - deobfuscator.setMappings(mappings); + if (fileMappings != null) { + System.out.println("Reading mappings..."); + Mappings mappings = new MappingsReader().read(new FileReader(fileMappings)); + deobfuscator.setMappings(mappings); + } return deobfuscator; } - private static String getArg(String[] args, int i, String name) { + private static String getArg(String[] args, int i, String name, boolean required) { if (i >= args.length) { - throw new IllegalArgumentException(name + " is required"); + if (required) { + throw new IllegalArgumentException(name + " is required"); + } else { + return null; + } } return args[i]; } private static File getWritableFile(String path) { + if (path == null) { + return null; + } File file = new File(path).getAbsoluteFile(); File dir = file.getParentFile(); if (dir == null || !dir.exists()) { @@ -119,6 +128,9 @@ public class CommandMain { } private static File getWritableFolder(String path) { + if (path == null) { + return null; + } File dir = new File(path).getAbsoluteFile(); if (!dir.exists()) { throw new IllegalArgumentException("Cannot write to folder: " + dir); @@ -127,6 +139,9 @@ public class CommandMain { } private static File getReadableFile(String path) { + if (path == null) { + return null; + } File file = new File(path).getAbsoluteFile(); if (!file.exists()) { throw new IllegalArgumentException("Cannot find file: " + file.getAbsolutePath()); diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index c1954fc7..b7440a72 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -163,9 +163,9 @@ public class Deobfuscator { } // check inner classes - for (ClassMapping innerClassMapping : classMapping.innerClasses()) { + for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { if (!checkClassMapping(relatedMethodChecker, innerClassMapping)) { - System.err.println("WARNING: unable to find inner class " + innerClassMapping + ". dropping mapping."); + System.err.println("WARNING: unable to find inner class " + EntryFactory.getObfClassEntry(m_jarIndex, classMapping) + ". dropping mapping."); classMapping.removeInnerClassMapping(innerClassMapping); } } diff --git a/src/cuchaz/enigma/MainFormatConverter.java b/src/cuchaz/enigma/MainFormatConverter.java index d4bb2db3..5db0e539 100644 --- a/src/cuchaz/enigma/MainFormatConverter.java +++ b/src/cuchaz/enigma/MainFormatConverter.java @@ -111,7 +111,7 @@ public class MainFormatConverter { } private static Object getFieldKey(ClassMapping classMapping, FieldMapping fieldMapping) { - return new ClassEntry(classMapping.getObfName()).getSimpleName() + "." + fieldMapping.getObfName(); + return classMapping.getObfSimpleName() + "." + fieldMapping.getObfName(); } private static String getFieldKey(FieldEntry obfFieldEntry) { diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 12cde4b9..26d5e7a7 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -110,10 +110,10 @@ public class TranslatingTypeLoader implements ITypeLoader { ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(deobfClassEntry); // is this an inner class referenced directly? - String obfOuterClassName = m_jarIndex.getOuterClass(obfClassEntry.getSimpleName()); - if (obfOuterClassName != null) { + ClassEntry obfOuterClassEntry = m_jarIndex.getOuterClass(obfClassEntry); + if (obfOuterClassEntry != null) { // this class doesn't really exist. Reference it by outer$inner instead - System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassName)); + System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassEntry)); return null; } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 1c74f158..6e7c69da 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -61,9 +61,9 @@ public class JarIndex { private Multimap m_methodImplementations; private Multimap> m_behaviorReferences; private Multimap> m_fieldReferences; - private Multimap m_innerClasses; - private Map m_outerClasses; - private Map m_anonymousClasses; + private Multimap m_innerClassesByOuter; + private Map m_outerClassesByInner; + private Map m_anonymousClasses; private Map m_bridgedMethods; public JarIndex() { @@ -74,8 +74,8 @@ public class JarIndex { m_methodImplementations = HashMultimap.create(); m_behaviorReferences = HashMultimap.create(); m_fieldReferences = HashMultimap.create(); - m_innerClasses = HashMultimap.create(); - m_outerClasses = Maps.newHashMap(); + m_innerClassesByOuter = HashMultimap.create(); + m_outerClassesByInner = Maps.newHashMap(); m_anonymousClasses = Maps.newHashMap(); m_bridgedMethods = Maps.newHashMap(); } @@ -129,33 +129,40 @@ public class JarIndex { } if (buildInnerClasses) { + // step 5: index inner classes and anonymous classes for (CtClass c : JarClassIterator.classes(jar)) { ClassRenamer.moveAllClassesOutOfDefaultPackage(c, Constants.NonePackage); - String outerClassName = findOuterClass(c); - if (outerClassName != null) { - String innerClassName = c.getSimpleName(); - m_innerClasses.put(outerClassName, innerClassName); - boolean innerWasAdded = m_outerClasses.put(innerClassName, outerClassName) == null; + ClassEntry innerClassEntry = EntryFactory.getClassEntry(c); + ClassEntry outerClassEntry = findOuterClass(c); + if (outerClassEntry != null) { + m_innerClassesByOuter.put(outerClassEntry, innerClassEntry); + boolean innerWasAdded = m_outerClassesByInner.put(innerClassEntry, outerClassEntry) == null; assert (innerWasAdded); - BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassName); + BehaviorEntry enclosingBehavior = isAnonymousClass(c, outerClassEntry); if (enclosingBehavior != null) { - m_anonymousClasses.put(innerClassName, enclosingBehavior); + m_anonymousClasses.put(innerClassEntry, enclosingBehavior); // DEBUG - // System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + //System.out.println("ANONYMOUS: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); } else { // DEBUG - // System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); + //System.out.println("INNER: " + outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName()); } } } // step 6: update other indices with inner class info Map renames = Maps.newHashMap(); - for (Map.Entry entry : m_outerClasses.entrySet()) { - renames.put(Constants.NonePackage + "/" + entry.getKey(), entry.getValue() + "$" + entry.getKey()); + for (Map.Entry mapEntry : m_innerClassesByOuter.entries()) { + ClassEntry outerClassEntry = mapEntry.getKey(); + ClassEntry innerClassEntry = mapEntry.getValue(); + outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry); + String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName(); + // DEBUG + //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); + renames.put(innerClassEntry.getName(), newName); } EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); m_translationIndex.renameClasses(renames); @@ -290,7 +297,7 @@ public class JarIndex { } } - private String findOuterClass(CtClass c) { + private ClassEntry findOuterClass(CtClass c) { // inner classes: // have constructors that can (illegally) set synthetic fields @@ -341,19 +348,19 @@ public class JarIndex { // do we have an answer yet? if (callerClasses.isEmpty()) { if (illegallySetClasses.size() == 1) { - return illegallySetClasses.iterator().next().getName(); + return illegallySetClasses.iterator().next(); } else { System.out.println(String.format("WARNING: Unable to find outer class for %s. No caller and no illegally set field classes.", classEntry)); } } else { if (callerClasses.size() == 1) { - return callerClasses.iterator().next().getName(); + return callerClasses.iterator().next(); } else { // multiple callers, do the illegally set classes narrow it down? Set intersection = Sets.newHashSet(callerClasses); intersection.retainAll(illegallySetClasses); if (intersection.size() == 1) { - return intersection.iterator().next().getName(); + return intersection.iterator().next(); } else { System.out.println(String.format("WARNING: Unable to choose outer class for %s among options: %s", classEntry, callerClasses)); } @@ -448,7 +455,7 @@ public class JarIndex { return true; } - private BehaviorEntry isAnonymousClass(CtClass c, String outerClassName) { + private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); @@ -669,23 +676,19 @@ public class JarIndex { return behaviorEntries; } - public Collection getInnerClasses(String obfOuterClassName) { - return m_innerClasses.get(obfOuterClassName); + public Collection getInnerClasses(ClassEntry obfOuterClassEntry) { + return m_innerClassesByOuter.get(obfOuterClassEntry); } - public String getOuterClass(String obfInnerClassName) { - // make sure we use the right name - if (new ClassEntry(obfInnerClassName).getPackageName() != null) { - throw new IllegalArgumentException("Don't reference obfuscated inner classes using packages: " + obfInnerClassName); - } - return m_outerClasses.get(obfInnerClassName); + public ClassEntry getOuterClass(ClassEntry obfInnerClassEntry) { + return m_outerClassesByInner.get(obfInnerClassEntry); } - public boolean isAnonymousClass(String obfInnerClassName) { - return m_anonymousClasses.containsKey(obfInnerClassName); + public boolean isAnonymousClass(ClassEntry obfInnerClassEntry) { + return m_anonymousClasses.containsKey(obfInnerClassEntry); } - public BehaviorEntry getAnonymousClassCaller(String obfInnerClassName) { + public BehaviorEntry getAnonymousClassCaller(ClassEntry obfInnerClassName) { return m_anonymousClasses.get(obfInnerClassName); } diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 5350b86c..e82f56ca 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -14,13 +14,12 @@ import java.util.Collection; import javassist.CtClass; import javassist.bytecode.ConstPool; -import javassist.bytecode.Descriptor; import javassist.bytecode.EnclosingMethodAttribute; import javassist.bytecode.InnerClassesAttribute; -import cuchaz.enigma.Constants; import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.mapping.BehaviorEntry; import cuchaz.enigma.mapping.ClassEntry; +import cuchaz.enigma.mapping.EntryFactory; public class InnerClassWriter { @@ -32,18 +31,23 @@ public class InnerClassWriter { public void write(CtClass c) { - // is this an inner or outer class? - String obfInnerClassName = new ClassEntry(Descriptor.toJvmName(c.getName())).getSimpleName(); - String obfOuterClassName = m_jarIndex.getOuterClass(obfInnerClassName); - if (obfOuterClassName == null) { - // this is an outer class - obfOuterClassName = Descriptor.toJvmName(c.getName()); + // first, assume this is an inner class + ClassEntry obfInnerClassEntry = EntryFactory.getClassEntry(c); + ClassEntry obfOuterClassEntry = m_jarIndex.getOuterClass(obfInnerClassEntry); + + // see if we're right + if (obfOuterClassEntry == null) { + + // nope, it's an outer class + obfInnerClassEntry = null; + obfOuterClassEntry = EntryFactory.getClassEntry(c); } else { - // this is an inner class, rename it to outer$inner - ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); + + // yeah, it's an inner class, rename it to outer$inner + ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); c.setName(obfClassEntry.getName()); - BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassName); + BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassEntry); if (caller != null) { // write the enclosing method attribute if (caller.getName().equals("")) { @@ -55,18 +59,19 @@ public class InnerClassWriter { } // write the inner classes if needed - Collection obfInnerClassNames = m_jarIndex.getInnerClasses(obfOuterClassName); - if (obfInnerClassNames != null && !obfInnerClassNames.isEmpty()) { - writeInnerClasses(c, obfOuterClassName, obfInnerClassNames); + Collection obfInnerClassEntries = m_jarIndex.getInnerClasses(obfOuterClassEntry); + if (obfInnerClassEntries != null && !obfInnerClassEntries.isEmpty()) { + writeInnerClasses(c, obfOuterClassEntry, obfInnerClassEntries); } } - private void writeInnerClasses(CtClass c, String obfOuterClassName, Collection obfInnerClassNames) { + private void writeInnerClasses(CtClass c, ClassEntry obfOuterClassEntry, Collection obfInnerClassEntries) { InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); c.getClassFile().addAttribute(attr); - for (String obfInnerClassName : obfInnerClassNames) { + for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { + // get the new inner class name - ClassEntry obfClassEntry = new ClassEntry(obfOuterClassName + "$" + obfInnerClassName); + ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); // here's what the JVM spec says about the InnerClasses attribute // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); @@ -77,7 +82,7 @@ public class InnerClassWriter { int outerClassIndex = 0; int innerClassSimpleNameIndex = 0; int accessFlags = 0; - if (!m_jarIndex.isAnonymousClass(obfInnerClassName)) { + if (!m_jarIndex.isAnonymousClass(obfInnerClassEntry)) { outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); } @@ -96,7 +101,7 @@ public class InnerClassWriter { */ // make sure the outer class references only the new inner class names - c.replaceClassName(Constants.NonePackage + "/" + obfInnerClassName, obfClassEntry.getName()); + c.replaceClassName(obfInnerClassEntry.getName(), obfClassEntry.getName()); } } } diff --git a/src/cuchaz/enigma/convert/ClassMatcher.java b/src/cuchaz/enigma/convert/ClassMatcher.java index d70b8ebb..224d0048 100644 --- a/src/cuchaz/enigma/convert/ClassMatcher.java +++ b/src/cuchaz/enigma/convert/ClassMatcher.java @@ -222,7 +222,7 @@ public class ClassMatcher { // check the method matches System.out.println("Checking methods..."); for (ClassMapping classMapping : mappings.classes()) { - ClassEntry classEntry = new ClassEntry(classMapping.getObfName()); + ClassEntry classEntry = new ClassEntry(classMapping.getObfFullName()); for (MethodMapping methodMapping : classMapping.methods()) { // skip constructors @@ -240,13 +240,13 @@ public class ClassMatcher { // show the available methods System.err.println("\tAvailable dest methods:"); - CtClass c = destLoader.loadClass(classMapping.getObfName()); + CtClass c = destLoader.loadClass(classMapping.getObfFullName()); for (CtBehavior behavior : c.getDeclaredBehaviors()) { System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); } System.err.println("\tAvailable source methods:"); - c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfName())); + c = sourceLoader.loadClass(matchedClassNames.inverse().get(classMapping.getObfFullName())); for (CtBehavior behavior : c.getDeclaredBehaviors()) { System.err.println("\t\t" + EntryFactory.getBehaviorEntry(behavior)); } diff --git a/src/cuchaz/enigma/mapping/ClassMapping.java b/src/cuchaz/enigma/mapping/ClassMapping.java index 885400b4..3610e33b 100644 --- a/src/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/cuchaz/enigma/mapping/ClassMapping.java @@ -20,7 +20,8 @@ public class ClassMapping implements Serializable, Comparable { private static final long serialVersionUID = -5148491146902340107L; - private String m_obfName; + private String m_obfFullName; + private String m_obfSimpleName; private String m_deobfName; private Map m_innerClassesByObf; private Map m_innerClassesByDeobf; @@ -34,7 +35,8 @@ public class ClassMapping implements Serializable, Comparable { } public ClassMapping(String obfName, String deobfName) { - m_obfName = obfName; + m_obfFullName = obfName; + m_obfSimpleName = new ClassEntry(obfName).getSimpleName(); m_deobfName = NameValidator.validateClassName(deobfName, false); m_innerClassesByObf = Maps.newHashMap(); m_innerClassesByDeobf = Maps.newHashMap(); @@ -44,8 +46,12 @@ public class ClassMapping implements Serializable, Comparable { m_methodsByDeobf = Maps.newHashMap(); } - public String getObfName() { - return m_obfName; + public String getObfFullName() { + return m_obfFullName; + } + + public String getObfSimpleName() { + return m_obfSimpleName; } public String getDeobfName() { @@ -64,8 +70,7 @@ public class ClassMapping implements Serializable, Comparable { } public void addInnerClassMapping(ClassMapping classMapping) { - assert (isSimpleClassName(classMapping.getObfName())); - boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfName(), classMapping) == null; + boolean obfWasAdded = m_innerClassesByObf.put(classMapping.getObfSimpleName(), classMapping) == null; assert (obfWasAdded); if (classMapping.getDeobfName() != null) { assert (isSimpleClassName(classMapping.getDeobfName())); @@ -75,7 +80,7 @@ public class ClassMapping implements Serializable, Comparable { } public void removeInnerClassMapping(ClassMapping classMapping) { - boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfName()) != null; + boolean obfWasRemoved = m_innerClassesByObf.remove(classMapping.getObfSimpleName()) != null; assert (obfWasRemoved); if (classMapping.getDeobfName() != null) { boolean deobfWasRemoved = m_innerClassesByDeobf.remove(classMapping.getDeobfName()) != null; @@ -112,11 +117,11 @@ public class ClassMapping implements Serializable, Comparable { return classMapping; } - public String getObfInnerClassName(String deobfName) { + public String getObfInnerClassSimpleName(String deobfName) { assert (isSimpleClassName(deobfName)); ClassMapping classMapping = m_innerClassesByDeobf.get(deobfName); if (classMapping != null) { - return classMapping.getObfName(); + return classMapping.getObfSimpleName(); } return null; } @@ -163,7 +168,7 @@ public class ClassMapping implements Serializable, Comparable { public void addFieldMapping(FieldMapping fieldMapping) { String obfKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); if (m_fieldsByObf.containsKey(obfKey)) { - throw new Error("Already have mapping for " + m_obfName + "." + obfKey); + throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey); } String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); if (m_fieldsByDeobf.containsKey(deobfKey)) { @@ -257,7 +262,7 @@ public class ClassMapping implements Serializable, Comparable { public void addMethodMapping(MethodMapping methodMapping) { String obfKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); if (m_methodsByObf.containsKey(obfKey)) { - throw new Error("Already have mapping for " + m_obfName + "." + obfKey); + throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey); } boolean wasAdded = m_methodsByObf.put(obfKey, methodMapping) == null; assert (wasAdded); @@ -339,7 +344,7 @@ public class ClassMapping implements Serializable, Comparable { @Override public String toString() { StringBuilder buf = new StringBuilder(); - buf.append(m_obfName); + buf.append(m_obfFullName); buf.append(" <-> "); buf.append(m_deobfName); buf.append("\n"); @@ -359,7 +364,7 @@ public class ClassMapping implements Serializable, Comparable { buf.append("Inner Classes:\n"); for (ClassMapping classMapping : m_innerClassesByObf.values()) { buf.append("\t"); - buf.append(classMapping.getObfName()); + buf.append(classMapping.getObfSimpleName()); buf.append(" <-> "); buf.append(classMapping.getDeobfName()); buf.append("\n"); @@ -370,10 +375,10 @@ public class ClassMapping implements Serializable, Comparable { @Override public int compareTo(ClassMapping other) { // sort by a, b, c, ... aa, ab, etc - if (m_obfName.length() != other.m_obfName.length()) { - return m_obfName.length() - other.m_obfName.length(); + if (m_obfFullName.length() != other.m_obfFullName.length()) { + return m_obfFullName.length() - other.m_obfFullName.length(); } - return m_obfName.compareTo(other.m_obfName); + return m_obfFullName.compareTo(other.m_obfFullName); } public boolean renameObfClass(String oldObfClassName, String newObfClassName) { @@ -399,9 +404,9 @@ public class ClassMapping implements Serializable, Comparable { } } - if (m_obfName.equals(oldObfClassName)) { + if (m_obfFullName.equals(oldObfClassName)) { // rename this class - m_obfName = newObfClassName; + m_obfFullName = newObfClassName; return true; } return false; diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index dceea29d..bbdfa739 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java @@ -25,25 +25,19 @@ public class EntryFactory { } public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { - return new ClassEntry(getChainedOuterClassName(jarIndex, classMapping.getObfName())); + return getChainedOuterClassName(jarIndex, new ClassEntry(classMapping.getObfFullName())); } - private static String getChainedOuterClassName(JarIndex jarIndex, String obfClassName) { + public static ClassEntry getChainedOuterClassName(JarIndex jarIndex, ClassEntry obfClassEntry) { // lookup the chain of outer classes - List obfOuterClassNames = Lists.newArrayList(); - String checkName = obfClassName; + List obfClassChain = Lists.newArrayList(obfClassEntry); + ClassEntry checkClassEntry = obfClassEntry; while (true) { - - // if this class name has a package, then it can't be an inner class - if (!new ClassEntry(checkName).isInDefaultPackage()) { - break; - } - - String obfOuterClassName = jarIndex.getOuterClass(checkName); - if (obfOuterClassName != null) { - obfOuterClassNames.add(obfOuterClassName); - checkName = obfOuterClassName; + ClassEntry obfOuterClassEntry = jarIndex.getOuterClass(checkClassEntry); + if (obfOuterClassEntry != null) { + obfClassChain.add(obfOuterClassEntry); + checkClassEntry = obfOuterClassEntry; } else { break; } @@ -51,12 +45,15 @@ public class EntryFactory { // build the chained class name StringBuilder buf = new StringBuilder(); - for (int i=obfOuterClassNames.size()-1; i>=0; i--) { - buf.append(obfOuterClassNames.get(i)); - buf.append("$"); + for (int i=obfClassChain.size()-1; i>=0; i--) { + if (buf.length() == 0) { + buf.append(obfClassChain.get(i).getName()); + } else { + buf.append("$"); + buf.append(obfClassChain.get(i).getSimpleName()); + } } - buf.append(obfClassName); - return buf.toString(); + return new ClassEntry(buf.toString()); } public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { diff --git a/src/cuchaz/enigma/mapping/Mappings.java b/src/cuchaz/enigma/mapping/Mappings.java index 675fdf1f..a85bcbf6 100644 --- a/src/cuchaz/enigma/mapping/Mappings.java +++ b/src/cuchaz/enigma/mapping/Mappings.java @@ -37,7 +37,7 @@ public class Mappings implements Serializable { this(); for (ClassMapping classMapping : classes) { - m_classesByObf.put(classMapping.getObfName(), classMapping); + m_classesByObf.put(classMapping.getObfFullName(), classMapping); if (classMapping.getDeobfName() != null) { m_classesByDeobf.put(classMapping.getDeobfName(), classMapping); } @@ -50,10 +50,10 @@ public class Mappings implements Serializable { } public void addClassMapping(ClassMapping classMapping) { - if (m_classesByObf.containsKey(classMapping.getObfName())) { - throw new Error("Already have mapping for " + classMapping.getObfName()); + if (m_classesByObf.containsKey(classMapping.getObfFullName())) { + throw new Error("Already have mapping for " + classMapping.getObfFullName()); } - boolean obfWasAdded = m_classesByObf.put(classMapping.getObfName(), classMapping) == null; + boolean obfWasAdded = m_classesByObf.put(classMapping.getObfFullName(), classMapping) == null; assert (obfWasAdded); if (classMapping.getDeobfName() != null) { if (m_classesByDeobf.containsKey(classMapping.getDeobfName())) { @@ -65,7 +65,7 @@ public class Mappings implements Serializable { } public void removeClassMapping(ClassMapping classMapping) { - boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfName()) != null; + boolean obfWasRemoved = m_classesByObf.remove(classMapping.getObfFullName()) != null; assert (obfWasRemoved); if (classMapping.getDeobfName() != null) { boolean deobfWasRemoved = m_classesByDeobf.remove(classMapping.getDeobfName()) != null; @@ -103,7 +103,7 @@ public class Mappings implements Serializable { if (classMapping.getDeobfName() != null) { classes.put(classMapping.getDeobfName(), classMapping); } else { - classes.put(classMapping.getObfName(), classMapping); + classes.put(classMapping.getObfFullName(), classMapping); } } @@ -144,7 +144,7 @@ public class Mappings implements Serializable { for (ClassMapping classMapping : classes()) { // add the class name - classNames.add(classMapping.getObfName()); + classNames.add(classMapping.getObfFullName()); // add classes from method signatures for (MethodMapping methodMapping : classMapping.methods()) { diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index ea343c4e..16f700d2 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java @@ -213,7 +213,7 @@ public class MappingsRenamer { ClassMapping classMapping = m_mappings.m_classesByObf.get(obfClassName); if (classMapping == null) { classMapping = new ClassMapping(obfClassName); - boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfName(), classMapping) == null; + boolean obfWasAdded = m_mappings.m_classesByObf.put(classMapping.getObfFullName(), classMapping) == null; assert (obfWasAdded); } return classMapping; diff --git a/src/cuchaz/enigma/mapping/MappingsWriter.java b/src/cuchaz/enigma/mapping/MappingsWriter.java index c7c2cc00..8b62db8c 100644 --- a/src/cuchaz/enigma/mapping/MappingsWriter.java +++ b/src/cuchaz/enigma/mapping/MappingsWriter.java @@ -31,9 +31,9 @@ public class MappingsWriter { private void write(PrintWriter out, ClassMapping classMapping, int depth) throws IOException { if (classMapping.getDeobfName() == null) { - out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfName()); + out.format("%sCLASS %s\n", getIndent(depth), classMapping.getObfFullName()); } else { - out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfName(), classMapping.getDeobfName()); + out.format("%sCLASS %s %s\n", getIndent(depth), classMapping.getObfFullName(), classMapping.getDeobfName()); } for (ClassMapping innerClassMapping : sorted(classMapping.innerClasses())) { diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 759dddf9..d9850324 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -10,8 +10,10 @@ ******************************************************************************/ package cuchaz.enigma.mapping; +import java.util.List; import java.util.Map; +import com.beust.jcommander.internal.Lists; import com.google.common.collect.Maps; import cuchaz.enigma.analysis.TranslationIndex; @@ -50,54 +52,106 @@ public class Translator { } } + public String translate(T entry) { + if (entry instanceof ClassEntry) { + return translate((ClassEntry)entry); + } else if (entry instanceof FieldEntry) { + return translate((FieldEntry)entry); + } else if (entry instanceof MethodEntry) { + return translate((MethodEntry)entry); + } else if (entry instanceof ConstructorEntry) { + return translate((ConstructorEntry)entry); + } else if (entry instanceof ArgumentEntry) { + return translate((ArgumentEntry)entry); + } else { + throw new Error("Unknown entry type: " + entry.getClass().getName()); + } + } + public String translateClass(String className) { return translate(new ClassEntry(className)); } public String translate(ClassEntry in) { - ClassMapping classMapping = m_classes.get(in.getOuterClassName()); - if (classMapping != null) { - if (in.isInnerClass()) { - // translate the inner class - String translatedInnerClassName = m_direction.choose( - classMapping.getDeobfInnerClassName(in.getInnerClassName()), - classMapping.getObfInnerClassName(in.getInnerClassName()) + + if (in.isInnerClass()) { + + // translate everything in the class chain, or return null + List mappingsChain = getClassMappingChain(in); + StringBuilder buf = new StringBuilder(); + for (ClassMapping classMapping : mappingsChain) { + if (classMapping == null) { + return null; + } + boolean isFirstClass = buf.length() == 0; + String name = m_direction.choose( + classMapping.getDeobfName(), + isFirstClass ? classMapping.getObfFullName() : classMapping.getObfSimpleName() ); - if (translatedInnerClassName != null) { - // try to translate the outer name - String translatedOuterClassName = m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); - if (translatedOuterClassName != null) { - return translatedOuterClassName + "$" + translatedInnerClassName; - } else { - return in.getOuterClassName() + "$" + translatedInnerClassName; - } + if (name == null) { + return null; + } + if (!isFirstClass) { + buf.append("$"); } - } else { - // just return outer - return m_direction.choose(classMapping.getDeobfName(), classMapping.getObfName()); + buf.append(name); } + return buf.toString(); + + } else { + + // normal classes are easier + ClassMapping classMapping = m_classes.get(in.getName()); + if (classMapping == null) { + return null; + } + return m_direction.choose( + classMapping.getDeobfName(), + classMapping.getObfFullName() + ); } - return null; } public ClassEntry translateEntry(ClassEntry in) { - // can we translate the inner class? - String name = translate(in); - if (name != null) { - return new ClassEntry(name); - } - if (in.isInnerClass()) { - // guess not. just translate the outer class name then - String outerClassName = translate(in.getOuterClassEntry()); - if (outerClassName != null) { - return new ClassEntry(outerClassName + "$" + in.getInnerClassName()); + // translate as much of the class chain as we can + List mappingsChain = getClassMappingChain(in); + String[] obfClassNames = in.getName().split("\\$"); + StringBuilder buf = new StringBuilder(); + for (int i=0; i mappingChain = getClassMappingChain(in); + return mappingChain.get(mappingChain.size() - 1); + } + + private List getClassMappingChain(ClassEntry in) { + + // get a list of all the classes in the hierarchy + String[] parts = in.getName().split("\\$"); + List mappingsChain = Lists.newArrayList(); + + // get mappings for the outer class + ClassMapping outerClassMapping = m_classes.get(parts[0]); + mappingsChain.add(outerClassMapping); + + for (int i=1; i