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/ClassRenamer.java | |
| 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 '')
| -rw-r--r-- | src/cuchaz/enigma/bytecode/ClassRenamer.java | 164 |
1 files changed, 112 insertions, 52 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 | } |