diff options
| author | 2015-02-25 22:42:34 -0500 | |
|---|---|---|
| committer | 2015-02-25 22:42:34 -0500 | |
| commit | 9809078524bd3bd40fbf7aa411f6e0dca02fd009 (patch) | |
| tree | 19f9ee0613231c747e728bc61f8a1b6ffa58e5b7 /src | |
| parent | more work getting inner class trees working in obf'd and deobf'd land (diff) | |
| download | enigma-9809078524bd3bd40fbf7aa411f6e0dca02fd009.tar.gz enigma-9809078524bd3bd40fbf7aa411f6e0dca02fd009.tar.xz enigma-9809078524bd3bd40fbf7aa411f6e0dca02fd009.zip | |
fixed lots of issues with inner class reconstruction, particularly for inner class trees
also fixed lots of issues with reading jars that aren't Minecraft. =P
Diffstat (limited to 'src')
| -rw-r--r-- | src/cuchaz/enigma/Deobfuscator.java | 33 | ||||
| -rw-r--r-- | src/cuchaz/enigma/TranslatingTypeLoader.java | 113 | ||||
| -rw-r--r-- | src/cuchaz/enigma/analysis/JarIndex.java | 29 | ||||
| -rw-r--r-- | src/cuchaz/enigma/bytecode/InnerClassWriter.java | 84 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/ClassEntry.java | 22 | ||||
| -rw-r--r-- | src/cuchaz/enigma/mapping/EntryFactory.java | 34 |
6 files changed, 156 insertions, 159 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index b7440a72..0b7808da 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java | |||
| @@ -204,33 +204,36 @@ public class Deobfuscator { | |||
| 204 | } | 204 | } |
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | public CompilationUnit getSourceTree(String obfClassName) { | 207 | public CompilationUnit getSourceTree(String className) { |
| 208 | // is this class deobfuscated? | 208 | |
| 209 | // we don't know if this class name is obfuscated or deobfuscated | ||
| 209 | // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out | 210 | // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out |
| 210 | // the decompiler only sees the deobfuscated class, so we need to load it by the deobfuscated name | 211 | // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one |
| 211 | String lookupClassName = obfClassName; | ||
| 212 | ClassMapping classMapping = m_mappings.getClassByObf(obfClassName); | ||
| 213 | if (classMapping != null && classMapping.getDeobfName() != null) { | ||
| 214 | lookupClassName = classMapping.getDeobfName(); | ||
| 215 | } | ||
| 216 | 212 | ||
| 217 | // is this class even in the jar? | 213 | // first, assume class name is deobf |
| 218 | if (!m_jarIndex.containsObfClass(new ClassEntry(obfClassName))) { | 214 | String deobfClassName = className; |
| 219 | return null; | 215 | |
| 216 | // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name | ||
| 217 | ClassMapping classMapping = m_mappings.getClassByObf(className); | ||
| 218 | if (classMapping != null && classMapping.getDeobfName() != null) { | ||
| 219 | deobfClassName = classMapping.getDeobfName(); | ||
| 220 | } | 220 | } |
| 221 | 221 | ||
| 222 | // set the type loader | 222 | // set the type loader |
| 223 | m_settings.setTypeLoader(new TranslatingTypeLoader( | 223 | TranslatingTypeLoader loader = new TranslatingTypeLoader( |
| 224 | m_jar, | 224 | m_jar, |
| 225 | m_jarIndex, | 225 | m_jarIndex, |
| 226 | getTranslator(TranslationDirection.Obfuscating), | 226 | getTranslator(TranslationDirection.Obfuscating), |
| 227 | getTranslator(TranslationDirection.Deobfuscating) | 227 | getTranslator(TranslationDirection.Deobfuscating) |
| 228 | )); | 228 | ); |
| 229 | m_settings.setTypeLoader(loader); | ||
| 229 | 230 | ||
| 230 | // see if procyon can find the type | 231 | // see if procyon can find the type |
| 231 | TypeReference type = new MetadataSystem(m_settings.getTypeLoader()).lookupType(lookupClassName); | 232 | TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); |
| 232 | if (type == null) { | 233 | if (type == null) { |
| 233 | throw new Error("Unable to find type: " + lookupClassName + " (obf name: " + obfClassName + ")"); | 234 | throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s", |
| 235 | className, deobfClassName, loader.getClassNamesToTry(deobfClassName) | ||
| 236 | )); | ||
| 234 | } | 237 | } |
| 235 | TypeDefinition resolvedType = type.resolve(); | 238 | TypeDefinition resolvedType = type.resolve(); |
| 236 | 239 | ||
diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index 26d5e7a7..94ad6ebe 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java | |||
| @@ -13,6 +13,7 @@ package cuchaz.enigma; | |||
| 13 | import java.io.ByteArrayOutputStream; | 13 | import java.io.ByteArrayOutputStream; |
| 14 | import java.io.IOException; | 14 | import java.io.IOException; |
| 15 | import java.io.InputStream; | 15 | import java.io.InputStream; |
| 16 | import java.util.List; | ||
| 16 | import java.util.Map; | 17 | import java.util.Map; |
| 17 | import java.util.jar.JarEntry; | 18 | import java.util.jar.JarEntry; |
| 18 | import java.util.jar.JarFile; | 19 | import java.util.jar.JarFile; |
| @@ -24,6 +25,7 @@ import javassist.CtClass; | |||
| 24 | import javassist.NotFoundException; | 25 | import javassist.NotFoundException; |
| 25 | import javassist.bytecode.Descriptor; | 26 | import javassist.bytecode.Descriptor; |
| 26 | 27 | ||
| 28 | import com.beust.jcommander.internal.Lists; | ||
| 27 | import com.google.common.collect.Maps; | 29 | import com.google.common.collect.Maps; |
| 28 | import com.strobel.assembler.metadata.Buffer; | 30 | import com.strobel.assembler.metadata.Buffer; |
| 29 | import com.strobel.assembler.metadata.ClasspathTypeLoader; | 31 | import com.strobel.assembler.metadata.ClasspathTypeLoader; |
| @@ -65,19 +67,20 @@ public class TranslatingTypeLoader implements ITypeLoader { | |||
| 65 | } | 67 | } |
| 66 | 68 | ||
| 67 | @Override | 69 | @Override |
| 68 | public boolean tryLoadType(String deobfClassName, Buffer out) { | 70 | public boolean tryLoadType(String className, Buffer out) { |
| 71 | |||
| 69 | // check the cache | 72 | // check the cache |
| 70 | byte[] data; | 73 | byte[] data; |
| 71 | if (m_cache.containsKey(deobfClassName)) { | 74 | if (m_cache.containsKey(className)) { |
| 72 | data = m_cache.get(deobfClassName); | 75 | data = m_cache.get(className); |
| 73 | } else { | 76 | } else { |
| 74 | data = loadType(deobfClassName); | 77 | data = loadType(className); |
| 75 | m_cache.put(deobfClassName, data); | 78 | m_cache.put(className, data); |
| 76 | } | 79 | } |
| 77 | 80 | ||
| 78 | if (data == null) { | 81 | if (data == null) { |
| 79 | // chain to default type loader | 82 | // chain to default type loader |
| 80 | return m_defaultTypeLoader.tryLoadType(deobfClassName, out); | 83 | return m_defaultTypeLoader.tryLoadType(className, out); |
| 81 | } | 84 | } |
| 82 | 85 | ||
| 83 | // send the class to the decompiler | 86 | // send the class to the decompiler |
| @@ -88,6 +91,7 @@ public class TranslatingTypeLoader implements ITypeLoader { | |||
| 88 | } | 91 | } |
| 89 | 92 | ||
| 90 | public CtClass loadClass(String deobfClassName) { | 93 | public CtClass loadClass(String deobfClassName) { |
| 94 | |||
| 91 | byte[] data = loadType(deobfClassName); | 95 | byte[] data = loadType(deobfClassName); |
| 92 | if (data == null) { | 96 | if (data == null) { |
| 93 | return null; | 97 | return null; |
| @@ -104,40 +108,35 @@ public class TranslatingTypeLoader implements ITypeLoader { | |||
| 104 | } | 108 | } |
| 105 | } | 109 | } |
| 106 | 110 | ||
| 107 | private byte[] loadType(String deobfClassName) { | 111 | private byte[] loadType(String className) { |
| 108 | 112 | ||
| 109 | ClassEntry deobfClassEntry = new ClassEntry(deobfClassName); | 113 | // NOTE: don't know if class name is obf or deobf |
| 110 | ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(deobfClassEntry); | 114 | ClassEntry classEntry = new ClassEntry(className); |
| 111 | 115 | ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry(classEntry); | |
| 112 | // is this an inner class referenced directly? | 116 | |
| 113 | ClassEntry obfOuterClassEntry = m_jarIndex.getOuterClass(obfClassEntry); | 117 | // is this an inner class referenced directly? (ie trying to load b instead of a$b) |
| 114 | if (obfOuterClassEntry != null) { | 118 | if (!obfClassEntry.isInnerClass()) { |
| 115 | // this class doesn't really exist. Reference it by outer$inner instead | 119 | List<ClassEntry> classChain = m_jarIndex.getObfClassChain(obfClassEntry); |
| 116 | System.err.println(String.format("WARNING: class %s referenced by bare inner name instead of via outer class %s", deobfClassName, obfOuterClassEntry)); | 120 | if (classChain.size() > 1) { |
| 117 | return null; | 121 | System.err.println(String.format("WARNING: no class %s after inner class reconstruction. Try %s", |
| 118 | } | 122 | className, obfClassEntry.buildClassEntry(classChain) |
| 119 | 123 | )); | |
| 120 | /* DEBUG | 124 | return null; |
| 121 | if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) { | 125 | } |
| 122 | System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); | ||
| 123 | } | 126 | } |
| 124 | */ | ||
| 125 | 127 | ||
| 126 | // get the jar entry | 128 | // is this a class we should even know about? |
| 127 | String classFileName; | 129 | if (!m_jarIndex.containsObfClass(obfClassEntry)) { |
| 128 | if (obfClassEntry.isInnerClass()) { | 130 | return null; |
| 129 | // use just the inner class name for inner classes | ||
| 130 | classFileName = obfClassEntry.getInnerClassName(); | ||
| 131 | } else if (obfClassEntry.getPackageName().equals(Constants.NonePackage)) { | ||
| 132 | // use the outer class simple name for classes in the none package | ||
| 133 | classFileName = obfClassEntry.getSimpleName(); | ||
| 134 | } else { | ||
| 135 | // otherwise, just use the class name (ie for classes in packages) | ||
| 136 | classFileName = obfClassEntry.getName(); | ||
| 137 | } | 131 | } |
| 138 | 132 | ||
| 139 | JarEntry entry = m_jar.getJarEntry(classFileName + ".class"); | 133 | // DEBUG |
| 140 | if (entry == null) { | 134 | //System.out.println(String.format("Looking for %s (obf: %s)", classEntry.getName(), obfClassEntry.getName())); |
| 135 | |||
| 136 | // find the class in the jar | ||
| 137 | String classInJarName = findClassInJar(obfClassEntry); | ||
| 138 | if (classInJarName == null) { | ||
| 139 | // couldn't find it | ||
| 141 | return null; | 140 | return null; |
| 142 | } | 141 | } |
| 143 | 142 | ||
| @@ -145,7 +144,7 @@ public class TranslatingTypeLoader implements ITypeLoader { | |||
| 145 | // read the class file into a buffer | 144 | // read the class file into a buffer |
| 146 | ByteArrayOutputStream data = new ByteArrayOutputStream(); | 145 | ByteArrayOutputStream data = new ByteArrayOutputStream(); |
| 147 | byte[] buf = new byte[1024 * 1024]; // 1 KiB | 146 | byte[] buf = new byte[1024 * 1024]; // 1 KiB |
| 148 | InputStream in = m_jar.getInputStream(entry); | 147 | InputStream in = m_jar.getInputStream(m_jar.getJarEntry(classInJarName + ".class")); |
| 149 | while (true) { | 148 | while (true) { |
| 150 | int bytesRead = in.read(buf); | 149 | int bytesRead = in.read(buf); |
| 151 | if (bytesRead <= 0) { | 150 | if (bytesRead <= 0) { |
| @@ -158,15 +157,15 @@ public class TranslatingTypeLoader implements ITypeLoader { | |||
| 158 | buf = data.toByteArray(); | 157 | buf = data.toByteArray(); |
| 159 | 158 | ||
| 160 | // load the javassist handle to the raw class | 159 | // load the javassist handle to the raw class |
| 161 | String javaClassFileName = Descriptor.toJavaName(classFileName); | ||
| 162 | ClassPool classPool = new ClassPool(); | 160 | ClassPool classPool = new ClassPool(); |
| 163 | classPool.insertClassPath(new ByteArrayClassPath(javaClassFileName, buf)); | 161 | String classInJarJavaName = Descriptor.toJavaName(classInJarName); |
| 164 | CtClass c = classPool.get(javaClassFileName); | 162 | classPool.insertClassPath(new ByteArrayClassPath(classInJarJavaName, buf)); |
| 163 | CtClass c = classPool.get(classInJarJavaName); | ||
| 165 | 164 | ||
| 166 | c = transformClass(c); | 165 | c = transformClass(c); |
| 167 | 166 | ||
| 168 | // sanity checking | 167 | // sanity checking |
| 169 | assertClassName(c, deobfClassEntry); | 168 | assertClassName(c, classEntry); |
| 170 | 169 | ||
| 171 | // DEBUG | 170 | // DEBUG |
| 172 | //Util.writeClass( c ); | 171 | //Util.writeClass( c ); |
| @@ -178,6 +177,38 @@ public class TranslatingTypeLoader implements ITypeLoader { | |||
| 178 | } | 177 | } |
| 179 | } | 178 | } |
| 180 | 179 | ||
| 180 | private String findClassInJar(ClassEntry obfClassEntry) { | ||
| 181 | |||
| 182 | // try to find the class in the jar | ||
| 183 | for (String className : getClassNamesToTry(obfClassEntry)) { | ||
| 184 | JarEntry jarEntry = m_jar.getJarEntry(className + ".class"); | ||
| 185 | if (jarEntry != null) { | ||
| 186 | return className; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | // didn't find it ;_; | ||
| 191 | return null; | ||
| 192 | } | ||
| 193 | |||
| 194 | public List<String> getClassNamesToTry(String className) { | ||
| 195 | return getClassNamesToTry(m_obfuscatingTranslator.translateEntry(new ClassEntry(className))); | ||
| 196 | } | ||
| 197 | |||
| 198 | public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { | ||
| 199 | List<String> classNamesToTry = Lists.newArrayList(); | ||
| 200 | classNamesToTry.add(obfClassEntry.getName()); | ||
| 201 | if (obfClassEntry.getPackageName().equals(Constants.NonePackage)) { | ||
| 202 | // taking off the none package, if any | ||
| 203 | classNamesToTry.add(obfClassEntry.getSimpleName()); | ||
| 204 | } | ||
| 205 | if (obfClassEntry.isInnerClass()) { | ||
| 206 | // try just the inner class name | ||
| 207 | classNamesToTry.add(obfClassEntry.getInnerClassName()); | ||
| 208 | } | ||
| 209 | return classNamesToTry; | ||
| 210 | } | ||
| 211 | |||
| 181 | public CtClass transformClass(CtClass c) | 212 | public CtClass transformClass(CtClass c) |
| 182 | throws IOException, NotFoundException, CannotCompileException { | 213 | throws IOException, NotFoundException, CannotCompileException { |
| 183 | 214 | ||
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 1afcb76c..e0a8bf52 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java | |||
| @@ -12,6 +12,7 @@ package cuchaz.enigma.analysis; | |||
| 12 | 12 | ||
| 13 | import java.lang.reflect.Modifier; | 13 | import java.lang.reflect.Modifier; |
| 14 | import java.util.Collection; | 14 | import java.util.Collection; |
| 15 | import java.util.Collections; | ||
| 15 | import java.util.HashSet; | 16 | import java.util.HashSet; |
| 16 | import java.util.List; | 17 | import java.util.List; |
| 17 | import java.util.Map; | 18 | import java.util.Map; |
| @@ -156,11 +157,8 @@ public class JarIndex { | |||
| 156 | 157 | ||
| 157 | // step 6: update other indices with inner class info | 158 | // step 6: update other indices with inner class info |
| 158 | Map<String,String> renames = Maps.newHashMap(); | 159 | Map<String,String> renames = Maps.newHashMap(); |
| 159 | for (Map.Entry<ClassEntry,ClassEntry> mapEntry : m_innerClassesByOuter.entries()) { | 160 | for (ClassEntry innerClassEntry : m_innerClassesByOuter.values()) { |
| 160 | ClassEntry outerClassEntry = mapEntry.getKey(); | 161 | String newName = innerClassEntry.buildClassEntry(getObfClassChain(innerClassEntry)).getName(); |
| 161 | ClassEntry innerClassEntry = mapEntry.getValue(); | ||
| 162 | outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry); | ||
| 163 | String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName(); | ||
| 164 | if (!innerClassEntry.getName().equals(newName)) { | 162 | if (!innerClassEntry.getName().equals(newName)) { |
| 165 | // DEBUG | 163 | // DEBUG |
| 166 | //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); | 164 | //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); |
| @@ -780,4 +778,25 @@ public class JarIndex { | |||
| 780 | public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { | 778 | public MethodEntry getBridgedMethod(MethodEntry bridgeMethodEntry) { |
| 781 | return m_bridgedMethods.get(bridgeMethodEntry); | 779 | return m_bridgedMethods.get(bridgeMethodEntry); |
| 782 | } | 780 | } |
| 781 | |||
| 782 | public List<ClassEntry> getObfClassChain(ClassEntry obfClassEntry) { | ||
| 783 | |||
| 784 | // build class chain in inner-to-outer order | ||
| 785 | List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry); | ||
| 786 | ClassEntry checkClassEntry = obfClassEntry; | ||
| 787 | while (true) { | ||
| 788 | ClassEntry obfOuterClassEntry = getOuterClass(checkClassEntry); | ||
| 789 | if (obfOuterClassEntry != null) { | ||
| 790 | obfClassChain.add(obfOuterClassEntry); | ||
| 791 | checkClassEntry = obfOuterClassEntry; | ||
| 792 | } else { | ||
| 793 | break; | ||
| 794 | } | ||
| 795 | } | ||
| 796 | |||
| 797 | // switch to outer-to-inner order | ||
| 798 | Collections.reverse(obfClassChain); | ||
| 799 | |||
| 800 | return obfClassChain; | ||
| 801 | } | ||
| 783 | } | 802 | } |
diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index 3bebd176..dd21a780 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java | |||
| @@ -11,7 +11,6 @@ | |||
| 11 | package cuchaz.enigma.bytecode; | 11 | package cuchaz.enigma.bytecode; |
| 12 | 12 | ||
| 13 | import java.util.Collection; | 13 | import java.util.Collection; |
| 14 | import java.util.Collections; | ||
| 15 | import java.util.List; | 14 | import java.util.List; |
| 16 | 15 | ||
| 17 | import javassist.CtClass; | 16 | import javassist.CtClass; |
| @@ -36,35 +35,14 @@ public class InnerClassWriter { | |||
| 36 | 35 | ||
| 37 | public void write(CtClass c) { | 36 | public void write(CtClass c) { |
| 38 | 37 | ||
| 39 | // build the class chain (inner to outer) | ||
| 40 | ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); | 38 | ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); |
| 41 | List<ClassEntry> obfClassChain = Lists.newArrayList(); | 39 | List<ClassEntry> obfClassChain = m_index.getObfClassChain(obfClassEntry); |
| 42 | ClassEntry checkClassEntry = obfClassEntry; | ||
| 43 | while (checkClassEntry != null) { | ||
| 44 | obfClassChain.add(checkClassEntry); | ||
| 45 | checkClassEntry = m_index.getOuterClass(checkClassEntry); | ||
| 46 | } | ||
| 47 | |||
| 48 | // change order: outer to inner | ||
| 49 | Collections.reverse(obfClassChain); | ||
| 50 | |||
| 51 | // does this class have any inner classes? | ||
| 52 | Collection<ClassEntry> obfInnerClassEntries = m_index.getInnerClasses(obfClassEntry); | ||
| 53 | 40 | ||
| 54 | boolean isInnerClass = obfClassChain.size() > 1; | 41 | boolean isInnerClass = obfClassChain.size() > 1; |
| 55 | if (isInnerClass) { | 42 | if (isInnerClass) { |
| 56 | 43 | ||
| 57 | // it's an inner class, rename it to the fully qualified name | 44 | // it's an inner class, rename it to the fully qualified name |
| 58 | StringBuilder buf = new StringBuilder(); | 45 | c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); |
| 59 | for (ClassEntry obfChainClassEntry : obfClassChain) { | ||
| 60 | if (buf.length() == 0) { | ||
| 61 | buf.append(obfChainClassEntry.getName()); | ||
| 62 | } else { | ||
| 63 | buf.append("$"); | ||
| 64 | buf.append(obfChainClassEntry.getSimpleName()); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | c.setName(buf.toString()); | ||
| 68 | 46 | ||
| 69 | BehaviorEntry caller = m_index.getAnonymousClassCaller(obfClassEntry); | 47 | BehaviorEntry caller = m_index.getAnonymousClassCaller(obfClassEntry); |
| 70 | if (caller != null) { | 48 | if (caller != null) { |
| @@ -78,6 +56,9 @@ public class InnerClassWriter { | |||
| 78 | } | 56 | } |
| 79 | } | 57 | } |
| 80 | 58 | ||
| 59 | // does this class have any inner classes? | ||
| 60 | Collection<ClassEntry> obfInnerClassEntries = m_index.getInnerClasses(obfClassEntry); | ||
| 61 | |||
| 81 | if (isInnerClass || !obfInnerClassEntries.isEmpty()) { | 62 | if (isInnerClass || !obfInnerClassEntries.isEmpty()) { |
| 82 | 63 | ||
| 83 | // create an inner class attribute | 64 | // create an inner class attribute |
| @@ -86,7 +67,11 @@ public class InnerClassWriter { | |||
| 86 | 67 | ||
| 87 | // write the ancestry, but not the outermost class | 68 | // write the ancestry, but not the outermost class |
| 88 | for (int i=1; i<obfClassChain.size(); i++) { | 69 | for (int i=1; i<obfClassChain.size(); i++) { |
| 89 | writeInnerClass(attr, obfClassChain, obfClassChain.get(i)); | 70 | ClassEntry obfInnerClassEntry = obfClassChain.get(i); |
| 71 | writeInnerClass(attr, obfClassChain, obfInnerClassEntry); | ||
| 72 | |||
| 73 | // update references to use the fully qualified inner class name | ||
| 74 | c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); | ||
| 90 | } | 75 | } |
| 91 | 76 | ||
| 92 | // write the inner classes | 77 | // write the inner classes |
| @@ -96,34 +81,34 @@ public class InnerClassWriter { | |||
| 96 | List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain); | 81 | List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain); |
| 97 | extendedObfClassChain.add(obfInnerClassEntry); | 82 | extendedObfClassChain.add(obfInnerClassEntry); |
| 98 | 83 | ||
| 99 | String fullyQualifiedInnerClassName = writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); | 84 | writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); |
| 100 | 85 | ||
| 101 | // make sure we only reference the fully qualified inner class name | 86 | // update references to use the fully qualified inner class name |
| 102 | c.replaceClassName(obfInnerClassEntry.getName(), fullyQualifiedInnerClassName); | 87 | c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); |
| 103 | } | 88 | } |
| 104 | } | 89 | } |
| 105 | } | 90 | } |
| 106 | 91 | ||
| 107 | private String writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) { | 92 | private void writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) { |
| 108 | 93 | ||
| 109 | // get the new inner class name | 94 | // get the new inner class name |
| 110 | String obfInnerClassName = getFullyQualifiedName(obfClassChain, obfClassEntry); | 95 | ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); |
| 111 | String obfParentClassName = getFullyQualifiedParentName(obfClassChain, obfClassEntry); | 96 | ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); |
| 112 | 97 | ||
| 113 | // here's what the JVM spec says about the InnerClasses attribute | 98 | // here's what the JVM spec says about the InnerClasses attribute |
| 114 | // append(inner, parent, 0 if anonymous else simple name, flags); | 99 | // append(inner, parent, 0 if anonymous else simple name, flags); |
| 115 | 100 | ||
| 116 | // update the attribute with this inner class | 101 | // update the attribute with this inner class |
| 117 | ConstPool constPool = attr.getConstPool(); | 102 | ConstPool constPool = attr.getConstPool(); |
| 118 | int innerClassIndex = constPool.addClassInfo(obfInnerClassName); | 103 | int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); |
| 119 | int parentClassIndex = constPool.addClassInfo(obfParentClassName); | 104 | int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); |
| 120 | int innerClassSimpleNameIndex = 0; | 105 | int innerClassNameIndex = 0; |
| 121 | int accessFlags = 0; | 106 | int accessFlags = 0; |
| 122 | if (!m_index.isAnonymousClass(obfClassEntry)) { | 107 | if (!m_index.isAnonymousClass(obfClassEntry)) { |
| 123 | innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getSimpleName()); | 108 | innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnerClassName()); |
| 124 | } | 109 | } |
| 125 | 110 | ||
| 126 | attr.append(innerClassIndex, parentClassIndex, innerClassSimpleNameIndex, accessFlags); | 111 | attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); |
| 127 | 112 | ||
| 128 | /* DEBUG | 113 | /* DEBUG |
| 129 | System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", | 114 | System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", |
| @@ -135,32 +120,5 @@ public class InnerClassWriter { | |||
| 135 | obfClassEntry.getName() | 120 | obfClassEntry.getName() |
| 136 | )); | 121 | )); |
| 137 | */ | 122 | */ |
| 138 | |||
| 139 | return obfInnerClassName; | ||
| 140 | } | ||
| 141 | |||
| 142 | private String getFullyQualifiedParentName(List<ClassEntry> classChain, ClassEntry classEntry) { | ||
| 143 | assert(classChain.size() > 1); | ||
| 144 | assert(classChain.contains(classEntry)); | ||
| 145 | StringBuilder buf = new StringBuilder(); | ||
| 146 | for (int i=0; classChain.get(i) != classEntry; i++) { | ||
| 147 | ClassEntry chainEntry = classChain.get(i); | ||
| 148 | if (buf.length() == 0) { | ||
| 149 | buf.append(chainEntry.getName()); | ||
| 150 | } else { | ||
| 151 | buf.append("$"); | ||
| 152 | buf.append(chainEntry.getSimpleName()); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | return buf.toString(); | ||
| 156 | } | ||
| 157 | |||
| 158 | private String getFullyQualifiedName(List<ClassEntry> classChain, ClassEntry classEntry) { | ||
| 159 | boolean isInner = classChain.size() > 1; | ||
| 160 | if (isInner) { | ||
| 161 | return getFullyQualifiedParentName(classChain, classEntry) + "$" + classEntry.getSimpleName(); | ||
| 162 | } else { | ||
| 163 | return classEntry.getName(); | ||
| 164 | } | ||
| 165 | } | 123 | } |
| 166 | } | 124 | } |
diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index 69f171a5..69e66bc0 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | package cuchaz.enigma.mapping; | 11 | package cuchaz.enigma.mapping; |
| 12 | 12 | ||
| 13 | import java.io.Serializable; | 13 | import java.io.Serializable; |
| 14 | import java.util.List; | ||
| 14 | 15 | ||
| 15 | public class ClassEntry implements Entry, Serializable { | 16 | public class ClassEntry implements Entry, Serializable { |
| 16 | 17 | ||
| @@ -114,13 +115,28 @@ public class ClassEntry implements Entry, Serializable { | |||
| 114 | } | 115 | } |
| 115 | 116 | ||
| 116 | public String getSimpleName() { | 117 | public String getSimpleName() { |
| 117 | if (isInnerClass()) { | ||
| 118 | return getInnerClassName(); | ||
| 119 | } | ||
| 120 | int pos = m_name.lastIndexOf('/'); | 118 | int pos = m_name.lastIndexOf('/'); |
| 121 | if (pos > 0) { | 119 | if (pos > 0) { |
| 122 | return m_name.substring(pos + 1); | 120 | return m_name.substring(pos + 1); |
| 123 | } | 121 | } |
| 124 | return m_name; | 122 | return m_name; |
| 125 | } | 123 | } |
| 124 | |||
| 125 | public ClassEntry buildClassEntry(List<ClassEntry> classChain) { | ||
| 126 | assert(classChain.contains(this)); | ||
| 127 | StringBuilder buf = new StringBuilder(); | ||
| 128 | for (ClassEntry chainEntry : classChain) { | ||
| 129 | if (buf.length() == 0) { | ||
| 130 | buf.append(chainEntry.getName()); | ||
| 131 | } else { | ||
| 132 | buf.append("$"); | ||
| 133 | buf.append(chainEntry.isInnerClass() ? chainEntry.getInnerClassName() : chainEntry.getSimpleName()); | ||
| 134 | } | ||
| 135 | |||
| 136 | if (chainEntry == this) { | ||
| 137 | break; | ||
| 138 | } | ||
| 139 | } | ||
| 140 | return new ClassEntry(buf.toString()); | ||
| 141 | } | ||
| 126 | } | 142 | } |
diff --git a/src/cuchaz/enigma/mapping/EntryFactory.java b/src/cuchaz/enigma/mapping/EntryFactory.java index bbdfa739..f4d62c8e 100644 --- a/src/cuchaz/enigma/mapping/EntryFactory.java +++ b/src/cuchaz/enigma/mapping/EntryFactory.java | |||
| @@ -1,7 +1,5 @@ | |||
| 1 | package cuchaz.enigma.mapping; | 1 | package cuchaz.enigma.mapping; |
| 2 | 2 | ||
| 3 | import java.util.List; | ||
| 4 | |||
| 5 | import javassist.CtBehavior; | 3 | import javassist.CtBehavior; |
| 6 | import javassist.CtClass; | 4 | import javassist.CtClass; |
| 7 | import javassist.CtConstructor; | 5 | import javassist.CtConstructor; |
| @@ -13,7 +11,6 @@ import javassist.expr.FieldAccess; | |||
| 13 | import javassist.expr.MethodCall; | 11 | import javassist.expr.MethodCall; |
| 14 | import javassist.expr.NewExpr; | 12 | import javassist.expr.NewExpr; |
| 15 | 13 | ||
| 16 | import com.beust.jcommander.internal.Lists; | ||
| 17 | import com.strobel.assembler.metadata.MethodDefinition; | 14 | import com.strobel.assembler.metadata.MethodDefinition; |
| 18 | 15 | ||
| 19 | import cuchaz.enigma.analysis.JarIndex; | 16 | import cuchaz.enigma.analysis.JarIndex; |
| @@ -25,35 +22,8 @@ public class EntryFactory { | |||
| 25 | } | 22 | } |
| 26 | 23 | ||
| 27 | public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { | 24 | public static ClassEntry getObfClassEntry(JarIndex jarIndex, ClassMapping classMapping) { |
| 28 | return getChainedOuterClassName(jarIndex, new ClassEntry(classMapping.getObfFullName())); | 25 | ClassEntry obfClassEntry = new ClassEntry(classMapping.getObfFullName()); |
| 29 | } | 26 | return obfClassEntry.buildClassEntry(jarIndex.getObfClassChain(obfClassEntry)); |
| 30 | |||
| 31 | public static ClassEntry getChainedOuterClassName(JarIndex jarIndex, ClassEntry obfClassEntry) { | ||
| 32 | |||
| 33 | // lookup the chain of outer classes | ||
| 34 | List<ClassEntry> obfClassChain = Lists.newArrayList(obfClassEntry); | ||
| 35 | ClassEntry checkClassEntry = obfClassEntry; | ||
| 36 | while (true) { | ||
| 37 | ClassEntry obfOuterClassEntry = jarIndex.getOuterClass(checkClassEntry); | ||
| 38 | if (obfOuterClassEntry != null) { | ||
| 39 | obfClassChain.add(obfOuterClassEntry); | ||
| 40 | checkClassEntry = obfOuterClassEntry; | ||
| 41 | } else { | ||
| 42 | break; | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | // build the chained class name | ||
| 47 | StringBuilder buf = new StringBuilder(); | ||
| 48 | for (int i=obfClassChain.size()-1; i>=0; i--) { | ||
| 49 | if (buf.length() == 0) { | ||
| 50 | buf.append(obfClassChain.get(i).getName()); | ||
| 51 | } else { | ||
| 52 | buf.append("$"); | ||
| 53 | buf.append(obfClassChain.get(i).getSimpleName()); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | return new ClassEntry(buf.toString()); | ||
| 57 | } | 27 | } |
| 58 | 28 | ||
| 59 | public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { | 29 | public static ClassEntry getDeobfClassEntry(ClassMapping classMapping) { |