diff options
| author | 2015-03-16 19:22:22 -0400 | |
|---|---|---|
| committer | 2015-03-16 19:22:22 -0400 | |
| commit | 5e3743a0aca3529eacf9be400c8b8d7547f66e7f (patch) | |
| tree | ea601747547f78e1b83ab828650932126440e221 /src/cuchaz/enigma/bytecode | |
| 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/cuchaz/enigma/bytecode')
| -rw-r--r-- | src/cuchaz/enigma/bytecode/ClassRenamer.java | 164 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/ClassTranslator.java | 42 |
2 files changed, 124 insertions, 82 deletions
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)); |