diff options
| author | 2015-03-16 19:22:22 -0400 | |
|---|---|---|
| committer | 2015-03-16 19:22:22 -0400 | |
| commit | 5e3743a0aca3529eacf9be400c8b8d7547f66e7f (patch) | |
| tree | ea601747547f78e1b83ab828650932126440e221 /src | |
| parent | update to new javassist version to (hopefully) get bug fixes (diff) | |
| download | enigma-fork-5e3743a0aca3529eacf9be400c8b8d7547f66e7f.tar.gz enigma-fork-5e3743a0aca3529eacf9be400c8b8d7547f66e7f.tar.xz enigma-fork-5e3743a0aca3529eacf9be400c8b8d7547f66e7f.zip | |
started adding minimal support for generics
fixed mark-as-deobfuscated issue
Diffstat (limited to 'src')
| -rw-r--r-- | src/cuchaz/enigma/Deobfuscator.java | 9 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/JarIndex.java | 31 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | 7 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/ClassRenamer.java | 164 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/ClassTranslator.java | 42 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/EntryFactory.java | 17 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/MappingsChecker.java | 2 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/MappingsRenamer.java | 10 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/ParameterizedType.java | 54 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/Signature.java | 10 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/Type.java | 78 |
11 files changed, 294 insertions, 130 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 5a23ce5..b63f163 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java | |||
| @@ -439,12 +439,9 @@ public class Deobfuscator { | |||
| 439 | Translator translator = getTranslator(TranslationDirection.Deobfuscating); | 439 | Translator translator = getTranslator(TranslationDirection.Deobfuscating); |
| 440 | if (obfEntry instanceof ClassEntry) { | 440 | if (obfEntry instanceof ClassEntry) { |
| 441 | ClassEntry obfClass = (ClassEntry)obfEntry; | 441 | ClassEntry obfClass = (ClassEntry)obfEntry; |
| 442 | ClassEntry translated = translator.translateEntry(obfClass); | 442 | List<ClassMapping> mappingChain = m_mappings.getClassMappingChain(obfClass); |
| 443 | if (obfClass.isInnerClass()) { | 443 | ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); |
| 444 | return !obfClass.getInnermostClassName().equals(translated.getInnermostClassName()); | 444 | return classMapping != null && classMapping.getDeobfName() != null; |
| 445 | } else { | ||
| 446 | return !obfClass.equals(translated); | ||
| 447 | } | ||
| 448 | } else if (obfEntry instanceof FieldEntry) { | 445 | } else if (obfEntry instanceof FieldEntry) { |
| 449 | return translator.translate((FieldEntry)obfEntry) != null; | 446 | return translator.translate((FieldEntry)obfEntry) != null; |
| 450 | } else if (obfEntry instanceof MethodEntry) { | 447 | } else if (obfEntry instanceof MethodEntry) { |
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 7ebbd97..e255468 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java | |||
| @@ -28,6 +28,7 @@ import javassist.CtMethod; | |||
| 28 | import javassist.NotFoundException; | 28 | import javassist.NotFoundException; |
| 29 | import javassist.bytecode.AccessFlag; | 29 | import javassist.bytecode.AccessFlag; |
| 30 | import javassist.bytecode.Descriptor; | 30 | import javassist.bytecode.Descriptor; |
| 31 | import javassist.bytecode.EnclosingMethodAttribute; | ||
| 31 | import javassist.bytecode.FieldInfo; | 32 | import javassist.bytecode.FieldInfo; |
| 32 | import javassist.bytecode.InnerClassesAttribute; | 33 | import javassist.bytecode.InnerClassesAttribute; |
| 33 | import javassist.expr.ConstructorCall; | 34 | import javassist.expr.ConstructorCall; |
| @@ -314,15 +315,6 @@ public class JarIndex { | |||
| 314 | if (classEntry.isInnerClass()) { | 315 | if (classEntry.isInnerClass()) { |
| 315 | return classEntry.getOuterClassEntry(); | 316 | return classEntry.getOuterClassEntry(); |
| 316 | } | 317 | } |
| 317 | InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); | ||
| 318 | if (innerClassesAttribute != null) { | ||
| 319 | for (int i=0; i<innerClassesAttribute.tableLength(); i++) { | ||
| 320 | ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(innerClassesAttribute.innerClass(i))); | ||
| 321 | if (classEntry.equals(innerClassEntry)) { | ||
| 322 | return new ClassEntry(Descriptor.toJvmName(innerClassesAttribute.outerClass(i))); | ||
| 323 | } | ||
| 324 | } | ||
| 325 | } | ||
| 326 | 318 | ||
| 327 | // inner classes: | 319 | // inner classes: |
| 328 | // have constructors that can (illegally) set synthetic fields | 320 | // have constructors that can (illegally) set synthetic fields |
| @@ -481,6 +473,27 @@ public class JarIndex { | |||
| 481 | 473 | ||
| 482 | private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { | 474 | private BehaviorEntry isAnonymousClass(CtClass c, ClassEntry outerClassEntry) { |
| 483 | 475 | ||
| 476 | // is this class already marked anonymous? | ||
| 477 | EnclosingMethodAttribute enclosingMethodAttribute = (EnclosingMethodAttribute)c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); | ||
| 478 | if (enclosingMethodAttribute != null) { | ||
| 479 | if (enclosingMethodAttribute.methodIndex() > 0) { | ||
| 480 | return EntryFactory.getBehaviorEntry( | ||
| 481 | Descriptor.toJvmName(enclosingMethodAttribute.className()), | ||
| 482 | enclosingMethodAttribute.methodName(), | ||
| 483 | enclosingMethodAttribute.methodDescriptor() | ||
| 484 | ); | ||
| 485 | } else { | ||
| 486 | // an attribute but no method? assume not anonymous | ||
| 487 | return null; | ||
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 | // if there's an inner class attribute, but not an enclosing method attribute, then it's not anonymous | ||
| 492 | InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); | ||
| 493 | if (innerClassesAttribute != null) { | ||
| 494 | return null; | ||
| 495 | } | ||
| 496 | |||
| 484 | ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); | 497 | ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); |
| 485 | 498 | ||
| 486 | // anonymous classes: | 499 | // anonymous classes: |
diff --git a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java index f4f4956..f4202b5 100644 --- a/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java +++ b/src/cuchaz/enigma/analysis/SourceIndexClassVisitor.java | |||
| @@ -30,7 +30,6 @@ import cuchaz.enigma.mapping.ClassEntry; | |||
| 30 | import cuchaz.enigma.mapping.ConstructorEntry; | 30 | import cuchaz.enigma.mapping.ConstructorEntry; |
| 31 | import cuchaz.enigma.mapping.EntryFactory; | 31 | import cuchaz.enigma.mapping.EntryFactory; |
| 32 | import cuchaz.enigma.mapping.FieldEntry; | 32 | import cuchaz.enigma.mapping.FieldEntry; |
| 33 | import cuchaz.enigma.mapping.Type; | ||
| 34 | 33 | ||
| 35 | public class SourceIndexClassVisitor extends SourceIndexVisitor { | 34 | public class SourceIndexClassVisitor extends SourceIndexVisitor { |
| 36 | 35 | ||
| @@ -93,8 +92,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { | |||
| 93 | @Override | 92 | @Override |
| 94 | public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { | 93 | public Void visitFieldDeclaration(FieldDeclaration node, SourceIndex index) { |
| 95 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); | 94 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); |
| 96 | ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); | 95 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); |
| 97 | FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); | ||
| 98 | assert (node.getVariables().size() == 1); | 96 | assert (node.getVariables().size() == 1); |
| 99 | VariableInitializer variable = node.getVariables().firstOrNullObject(); | 97 | VariableInitializer variable = node.getVariables().firstOrNullObject(); |
| 100 | index.addDeclaration(variable.getNameToken(), fieldEntry); | 98 | index.addDeclaration(variable.getNameToken(), fieldEntry); |
| @@ -106,8 +104,7 @@ public class SourceIndexClassVisitor extends SourceIndexVisitor { | |||
| 106 | public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { | 104 | public Void visitEnumValueDeclaration(EnumValueDeclaration node, SourceIndex index) { |
| 107 | // treat enum declarations as field declarations | 105 | // treat enum declarations as field declarations |
| 108 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); | 106 | FieldDefinition def = node.getUserData(Keys.FIELD_DEFINITION); |
| 109 | ClassEntry classEntry = new ClassEntry(def.getDeclaringType().getInternalName()); | 107 | FieldEntry fieldEntry = EntryFactory.getFieldEntry(def); |
| 110 | FieldEntry fieldEntry = new FieldEntry(classEntry, def.getName(), new Type(def.getErasedSignature())); | ||
| 111 | index.addDeclaration(node.getNameToken(), fieldEntry); | 108 | index.addDeclaration(node.getNameToken(), fieldEntry); |
| 112 | 109 | ||
| 113 | return recurse(node, index); | 110 | return recurse(node, index); |
diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java index e9cdea3..8bc084d 100644 --- a/src/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java | |||
| @@ -23,60 +23,100 @@ import com.google.common.collect.Maps; | |||
| 23 | import com.google.common.collect.Sets; | 23 | import com.google.common.collect.Sets; |
| 24 | 24 | ||
| 25 | import cuchaz.enigma.mapping.ClassEntry; | 25 | import cuchaz.enigma.mapping.ClassEntry; |
| 26 | import cuchaz.enigma.mapping.ClassNameReplacer; | ||
| 27 | import cuchaz.enigma.mapping.ParameterizedType; | ||
| 28 | import cuchaz.enigma.mapping.Translator; | ||
| 29 | import cuchaz.enigma.mapping.Type; | ||
| 26 | 30 | ||
| 27 | public class ClassRenamer { | 31 | public class ClassRenamer { |
| 28 | 32 | ||
| 29 | public static void renameClasses(CtClass c, Map<ClassEntry,ClassEntry> map) { | 33 | public static void renameClasses(CtClass c, final Translator translator) { |
| 30 | 34 | renameClasses(c, new ClassNameReplacer() { | |
| 31 | // build the map used by javassist | 35 | @Override |
| 32 | ClassMap nameMap = new ClassMap(); | 36 | public String replace(String className) { |
| 33 | for (Map.Entry<ClassEntry,ClassEntry> entry : map.entrySet()) { | 37 | ClassEntry entry = translator.translateEntry(new ClassEntry(className)); |
| 34 | nameMap.put(entry.getKey().getName(), entry.getValue().getName()); | 38 | if (entry != null) { |
| 35 | } | 39 | return entry.getName(); |
| 36 | |||
| 37 | c.replaceClassName(nameMap); | ||
| 38 | |||
| 39 | // replace simple names in the InnerClasses attribute too | ||
| 40 | ConstPool constants = c.getClassFile().getConstPool(); | ||
| 41 | InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); | ||
| 42 | if (attr != null) { | ||
| 43 | for (int i = 0; i < attr.tableLength(); i++) { | ||
| 44 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); | ||
| 45 | if (attr.innerNameIndex(i) != 0) { | ||
| 46 | attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnermostClassName())); | ||
| 47 | } | 40 | } |
| 48 | 41 | return null; | |
| 49 | /* DEBUG | ||
| 50 | System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); | ||
| 51 | */ | ||
| 52 | } | 42 | } |
| 53 | } | 43 | }); |
| 54 | } | 44 | } |
| 55 | 45 | ||
| 56 | public static Set<ClassEntry> getAllClassEntries(final CtClass c) { | 46 | public static void moveAllClassesOutOfDefaultPackage(CtClass c, final String newPackageName) { |
| 47 | renameClasses(c, new ClassNameReplacer() { | ||
| 48 | @Override | ||
| 49 | public String replace(String className) { | ||
| 50 | ClassEntry entry = new ClassEntry(className); | ||
| 51 | if (entry.isInDefaultPackage()) { | ||
| 52 | return newPackageName + "/" + entry.getName(); | ||
| 53 | } | ||
| 54 | return null; | ||
| 55 | } | ||
| 56 | }); | ||
| 57 | } | ||
| 58 | |||
| 59 | public static void moveAllClassesIntoDefaultPackage(CtClass c, final String oldPackageName) { | ||
| 60 | renameClasses(c, new ClassNameReplacer() { | ||
| 61 | @Override | ||
| 62 | public String replace(String className) { | ||
| 63 | ClassEntry entry = new ClassEntry(className); | ||
| 64 | if (entry.getPackageName().equals(oldPackageName)) { | ||
| 65 | return entry.getSimpleName(); | ||
| 66 | } | ||
| 67 | return null; | ||
| 68 | } | ||
| 69 | }); | ||
| 70 | } | ||
| 71 | |||
| 72 | public static void renameClasses(CtClass c, ClassNameReplacer replacer) { | ||
| 73 | Map<ParameterizedType,ParameterizedType> map = Maps.newHashMap(); | ||
| 74 | for (ParameterizedType type : ClassRenamer.getAllClassTypes(c)) { | ||
| 75 | ParameterizedType renamedType = new ParameterizedType(type, replacer); | ||
| 76 | if (!type.equals(renamedType)) { | ||
| 77 | map.put(type, renamedType); | ||
| 78 | } | ||
| 79 | } | ||
| 80 | renameTypes(c, map); | ||
| 81 | } | ||
| 82 | |||
| 83 | public static Set<ParameterizedType> getAllClassTypes(final CtClass c) { | ||
| 57 | 84 | ||
| 58 | // get the classes that javassist knows about | 85 | // TODO: might have to scan SignatureAttributes directly because javassist is buggy |
| 59 | final Set<ClassEntry> entries = Sets.newHashSet(); | 86 | |
| 87 | // get the class types that javassist knows about | ||
| 88 | final Set<ParameterizedType> types = Sets.newHashSet(); | ||
| 60 | ClassMap map = new ClassMap() { | 89 | ClassMap map = new ClassMap() { |
| 61 | @Override | 90 | @Override |
| 62 | public Object get(Object obj) { | 91 | public Object get(Object obj) { |
| 63 | if (obj instanceof String) { | 92 | if (obj instanceof String) { |
| 64 | String str = (String)obj; | 93 | String str = (String)obj; |
| 65 | 94 | ||
| 66 | // javassist throws a lot of weird things at this map | 95 | // sometimes javasist gives us dot-separated classes... whadda hell? |
| 67 | // I either have to implement my on class scanner, or just try to filter out the weirdness | 96 | str = str.replace('.', '/'); |
| 68 | // I'm opting to filter out the weirdness for now | ||
| 69 | 97 | ||
| 70 | // skip anything with generic arguments | 98 | // skip weird types |
| 71 | if (str.indexOf('<') >= 0 || str.indexOf('>') >= 0 || str.indexOf(';') >= 0) { | 99 | boolean hasNestedParams = str.indexOf('<') >= 0 && str.indexOf('<', str.indexOf('<')+1) >= 0; |
| 100 | boolean hasWeirdChars = str.indexOf('*') >= 0 || str.indexOf('-') >= 0 || str.indexOf('+') >= 0; | ||
| 101 | if (hasNestedParams || hasWeirdChars) { | ||
| 102 | // TEMP | ||
| 103 | System.out.println("Skipped translating: " + str); | ||
| 72 | return null; | 104 | return null; |
| 73 | } | 105 | } |
| 74 | 106 | ||
| 75 | // convert path/to/class.inner to path/to/class$inner | 107 | ParameterizedType type = new ParameterizedType(new Type("L" + str + ";")); |
| 76 | str = str.replace('.', '$'); | 108 | assert(type.isClass()); |
| 109 | // TEMP | ||
| 110 | try { | ||
| 111 | type.getClassEntry(); | ||
| 112 | } catch (Throwable t) { | ||
| 113 | // bad type | ||
| 114 | // TEMP | ||
| 115 | System.out.println("Skipped translating: " + str); | ||
| 116 | return null; | ||
| 117 | } | ||
| 77 | 118 | ||
| 78 | // remember everything else | 119 | types.add(type); |
| 79 | entries.add(new ClassEntry(str)); | ||
| 80 | } | 120 | } |
| 81 | return null; | 121 | return null; |
| 82 | } | 122 | } |
| @@ -85,26 +125,46 @@ public class ClassRenamer { | |||
| 85 | }; | 125 | }; |
| 86 | c.replaceClassName(map); | 126 | c.replaceClassName(map); |
| 87 | 127 | ||
| 88 | return entries; | 128 | return types; |
| 89 | } | 129 | } |
| 90 | 130 | ||
| 91 | public static void moveAllClassesOutOfDefaultPackage(CtClass c, String newPackageName) { | 131 | public static void renameTypes(CtClass c, Map<ParameterizedType,ParameterizedType> map) { |
| 92 | Map<ClassEntry,ClassEntry> map = Maps.newHashMap(); | 132 | |
| 93 | for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { | 133 | // convert the type map to a javassist class map |
| 94 | if (classEntry.isInDefaultPackage()) { | 134 | ClassMap nameMap = new ClassMap(); |
| 95 | map.put(classEntry, new ClassEntry(newPackageName + "/" + classEntry.getName())); | 135 | for (Map.Entry<ParameterizedType,ParameterizedType> entry : map.entrySet()) { |
| 96 | } | 136 | String source = entry.getKey().toString(); |
| 137 | String dest = entry.getValue().toString(); | ||
| 138 | |||
| 139 | // don't forget to chop off the L ... ; | ||
| 140 | // javassist doesn't want it there | ||
| 141 | source = source.substring(1, source.length() - 1); | ||
| 142 | dest = dest.substring(1, dest.length() - 1); | ||
| 143 | |||
| 144 | nameMap.put(source, dest); | ||
| 97 | } | 145 | } |
| 98 | ClassRenamer.renameClasses(c, map); | 146 | |
| 99 | } | 147 | // replace!! |
| 100 | 148 | c.replaceClassName(nameMap); | |
| 101 | public static void moveAllClassesIntoDefaultPackage(CtClass c, String oldPackageName) { | 149 | |
| 102 | Map<ClassEntry,ClassEntry> map = Maps.newHashMap(); | 150 | // replace simple names in the InnerClasses attribute too |
| 103 | for (ClassEntry classEntry : ClassRenamer.getAllClassEntries(c)) { | 151 | ConstPool constants = c.getClassFile().getConstPool(); |
| 104 | if (classEntry.getPackageName().equals(oldPackageName)) { | 152 | InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); |
| 105 | map.put(classEntry, new ClassEntry(classEntry.getSimpleName())); | 153 | if (attr != null) { |
| 154 | for (int i = 0; i < attr.tableLength(); i++) { | ||
| 155 | |||
| 156 | // get the inner class full name (which has already been translated) | ||
| 157 | ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); | ||
| 158 | |||
| 159 | if (attr.innerNameIndex(i) != 0) { | ||
| 160 | // update the inner name | ||
| 161 | attr.setInnerNameIndex(i, constants.addUtf8Info(classEntry.getInnermostClassName())); | ||
| 162 | } | ||
| 163 | |||
| 164 | /* DEBUG | ||
| 165 | System.out.println(String.format("\tDEOBF: %s-> ATTR: %s,%s,%s", classEntry, attr.outerClass(i), attr.innerClass(i), attr.innerName(i))); | ||
| 166 | */ | ||
| 106 | } | 167 | } |
| 107 | } | 168 | } |
| 108 | ClassRenamer.renameClasses(c, map); | ||
| 109 | } | 169 | } |
| 110 | } | 170 | } |
diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 94ab2c4..7952577 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java | |||
| @@ -10,8 +10,6 @@ | |||
| 10 | ******************************************************************************/ | 10 | ******************************************************************************/ |
| 11 | package cuchaz.enigma.bytecode; | 11 | package cuchaz.enigma.bytecode; |
| 12 | 12 | ||
| 13 | import java.util.Map; | ||
| 14 | |||
| 15 | import javassist.CtBehavior; | 13 | import javassist.CtBehavior; |
| 16 | import javassist.CtClass; | 14 | import javassist.CtClass; |
| 17 | import javassist.CtField; | 15 | import javassist.CtField; |
| @@ -19,9 +17,6 @@ import javassist.CtMethod; | |||
| 19 | import javassist.bytecode.ConstPool; | 17 | import javassist.bytecode.ConstPool; |
| 20 | import javassist.bytecode.Descriptor; | 18 | import javassist.bytecode.Descriptor; |
| 21 | import javassist.bytecode.SourceFileAttribute; | 19 | import javassist.bytecode.SourceFileAttribute; |
| 22 | |||
| 23 | import com.google.common.collect.Maps; | ||
| 24 | |||
| 25 | import cuchaz.enigma.mapping.BehaviorEntry; | 20 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 26 | import cuchaz.enigma.mapping.ClassEntry; | 21 | import cuchaz.enigma.mapping.ClassEntry; |
| 27 | import cuchaz.enigma.mapping.EntryFactory; | 22 | import cuchaz.enigma.mapping.EntryFactory; |
| @@ -50,20 +45,15 @@ public class ClassTranslator { | |||
| 50 | 45 | ||
| 51 | case ConstPool.CONST_Fieldref: { | 46 | case ConstPool.CONST_Fieldref: { |
| 52 | 47 | ||
| 53 | // translate the name | 48 | // translate the name and type |
| 54 | FieldEntry entry = new FieldEntry( | 49 | FieldEntry entry = EntryFactory.getFieldEntry( |
| 55 | new ClassEntry(Descriptor.toJvmName(constants.getFieldrefClassName(i))), | 50 | Descriptor.toJvmName(constants.getFieldrefClassName(i)), |
| 56 | constants.getFieldrefName(i), | 51 | constants.getFieldrefName(i), |
| 57 | new Type(constants.getFieldrefType(i)) | 52 | constants.getFieldrefType(i) |
| 58 | ); | 53 | ); |
| 59 | FieldEntry translatedEntry = m_translator.translateEntry(entry); | 54 | FieldEntry translatedEntry = m_translator.translateEntry(entry); |
| 60 | 55 | if (!entry.equals(translatedEntry)) { | |
| 61 | // translate the type | 56 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); |
| 62 | Type type = new Type(constants.getFieldrefType(i)); | ||
| 63 | Type translatedType = m_translator.translateType(type); | ||
| 64 | |||
| 65 | if (!entry.equals(translatedEntry) || !type.equals(translatedType)) { | ||
| 66 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedType.toString()); | ||
| 67 | } | 57 | } |
| 68 | } | 58 | } |
| 69 | break; | 59 | break; |
| @@ -71,15 +61,14 @@ public class ClassTranslator { | |||
| 71 | case ConstPool.CONST_Methodref: | 61 | case ConstPool.CONST_Methodref: |
| 72 | case ConstPool.CONST_InterfaceMethodref: { | 62 | case ConstPool.CONST_InterfaceMethodref: { |
| 73 | 63 | ||
| 74 | // translate the name and type | 64 | // translate the name and type (ie signature) |
| 75 | BehaviorEntry entry = EntryFactory.getBehaviorEntry( | 65 | BehaviorEntry entry = EntryFactory.getBehaviorEntry( |
| 76 | Descriptor.toJvmName(editor.getMemberrefClassname(i)), | 66 | Descriptor.toJvmName(editor.getMemberrefClassname(i)), |
| 77 | editor.getMemberrefName(i), | 67 | editor.getMemberrefName(i), |
| 78 | editor.getMemberrefType(i) | 68 | editor.getMemberrefType(i) |
| 79 | ); | 69 | ); |
| 80 | BehaviorEntry translatedEntry = m_translator.translateEntry(entry); | 70 | BehaviorEntry translatedEntry = m_translator.translateEntry(entry); |
| 81 | 71 | if (!entry.equals(translatedEntry)) { | |
| 82 | if (!entry.getName().equals(translatedEntry.getName()) || !entry.getSignature().equals(translatedEntry.getSignature())) { | ||
| 83 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); | 72 | editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); |
| 84 | } | 73 | } |
| 85 | } | 74 | } |
| @@ -120,25 +109,18 @@ public class ClassTranslator { | |||
| 120 | } | 109 | } |
| 121 | 110 | ||
| 122 | if (entry.getSignature() != null) { | 111 | if (entry.getSignature() != null) { |
| 123 | // translate the type | 112 | // translate the signature |
| 124 | Signature translatedSignature = m_translator.translateSignature(entry.getSignature()); | 113 | Signature translatedSignature = m_translator.translateSignature(entry.getSignature()); |
| 125 | behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); | 114 | behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); |
| 126 | } | 115 | } |
| 127 | } | 116 | } |
| 128 | 117 | ||
| 129 | // translate all the class names referenced in the code | 118 | // translate all the class names referenced in the code |
| 130 | // the above code only changed method/field/reference names and types, but not the class names themselves | 119 | // the above code only changed method/field/reference names and types, but not the rest of the class references |
| 131 | Map<ClassEntry,ClassEntry> map = Maps.newHashMap(); | 120 | ClassRenamer.renameClasses(c, m_translator); |
| 132 | for (ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries(c)) { | ||
| 133 | ClassEntry deobfClassEntry = m_translator.translateEntry(obfClassEntry); | ||
| 134 | if (!obfClassEntry.equals(deobfClassEntry)) { | ||
| 135 | map.put(obfClassEntry, deobfClassEntry); | ||
| 136 | } | ||
| 137 | } | ||
| 138 | ClassRenamer.renameClasses(c, map); | ||
| 139 | 121 | ||
| 140 | // translate the source file attribute too | 122 | // translate the source file attribute too |
| 141 | ClassEntry deobfClassEntry = map.get(classEntry); | 123 | ClassEntry deobfClassEntry = m_translator.translateEntry(classEntry); |
| 142 | if (deobfClassEntry != null) { | 124 | if (deobfClassEntry != null) { |
| 143 | String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassName()) + ".java"; | 125 | String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassName()) + ".java"; |
| 144 | c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); | 126 | c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); |
diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index 7bc6183..4898e6d 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java | |||
| @@ -11,6 +11,7 @@ import javassist.expr.FieldAccess; | |||
| 11 | import javassist.expr.MethodCall; | 11 | import javassist.expr.MethodCall; |
| 12 | import javassist.expr.NewExpr; | 12 | import javassist.expr.NewExpr; |
| 13 | 13 | ||
| 14 | import com.strobel.assembler.metadata.FieldDefinition; | ||
| 14 | import com.strobel.assembler.metadata.MethodDefinition; | 15 | import com.strobel.assembler.metadata.MethodDefinition; |
| 15 | 16 | ||
| 16 | import cuchaz.enigma.analysis.JarIndex; | 17 | import cuchaz.enigma.analysis.JarIndex; |
| @@ -54,6 +55,18 @@ public class EntryFactory { | |||
| 54 | ); | 55 | ); |
| 55 | } | 56 | } |
| 56 | 57 | ||
| 58 | public static FieldEntry getFieldEntry(FieldDefinition def) { | ||
| 59 | return new FieldEntry( | ||
| 60 | new ClassEntry(def.getDeclaringType().getInternalName()), | ||
| 61 | def.getName(), | ||
| 62 | new Type(def.getErasedSignature()) | ||
| 63 | ); | ||
| 64 | } | ||
| 65 | |||
| 66 | public static FieldEntry getFieldEntry(String className, String name, String type) { | ||
| 67 | return new FieldEntry(new ClassEntry(className), name, new Type(type)); | ||
| 68 | } | ||
| 69 | |||
| 57 | public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { | 70 | public static FieldEntry getObfFieldEntry(ClassMapping classMapping, FieldMapping fieldMapping) { |
| 58 | return new FieldEntry( | 71 | return new FieldEntry( |
| 59 | getObfClassEntry(classMapping), | 72 | getObfClassEntry(classMapping), |
| @@ -82,7 +95,7 @@ public class EntryFactory { | |||
| 82 | return new MethodEntry( | 95 | return new MethodEntry( |
| 83 | new ClassEntry(def.getDeclaringType().getInternalName()), | 96 | new ClassEntry(def.getDeclaringType().getInternalName()), |
| 84 | def.getName(), | 97 | def.getName(), |
| 85 | new Signature(def.getSignature()) | 98 | new Signature(def.getErasedSignature()) |
| 86 | ); | 99 | ); |
| 87 | } | 100 | } |
| 88 | 101 | ||
| @@ -121,7 +134,7 @@ public class EntryFactory { | |||
| 121 | } else { | 134 | } else { |
| 122 | return new ConstructorEntry( | 135 | return new ConstructorEntry( |
| 123 | new ClassEntry(def.getDeclaringType().getInternalName()), | 136 | new ClassEntry(def.getDeclaringType().getInternalName()), |
| 124 | new Signature(def.getSignature()) | 137 | new Signature(def.getErasedSignature()) |
| 125 | ); | 138 | ); |
| 126 | } | 139 | } |
| 127 | } | 140 | } |
diff --git a/src/cuchaz/enigma/mapping/MappingsChecker.java b/src/cuchaz/enigma/mapping/MappingsChecker.java index c5ff7a7..57ea90c 100644 --- a/src/cuchaz/enigma/mapping/MappingsChecker.java +++ b/src/cuchaz/enigma/mapping/MappingsChecker.java | |||
| @@ -66,7 +66,7 @@ public class MappingsChecker { | |||
| 66 | 66 | ||
| 67 | // check the fields | 67 | // check the fields |
| 68 | for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { | 68 | for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { |
| 69 | FieldEntry obfFieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName(), fieldMapping.getObfType()); | 69 | FieldEntry obfFieldEntry = EntryFactory.getObfFieldEntry(classMapping, fieldMapping); |
| 70 | if (!m_index.containsObfField(obfFieldEntry)) { | 70 | if (!m_index.containsObfField(obfFieldEntry)) { |
| 71 | classMapping.removeFieldMapping(fieldMapping); | 71 | classMapping.removeFieldMapping(fieldMapping); |
| 72 | m_droppedFieldMappings.put(obfFieldEntry, fieldMapping); | 72 | m_droppedFieldMappings.put(obfFieldEntry, fieldMapping); |
diff --git a/src/cuchaz/enigma/mapping/MappingsRenamer.java b/src/cuchaz/enigma/mapping/MappingsRenamer.java index d7766dc..ad6c878 100644 --- a/src/cuchaz/enigma/mapping/MappingsRenamer.java +++ b/src/cuchaz/enigma/mapping/MappingsRenamer.java | |||
| @@ -66,7 +66,15 @@ public class MappingsRenamer { | |||
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | public void markClassAsDeobfuscated(ClassEntry obf) { | 68 | public void markClassAsDeobfuscated(ClassEntry obf) { |
| 69 | setClassName(obf, obf.isInnerClass() ? obf.getInnermostClassName() : obf.getSimpleName()); | 69 | String deobfName = obf.isInnerClass() ? obf.getInnermostClassName() : obf.getName(); |
| 70 | List<ClassMapping> mappingChain = getOrCreateClassMappingChain(obf); | ||
| 71 | if (mappingChain.size() == 1) { | ||
| 72 | ClassMapping classMapping = mappingChain.get(0); | ||
| 73 | m_mappings.setClassDeobfName(classMapping, deobfName); | ||
| 74 | } else { | ||
| 75 | ClassMapping outerClassMapping = mappingChain.get(mappingChain.size() - 2); | ||
| 76 | outerClassMapping.setInnerClassName(obf, deobfName); | ||
| 77 | } | ||
| 70 | } | 78 | } |
| 71 | 79 | ||
| 72 | public void setFieldName(FieldEntry obf, String deobfName) { | 80 | public void setFieldName(FieldEntry obf, String deobfName) { |
diff --git a/src/cuchaz/enigma/mapping/ParameterizedType.java b/src/cuchaz/enigma/mapping/ParameterizedType.java new file mode 100644 index 0000000..af24ef4 --- /dev/null +++ b/src/cuchaz/enigma/mapping/ParameterizedType.java | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | package cuchaz.enigma.mapping; | ||
| 2 | |||
| 3 | import cuchaz.enigma.Util; | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | public class ParameterizedType extends Type { | ||
| 8 | |||
| 9 | private static final long serialVersionUID = 1758975507937309011L; | ||
| 10 | |||
| 11 | public ParameterizedType(Type other) { | ||
| 12 | super(other); | ||
| 13 | for (int i=0; i<m_parameters.size(); i++) { | ||
| 14 | m_parameters.set(i, new ParameterizedType(m_parameters.get(i))); | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | public ParameterizedType(ParameterizedType type, ClassNameReplacer replacer) { | ||
| 19 | this(new Type(type, replacer)); | ||
| 20 | } | ||
| 21 | |||
| 22 | @Override | ||
| 23 | public String toString() { | ||
| 24 | if (hasParameters()) { | ||
| 25 | StringBuilder buf = new StringBuilder(); | ||
| 26 | buf.append(m_name.substring(0, m_name.length() - 1)); | ||
| 27 | buf.append("<"); | ||
| 28 | for (Type parameter : parameters()) { | ||
| 29 | buf.append(parameter.toString()); | ||
| 30 | } | ||
| 31 | buf.append(">;"); | ||
| 32 | return buf.toString(); | ||
| 33 | } else { | ||
| 34 | return m_name; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | @Override | ||
| 39 | public boolean equals(Object other) { | ||
| 40 | if (other instanceof ParameterizedType) { | ||
| 41 | return equals((ParameterizedType)other); | ||
| 42 | } | ||
| 43 | return false; | ||
| 44 | } | ||
| 45 | |||
| 46 | public boolean equals(ParameterizedType other) { | ||
| 47 | return m_name.equals(other.m_name) && m_parameters.equals(other.m_parameters); | ||
| 48 | } | ||
| 49 | |||
| 50 | public int hashCode() { | ||
| 51 | return Util.combineHashesOrdered(m_name.hashCode(), m_parameters.hashCode()); | ||
| 52 | } | ||
| 53 | |||
| 54 | } | ||
diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java index ea83e40..f4850ac 100644 --- a/src/cuchaz/enigma/mapping/Signature.java +++ b/src/cuchaz/enigma/mapping/Signature.java | |||
| @@ -79,16 +79,6 @@ public class Signature implements Serializable { | |||
| 79 | return types; | 79 | return types; |
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | public Iterable<ClassEntry> classes() { | ||
| 83 | List<ClassEntry> out = Lists.newArrayList(); | ||
| 84 | for (Type type : types()) { | ||
| 85 | if (type.isClass()) { | ||
| 86 | out.add(type.getClassEntry()); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | return out; | ||
| 90 | } | ||
| 91 | |||
| 92 | @Override | 82 | @Override |
| 93 | public boolean equals(Object other) { | 83 | public boolean equals(Object other) { |
| 94 | if (other instanceof Signature) { | 84 | if (other instanceof Signature) { |
diff --git a/src/cuchaz/enigma/mapping/Type.java b/src/cuchaz/enigma/mapping/Type.java index 72118b0..d530083 100644 --- a/src/cuchaz/enigma/mapping/Type.java +++ b/src/cuchaz/enigma/mapping/Type.java | |||
| @@ -1,8 +1,10 @@ | |||
| 1 | package cuchaz.enigma.mapping; | 1 | package cuchaz.enigma.mapping; |
| 2 | 2 | ||
| 3 | import java.io.Serializable; | 3 | import java.io.Serializable; |
| 4 | import java.util.List; | ||
| 4 | import java.util.Map; | 5 | import java.util.Map; |
| 5 | 6 | ||
| 7 | import com.beust.jcommander.internal.Lists; | ||
| 6 | import com.google.common.collect.Maps; | 8 | import com.google.common.collect.Maps; |
| 7 | 9 | ||
| 8 | public class Type implements Serializable { | 10 | public class Type implements Serializable { |
| @@ -84,33 +86,66 @@ public class Type implements Serializable { | |||
| 84 | throw new IllegalArgumentException("don't know how to parse: " + in); | 86 | throw new IllegalArgumentException("don't know how to parse: " + in); |
| 85 | } | 87 | } |
| 86 | 88 | ||
| 87 | private String m_name; | 89 | protected String m_name; |
| 90 | protected List<Type> m_parameters; | ||
| 88 | 91 | ||
| 89 | public Type(String name) { | 92 | public Type(String name) { |
| 90 | m_name = name; | 93 | m_name = null; |
| 94 | m_parameters = Lists.newArrayList(); | ||
| 95 | |||
| 96 | int start = name.indexOf('<'); | ||
| 97 | int stop = name.lastIndexOf('>'); | ||
| 98 | if (start > 0 && stop > start) { | ||
| 99 | |||
| 100 | // deal with generic parameters | ||
| 101 | m_name = name.substring(0, start) + name.substring(stop + 1); | ||
| 102 | |||
| 103 | String parameters = name.substring(start + 1, stop); | ||
| 104 | int i=0; | ||
| 105 | while (i<parameters.length()) { | ||
| 106 | String typeName = Type.parseFirst(parameters.substring(i)); | ||
| 107 | if (typeName == null) { | ||
| 108 | throw new Error("Don't know how to parse parameters: " + name); | ||
| 109 | } | ||
| 110 | m_parameters.add(new Type(typeName)); | ||
| 111 | i += typeName.length(); | ||
| 112 | } | ||
| 113 | |||
| 114 | } else { | ||
| 115 | m_name = name; | ||
| 116 | } | ||
| 91 | } | 117 | } |
| 92 | 118 | ||
| 93 | public Type(Type other) { | 119 | public Type(Type other) { |
| 94 | m_name = other.m_name; | 120 | m_name = other.m_name; |
| 121 | m_parameters = Lists.newArrayList(); | ||
| 122 | for (Type parameter : other.m_parameters) { | ||
| 123 | m_parameters.add(new Type(parameter)); | ||
| 124 | } | ||
| 95 | } | 125 | } |
| 96 | 126 | ||
| 97 | public Type(ClassEntry classEntry) { | 127 | public Type(ClassEntry classEntry) { |
| 98 | m_name = "L" + classEntry.getClassName() + ";"; | 128 | m_name = "L" + classEntry.getClassName() + ";"; |
| 99 | } | 129 | } |
| 100 | 130 | ||
| 101 | public Type(Type type, ClassNameReplacer replacer) { | 131 | public Type(Type other, ClassNameReplacer replacer) { |
| 102 | m_name = type.m_name; | 132 | m_name = other.m_name; |
| 103 | if (type.isClass()) { | 133 | if (other.isClass()) { |
| 104 | String replacedName = replacer.replace(type.getClassEntry().getClassName()); | 134 | String replacedName = replacer.replace(other.getClassEntry().getClassName()); |
| 105 | if (replacedName != null) { | 135 | if (replacedName != null) { |
| 106 | m_name = "L" + replacedName + ";"; | 136 | m_name = "L" + replacedName + ";"; |
| 107 | } | 137 | } |
| 108 | } else if (type.isArray() && type.hasClass()) { | 138 | } else if (other.isArray() && other.hasClass()) { |
| 109 | String replacedName = replacer.replace(type.getClassEntry().getClassName()); | 139 | String replacedName = replacer.replace(other.getClassEntry().getClassName()); |
| 110 | if (replacedName != null) { | 140 | if (replacedName != null) { |
| 111 | m_name = Type.getArrayPrefix(type.getArrayDimension()) + "L" + replacedName + ";"; | 141 | m_name = Type.getArrayPrefix(other.getArrayDimension()) + "L" + replacedName + ";"; |
| 112 | } | 142 | } |
| 113 | } | 143 | } |
| 144 | |||
| 145 | m_parameters = Lists.newArrayList(); | ||
| 146 | for (Type parameter : other.m_parameters) { | ||
| 147 | m_parameters.add(new Type(parameter, replacer)); | ||
| 148 | } | ||
| 114 | } | 149 | } |
| 115 | 150 | ||
| 116 | @Override | 151 | @Override |
| @@ -137,10 +172,6 @@ public class Type implements Serializable { | |||
| 137 | return m_name.charAt(0) == 'L' && m_name.charAt(m_name.length() - 1) == ';'; | 172 | return m_name.charAt(0) == 'L' && m_name.charAt(m_name.length() - 1) == ';'; |
| 138 | } | 173 | } |
| 139 | 174 | ||
| 140 | public boolean isTemplate() { | ||
| 141 | return m_name.charAt(0) == 'T' && m_name.charAt(m_name.length() - 1) == ';'; | ||
| 142 | } | ||
| 143 | |||
| 144 | public ClassEntry getClassEntry() { | 175 | public ClassEntry getClassEntry() { |
| 145 | if (isClass()) { | 176 | if (isClass()) { |
| 146 | String name = m_name.substring(1, m_name.length() - 1); | 177 | String name = m_name.substring(1, m_name.length() - 1); |
| @@ -160,6 +191,17 @@ public class Type implements Serializable { | |||
| 160 | } | 191 | } |
| 161 | } | 192 | } |
| 162 | 193 | ||
| 194 | public boolean isTemplate() { | ||
| 195 | return m_name.charAt(0) == 'T' && m_name.charAt(m_name.length() - 1) == ';'; | ||
| 196 | } | ||
| 197 | |||
| 198 | public String getTemplate() { | ||
| 199 | if (!isTemplate()) { | ||
| 200 | throw new IllegalStateException("not an template"); | ||
| 201 | } | ||
| 202 | return m_name.substring(1, m_name.length() - 1); | ||
| 203 | } | ||
| 204 | |||
| 163 | public boolean isArray() { | 205 | public boolean isArray() { |
| 164 | return m_name.charAt(0) == '['; | 206 | return m_name.charAt(0) == '['; |
| 165 | } | 207 | } |
| @@ -190,6 +232,14 @@ public class Type implements Serializable { | |||
| 190 | return isClass() || (isArray() && getArrayType().hasClass()); | 232 | return isClass() || (isArray() && getArrayType().hasClass()); |
| 191 | } | 233 | } |
| 192 | 234 | ||
| 235 | public boolean hasParameters() { | ||
| 236 | return !m_parameters.isEmpty(); | ||
| 237 | } | ||
| 238 | |||
| 239 | public Iterable<Type> parameters() { | ||
| 240 | return m_parameters; | ||
| 241 | } | ||
| 242 | |||
| 193 | @Override | 243 | @Override |
| 194 | public boolean equals(Object other) { | 244 | public boolean equals(Object other) { |
| 195 | if (other instanceof Type) { | 245 | if (other instanceof Type) { |
| @@ -214,7 +264,7 @@ public class Type implements Serializable { | |||
| 214 | 264 | ||
| 215 | private static String readClass(String in) { | 265 | private static String readClass(String in) { |
| 216 | // read all the characters in the buffer until we hit a ';' | 266 | // read all the characters in the buffer until we hit a ';' |
| 217 | // remember to treat parameters correctly | 267 | // include the parameters too |
| 218 | StringBuilder buf = new StringBuilder(); | 268 | StringBuilder buf = new StringBuilder(); |
| 219 | int depth = 0; | 269 | int depth = 0; |
| 220 | for (int i=0; i<in.length(); i++) { | 270 | for (int i=0; i<in.length(); i++) { |