diff options
Diffstat (limited to 'src/main/java/cuchaz/enigma/Deobfuscator.java')
| -rw-r--r-- | src/main/java/cuchaz/enigma/Deobfuscator.java | 267 |
1 files changed, 115 insertions, 152 deletions
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java index 076c546..ef452b0 100644 --- a/src/main/java/cuchaz/enigma/Deobfuscator.java +++ b/src/main/java/cuchaz/enigma/Deobfuscator.java | |||
| @@ -11,36 +11,39 @@ | |||
| 11 | 11 | ||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import com.google.common.base.Functions; | ||
| 14 | import com.google.common.base.Stopwatch; | 15 | import com.google.common.base.Stopwatch; |
| 15 | import com.google.common.collect.Lists; | ||
| 16 | import com.strobel.assembler.metadata.ITypeLoader; | 16 | import com.strobel.assembler.metadata.ITypeLoader; |
| 17 | import com.strobel.assembler.metadata.MetadataSystem; | 17 | import com.strobel.assembler.metadata.MetadataSystem; |
| 18 | import com.strobel.assembler.metadata.TypeDefinition; | 18 | import com.strobel.assembler.metadata.TypeDefinition; |
| 19 | import com.strobel.assembler.metadata.TypeReference; | 19 | import com.strobel.assembler.metadata.TypeReference; |
| 20 | import com.strobel.decompiler.DecompilerContext; | ||
| 21 | import com.strobel.decompiler.DecompilerSettings; | 20 | import com.strobel.decompiler.DecompilerSettings; |
| 22 | import com.strobel.decompiler.PlainTextOutput; | ||
| 23 | import com.strobel.decompiler.languages.java.JavaOutputVisitor; | ||
| 24 | import com.strobel.decompiler.languages.java.ast.AstBuilder; | ||
| 25 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; | 21 | import com.strobel.decompiler.languages.java.ast.CompilationUnit; |
| 26 | import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; | 22 | import cuchaz.enigma.analysis.EntryReference; |
| 27 | import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; | 23 | import cuchaz.enigma.analysis.IndexTreeBuilder; |
| 28 | import cuchaz.enigma.analysis.*; | 24 | import cuchaz.enigma.analysis.ParsedJar; |
| 29 | import cuchaz.enigma.analysis.index.JarIndex; | 25 | import cuchaz.enigma.analysis.index.JarIndex; |
| 30 | import cuchaz.enigma.api.EnigmaPlugin; | 26 | import cuchaz.enigma.api.EnigmaPlugin; |
| 27 | import cuchaz.enigma.bytecode.translators.TranslationClassVisitor; | ||
| 28 | import cuchaz.enigma.translation.Translatable; | ||
| 29 | import cuchaz.enigma.translation.Translator; | ||
| 31 | import cuchaz.enigma.translation.mapping.*; | 30 | import cuchaz.enigma.translation.mapping.*; |
| 32 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; | 31 | import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree; |
| 33 | import cuchaz.enigma.translation.mapping.tree.EntryTree; | 32 | import cuchaz.enigma.translation.mapping.tree.EntryTree; |
| 34 | import cuchaz.enigma.translation.representation.ReferencedEntryPool; | ||
| 35 | import cuchaz.enigma.translation.representation.entry.ClassEntry; | 33 | import cuchaz.enigma.translation.representation.entry.ClassEntry; |
| 36 | import cuchaz.enigma.translation.representation.entry.Entry; | 34 | import cuchaz.enigma.translation.representation.entry.Entry; |
| 37 | import cuchaz.enigma.translation.representation.entry.MethodEntry; | 35 | import cuchaz.enigma.translation.representation.entry.MethodEntry; |
| 38 | import cuchaz.enigma.utils.Utils; | 36 | import org.objectweb.asm.ClassVisitor; |
| 39 | import oml.ast.transformers.*; | ||
| 40 | import org.objectweb.asm.ClassWriter; | 37 | import org.objectweb.asm.ClassWriter; |
| 38 | import org.objectweb.asm.Opcodes; | ||
| 41 | import org.objectweb.asm.tree.ClassNode; | 39 | import org.objectweb.asm.tree.ClassNode; |
| 42 | 40 | ||
| 43 | import java.io.*; | 41 | import java.io.File; |
| 42 | import java.io.FileOutputStream; | ||
| 43 | import java.io.IOException; | ||
| 44 | import java.io.Writer; | ||
| 45 | import java.nio.file.Files; | ||
| 46 | import java.nio.file.Path; | ||
| 44 | import java.util.*; | 47 | import java.util.*; |
| 45 | import java.util.concurrent.ConcurrentHashMap; | 48 | import java.util.concurrent.ConcurrentHashMap; |
| 46 | import java.util.concurrent.atomic.AtomicInteger; | 49 | import java.util.concurrent.atomic.AtomicInteger; |
| @@ -53,11 +56,12 @@ import java.util.stream.Collectors; | |||
| 53 | public class Deobfuscator { | 56 | public class Deobfuscator { |
| 54 | 57 | ||
| 55 | private final ServiceLoader<EnigmaPlugin> plugins = ServiceLoader.load(EnigmaPlugin.class); | 58 | private final ServiceLoader<EnigmaPlugin> plugins = ServiceLoader.load(EnigmaPlugin.class); |
| 56 | private final ReferencedEntryPool entryPool = new ReferencedEntryPool(); | ||
| 57 | private final ParsedJar parsedJar; | 59 | private final ParsedJar parsedJar; |
| 58 | private final DecompilerSettings settings; | ||
| 59 | private final JarIndex jarIndex; | 60 | private final JarIndex jarIndex; |
| 60 | private final IndexTreeBuilder indexTreeBuilder; | 61 | private final IndexTreeBuilder indexTreeBuilder; |
| 62 | |||
| 63 | private final SourceProvider obfSourceProvider; | ||
| 64 | |||
| 61 | private EntryRemapper mapper; | 65 | private EntryRemapper mapper; |
| 62 | 66 | ||
| 63 | public Deobfuscator(ParsedJar jar, Consumer<String> listener) { | 67 | public Deobfuscator(ParsedJar jar, Consumer<String> listener) { |
| @@ -75,15 +79,8 @@ public class Deobfuscator { | |||
| 75 | this.indexTreeBuilder = new IndexTreeBuilder(jarIndex); | 79 | this.indexTreeBuilder = new IndexTreeBuilder(jarIndex); |
| 76 | 80 | ||
| 77 | listener.accept("Preparing..."); | 81 | listener.accept("Preparing..."); |
| 78 | // config the decompiler | 82 | |
| 79 | this.settings = DecompilerSettings.javaDefaults(); | 83 | this.obfSourceProvider = new SourceProvider(SourceProvider.createSettings(), new CompiledSourceTypeLoader(parsedJar)); |
| 80 | this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); | ||
| 81 | this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true)); | ||
| 82 | this.settings.setForceExplicitTypeArguments( | ||
| 83 | Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); | ||
| 84 | // DEBUG | ||
| 85 | this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); | ||
| 86 | this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); | ||
| 87 | 84 | ||
| 88 | // init mappings | 85 | // init mappings |
| 89 | mapper = new EntryRemapper(jarIndex); | 86 | mapper = new EntryRemapper(jarIndex); |
| @@ -93,7 +90,7 @@ public class Deobfuscator { | |||
| 93 | this(new ParsedJar(jar), listener); | 90 | this(new ParsedJar(jar), listener); |
| 94 | } | 91 | } |
| 95 | 92 | ||
| 96 | public Deobfuscator(ParsedJar jar) throws IOException { | 93 | public Deobfuscator(ParsedJar jar) { |
| 97 | this(jar, (msg) -> { | 94 | this(jar, (msg) -> { |
| 98 | }); | 95 | }); |
| 99 | } | 96 | } |
| @@ -128,9 +125,9 @@ public class Deobfuscator { | |||
| 128 | Collection<Entry<?>> dropped = dropMappings(mappings); | 125 | Collection<Entry<?>> dropped = dropMappings(mappings); |
| 129 | mapper = new EntryRemapper(jarIndex, mappings); | 126 | mapper = new EntryRemapper(jarIndex, mappings); |
| 130 | 127 | ||
| 131 | DeltaTrackingTree<EntryMapping> deobfToObf = mapper.getDeobfToObf(); | 128 | DeltaTrackingTree<EntryMapping> obfToDeobf = mapper.getObfToDeobf(); |
| 132 | for (Entry<?> entry : dropped) { | 129 | for (Entry<?> entry : dropped) { |
| 133 | deobfToObf.trackDeletion(entry); | 130 | obfToDeobf.trackDeletion(entry); |
| 134 | } | 131 | } |
| 135 | } else { | 132 | } else { |
| 136 | mapper = new EntryRemapper(jarIndex); | 133 | mapper = new EntryRemapper(jarIndex); |
| @@ -161,7 +158,7 @@ public class Deobfuscator { | |||
| 161 | ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry); | 158 | ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry); |
| 162 | if (!deobfClassEntry.equals(obfClassEntry)) { | 159 | if (!deobfClassEntry.equals(obfClassEntry)) { |
| 163 | // if the class has a mapping, clearly it's deobfuscated | 160 | // if the class has a mapping, clearly it's deobfuscated |
| 164 | deobfClasses.add(deobfClassEntry); | 161 | deobfClasses.add(obfClassEntry); |
| 165 | } else if (obfClassEntry.getPackageName() != null) { | 162 | } else if (obfClassEntry.getPackageName() != null) { |
| 166 | // also call it deobufscated if it's not in the none package | 163 | // also call it deobufscated if it's not in the none package |
| 167 | deobfClasses.add(obfClassEntry); | 164 | deobfClasses.add(obfClassEntry); |
| @@ -172,151 +169,126 @@ public class Deobfuscator { | |||
| 172 | } | 169 | } |
| 173 | } | 170 | } |
| 174 | 171 | ||
| 175 | public TranslatingTypeLoader createTypeLoader() { | 172 | public SourceProvider getObfSourceProvider() { |
| 176 | return new TranslatingTypeLoader( | 173 | return obfSourceProvider; |
| 177 | this.parsedJar, | ||
| 178 | this.jarIndex, | ||
| 179 | this.entryPool, | ||
| 180 | this.mapper.getObfuscator(), | ||
| 181 | this.mapper.getDeobfuscator() | ||
| 182 | ); | ||
| 183 | } | ||
| 184 | |||
| 185 | public CompilationUnit getSourceTree(String className) { | ||
| 186 | return getSourceTree(className, createTypeLoader()); | ||
| 187 | } | 174 | } |
| 188 | 175 | ||
| 189 | public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader) { | 176 | public void writeSources(Path outputDirectory, ProgressListener progress) { |
| 190 | return getSourceTree(className, loader, new NoRetryMetadataSystem(loader)); | 177 | // get the classes to decompile |
| 191 | } | 178 | Collection<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses(); |
| 192 | 179 | ||
| 193 | public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader, MetadataSystem metadataSystem) { | 180 | Stopwatch stopwatch = Stopwatch.createStarted(); |
| 194 | 181 | ||
| 195 | // we don't know if this class name is obfuscated or deobfuscated | 182 | try { |
| 196 | // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out | 183 | Translator deobfuscator = mapper.getDeobfuscator(); |
| 197 | // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one | ||
| 198 | 184 | ||
| 199 | String deobfClassName = mapper.deobfuscate(new ClassEntry(className)).getFullName(); | 185 | // deobfuscate everything first |
| 186 | Map<String, ClassNode> translatedNodes = deobfuscateClasses(progress, classEntries, deobfuscator); | ||
| 200 | 187 | ||
| 201 | // set the desc loader | 188 | decompileClasses(outputDirectory, progress, translatedNodes); |
| 202 | this.settings.setTypeLoader(loader); | 189 | } finally { |
| 190 | stopwatch.stop(); | ||
| 203 | 191 | ||
| 204 | // see if procyon can find the desc | 192 | System.out.println("writeSources Done in : " + stopwatch.toString()); |
| 205 | TypeReference type = metadataSystem.lookupType(deobfClassName); | ||
| 206 | if (type == null) { | ||
| 207 | throw new Error(String.format("Unable to find desc: %s (deobf: %s)\nTried class names: %s", | ||
| 208 | className, deobfClassName, loader.getClassNamesToTry(deobfClassName) | ||
| 209 | )); | ||
| 210 | } | 193 | } |
| 211 | TypeDefinition resolvedType = type.resolve(); | ||
| 212 | |||
| 213 | // decompile it! | ||
| 214 | DecompilerContext context = new DecompilerContext(); | ||
| 215 | context.setCurrentType(resolvedType); | ||
| 216 | context.setSettings(this.settings); | ||
| 217 | AstBuilder builder = new AstBuilder(context); | ||
| 218 | builder.addType(resolvedType); | ||
| 219 | builder.runTransformations(null); | ||
| 220 | runCustomTransforms(builder, context); | ||
| 221 | return builder.getCompilationUnit(); | ||
| 222 | } | 194 | } |
| 223 | 195 | ||
| 224 | public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { | 196 | private Map<String, ClassNode> deobfuscateClasses(ProgressListener progress, Collection<ClassEntry> classEntries, Translator translator) { |
| 225 | return getSourceIndex(sourceTree, source, true); | 197 | AtomicInteger count = new AtomicInteger(); |
| 226 | } | 198 | if (progress != null) { |
| 227 | 199 | progress.init(classEntries.size(), "Deobfuscating classes..."); | |
| 228 | public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, boolean ignoreBadTokens) { | ||
| 229 | |||
| 230 | // build the source index | ||
| 231 | SourceIndex index = new SourceIndex(source, ignoreBadTokens); | ||
| 232 | sourceTree.acceptVisitor(new SourceIndexVisitor(entryPool), index); | ||
| 233 | |||
| 234 | EntryResolver resolver = mapper.getDeobfResolver(); | ||
| 235 | |||
| 236 | Collection<Token> tokens = Lists.newArrayList(index.referenceTokens()); | ||
| 237 | |||
| 238 | // resolve all the classes in the source references | ||
| 239 | for (Token token : tokens) { | ||
| 240 | EntryReference<Entry<?>, Entry<?>> deobfReference = index.getDeobfReference(token); | ||
| 241 | index.replaceDeobfReference(token, resolver.resolveFirstReference(deobfReference, ResolutionStrategy.RESOLVE_CLOSEST)); | ||
| 242 | } | 200 | } |
| 243 | 201 | ||
| 244 | return index; | 202 | return classEntries.parallelStream() |
| 245 | } | 203 | .map(entry -> { |
| 204 | ClassEntry translatedEntry = translator.translate(entry); | ||
| 205 | if (progress != null) { | ||
| 206 | progress.step(count.getAndIncrement(), translatedEntry.toString()); | ||
| 207 | } | ||
| 208 | |||
| 209 | ClassNode node = parsedJar.getClassNode(entry.getFullName()); | ||
| 210 | if (node != null) { | ||
| 211 | ClassNode translatedNode = new ClassNode(); | ||
| 212 | node.accept(new TranslationClassVisitor(translator, Opcodes.ASM5, translatedNode)); | ||
| 213 | return translatedNode; | ||
| 214 | } | ||
| 246 | 215 | ||
| 247 | public String getSource(CompilationUnit sourceTree) { | 216 | return null; |
| 248 | // render the AST into source | 217 | }) |
| 249 | StringWriter buf = new StringWriter(); | 218 | .filter(Objects::nonNull) |
| 250 | sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); | 219 | .collect(Collectors.toMap(n -> n.name, Functions.identity())); |
| 251 | sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), this.settings), null); | ||
| 252 | return buf.toString(); | ||
| 253 | } | 220 | } |
| 254 | 221 | ||
| 255 | public void writeSources(File dirOut, ProgressListener progress) { | 222 | private void decompileClasses(Path outputDirectory, ProgressListener progress, Map<String, ClassNode> translatedClasses) { |
| 256 | // get the classes to decompile | 223 | Collection<ClassNode> decompileClasses = translatedClasses.values().stream() |
| 257 | Set<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses().stream() | 224 | .filter(classNode -> classNode.name.indexOf('$') == -1) |
| 258 | .filter(classEntry -> !classEntry.isInnerClass()) | 225 | .collect(Collectors.toList()); |
| 259 | .collect(Collectors.toSet()); | ||
| 260 | 226 | ||
| 261 | if (progress != null) { | 227 | if (progress != null) { |
| 262 | progress.init(classEntries.size(), "Decompiling classes..."); | 228 | progress.init(decompileClasses.size(), "Decompiling classes..."); |
| 263 | } | 229 | } |
| 264 | 230 | ||
| 265 | //create a common instance outside the loop as mappings shouldn't be changing while this is happening | 231 | //create a common instance outside the loop as mappings shouldn't be changing while this is happening |
| 266 | //synchronized to make sure the parallelStream doesn't CME with the cache | 232 | //synchronized to make sure the parallelStream doesn't CME with the cache |
| 267 | ITranslatingTypeLoader typeLoader = new SynchronizedTypeLoader(createTypeLoader()); | 233 | ITypeLoader typeLoader = new SynchronizedTypeLoader(new CompiledSourceTypeLoader(translatedClasses::get)); |
| 268 | 234 | ||
| 269 | MetadataSystem metadataSystem = new NoRetryMetadataSystem(typeLoader); | 235 | MetadataSystem metadataSystem = new Deobfuscator.NoRetryMetadataSystem(typeLoader); |
| 270 | metadataSystem.setEagerMethodLoadingEnabled(true);//ensures methods are loaded on classload and prevents race conditions | 236 | |
| 237 | //ensures methods are loaded on classload and prevents race conditions | ||
| 238 | metadataSystem.setEagerMethodLoadingEnabled(true); | ||
| 239 | |||
| 240 | DecompilerSettings settings = SourceProvider.createSettings(); | ||
| 241 | SourceProvider sourceProvider = new SourceProvider(settings, typeLoader, metadataSystem); | ||
| 271 | 242 | ||
| 272 | // DEOBFUSCATE ALL THE THINGS!! @_@ | ||
| 273 | Stopwatch stopwatch = Stopwatch.createStarted(); | ||
| 274 | AtomicInteger count = new AtomicInteger(); | 243 | AtomicInteger count = new AtomicInteger(); |
| 275 | classEntries.parallelStream().forEach(obfClassEntry -> { | 244 | |
| 276 | ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry); | 245 | decompileClasses.parallelStream().forEach(translatedNode -> { |
| 277 | if (progress != null) { | 246 | if (progress != null) { |
| 278 | progress.step(count.getAndIncrement(), deobfClassEntry.toString()); | 247 | progress.step(count.getAndIncrement(), translatedNode.name); |
| 279 | } | 248 | } |
| 280 | 249 | ||
| 281 | try { | 250 | decompileClass(outputDirectory, translatedNode, sourceProvider); |
| 282 | // get the source | 251 | }); |
| 283 | CompilationUnit sourceTree = getSourceTree(obfClassEntry.getName(), typeLoader, metadataSystem); | 252 | } |
| 284 | 253 | ||
| 285 | // write the file | 254 | private void decompileClass(Path outputDirectory, ClassNode translatedNode, SourceProvider sourceProvider) { |
| 286 | File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); | 255 | try { |
| 287 | file.getParentFile().mkdirs(); | 256 | // get the source |
| 288 | try (Writer writer = new BufferedWriter(new FileWriter(file))) { | 257 | CompilationUnit sourceTree = sourceProvider.getSources(translatedNode.name); |
| 289 | sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null); | 258 | |
| 290 | sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(writer), settings), null); | 259 | Path path = outputDirectory.resolve(translatedNode.name.replace('.', '/') + ".java"); |
| 291 | } | 260 | Files.createDirectories(path.getParent()); |
| 292 | } catch (Throwable t) { | 261 | |
| 293 | // don't crash the whole world here, just log the error and keep going | 262 | try (Writer writer = Files.newBufferedWriter(path)) { |
| 294 | // TODO: set up logback via log4j | 263 | sourceProvider.writeSource(writer, sourceTree); |
| 295 | System.err.println("Unable to decompile class " + deobfClassEntry + " (" + obfClassEntry + ")"); | ||
| 296 | t.printStackTrace(System.err); | ||
| 297 | } | 264 | } |
| 298 | }); | 265 | } catch (Throwable t) { |
| 299 | stopwatch.stop(); | 266 | // don't crash the whole world here, just log the error and keep going |
| 300 | System.out.println("writeSources Done in : " + stopwatch.toString()); | 267 | // TODO: set up logback via log4j |
| 301 | if (progress != null) { | 268 | System.err.println("Unable to decompile class " + translatedNode.name); |
| 302 | progress.step(count.get(), "Done:"); | 269 | t.printStackTrace(System.err); |
| 303 | } | 270 | } |
| 304 | } | 271 | } |
| 305 | 272 | ||
| 306 | public void writeJar(File out, ProgressListener progress) { | 273 | public void writeTransformedJar(File out, ProgressListener progress) { |
| 307 | transformJar(out, progress, createTypeLoader()::transformInto); | 274 | Translator deobfuscator = mapper.getDeobfuscator(); |
| 275 | writeTransformedJar(out, progress, (node, visitor) -> { | ||
| 276 | ClassEntry entry = new ClassEntry(node.name); | ||
| 277 | node.accept(new TranslationClassVisitor(deobfuscator, Opcodes.ASM5, visitor)); | ||
| 278 | return deobfuscator.translate(entry).getFullName(); | ||
| 279 | }); | ||
| 308 | } | 280 | } |
| 309 | 281 | ||
| 310 | public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { | 282 | public void writeTransformedJar(File out, ProgressListener progress, ClassTransformer transformer) { |
| 311 | try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { | 283 | try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { |
| 312 | if (progress != null) { | 284 | if (progress != null) { |
| 313 | progress.init(parsedJar.getClassCount(), "Transforming classes..."); | 285 | progress.init(parsedJar.getClassCount(), "Transforming classes..."); |
| 314 | } | 286 | } |
| 315 | 287 | ||
| 316 | AtomicInteger i = new AtomicInteger(); | 288 | AtomicInteger count = new AtomicInteger(); |
| 317 | parsedJar.visitNode(node -> { | 289 | parsedJar.visitNode(node -> { |
| 318 | if (progress != null) { | 290 | if (progress != null) { |
| 319 | progress.step(i.getAndIncrement(), node.name); | 291 | progress.step(count.getAndIncrement(), node.name); |
| 320 | } | 292 | } |
| 321 | 293 | ||
| 322 | try { | 294 | try { |
| @@ -329,10 +301,6 @@ public class Deobfuscator { | |||
| 329 | throw new Error("Unable to transform class " + node.name, t); | 301 | throw new Error("Unable to transform class " + node.name, t); |
| 330 | } | 302 | } |
| 331 | }); | 303 | }); |
| 332 | |||
| 333 | if (progress != null) { | ||
| 334 | progress.step(i.get(), "Done!"); | ||
| 335 | } | ||
| 336 | } catch (IOException ex) { | 304 | } catch (IOException ex) { |
| 337 | throw new Error("Unable to write to Jar file!"); | 305 | throw new Error("Unable to write to Jar file!"); |
| 338 | } | 306 | } |
| @@ -355,7 +323,7 @@ public class Deobfuscator { | |||
| 355 | } | 323 | } |
| 356 | } | 324 | } |
| 357 | 325 | ||
| 358 | public boolean isObfuscatedIdentifier(Entry<?> obfEntry) { | 326 | public boolean isRenamable(Entry<?> obfEntry) { |
| 359 | if (obfEntry instanceof MethodEntry) { | 327 | if (obfEntry instanceof MethodEntry) { |
| 360 | // HACKHACK: Object methods are not obfuscated identifiers | 328 | // HACKHACK: Object methods are not obfuscated identifiers |
| 361 | MethodEntry obfMethodEntry = (MethodEntry) obfEntry; | 329 | MethodEntry obfMethodEntry = (MethodEntry) obfEntry; |
| @@ -389,12 +357,15 @@ public class Deobfuscator { | |||
| 389 | return this.jarIndex.getEntryIndex().hasEntry(obfEntry); | 357 | return this.jarIndex.getEntryIndex().hasEntry(obfEntry); |
| 390 | } | 358 | } |
| 391 | 359 | ||
| 392 | public boolean isRenameable(EntryReference<Entry<?>, Entry<?>> obfReference) { | 360 | public boolean isRenamable(EntryReference<Entry<?>, Entry<?>> obfReference) { |
| 393 | return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry()); | 361 | return obfReference.isNamed() && isRenamable(obfReference.getNameableEntry()); |
| 394 | } | 362 | } |
| 395 | 363 | ||
| 396 | public boolean hasDeobfuscatedName(Entry<?> obfEntry) { | 364 | public boolean isRemapped(Entry<?> entry) { |
| 397 | return mapper.hasDeobfMapping(obfEntry); | 365 | EntryResolver resolver = mapper.getObfResolver(); |
| 366 | DeltaTrackingTree<EntryMapping> mappings = mapper.getObfToDeobf(); | ||
| 367 | return resolver.resolveEntry(entry, ResolutionStrategy.RESOLVE_ROOT).stream() | ||
| 368 | .anyMatch(mappings::contains); | ||
| 398 | } | 369 | } |
| 399 | 370 | ||
| 400 | public void rename(Entry<?> obfEntry, String newName) { | 371 | public void rename(Entry<?> obfEntry, String newName) { |
| @@ -409,21 +380,12 @@ public class Deobfuscator { | |||
| 409 | mapper.mapFromObf(obfEntry, new EntryMapping(mapper.deobfuscate(obfEntry).getName())); | 380 | mapper.mapFromObf(obfEntry, new EntryMapping(mapper.deobfuscate(obfEntry).getName())); |
| 410 | } | 381 | } |
| 411 | 382 | ||
| 412 | public static void runCustomTransforms(AstBuilder builder, DecompilerContext context) { | 383 | public <T extends Translatable> T deobfuscate(T translatable) { |
| 413 | List<IAstTransform> transformers = Arrays.asList( | 384 | return mapper.deobfuscate(translatable); |
| 414 | new ObfuscatedEnumSwitchRewriterTransform(context), | ||
| 415 | new VarargsFixer(context), | ||
| 416 | new RemoveObjectCasts(context), | ||
| 417 | new Java8Generics(), | ||
| 418 | new InvalidIdentifierFix() | ||
| 419 | ); | ||
| 420 | for (IAstTransform transform : transformers) { | ||
| 421 | transform.run(builder.getCompilationUnit()); | ||
| 422 | } | ||
| 423 | } | 385 | } |
| 424 | 386 | ||
| 425 | public interface ClassTransformer { | 387 | public interface ClassTransformer { |
| 426 | String transform(ClassNode node, ClassWriter writer); | 388 | String transform(ClassNode node, ClassVisitor visitor); |
| 427 | } | 389 | } |
| 428 | 390 | ||
| 429 | public static class NoRetryMetadataSystem extends MetadataSystem { | 391 | public static class NoRetryMetadataSystem extends MetadataSystem { |
| @@ -448,6 +410,7 @@ public class Deobfuscator { | |||
| 448 | return result; | 410 | return result; |
| 449 | } | 411 | } |
| 450 | 412 | ||
| 413 | @Override | ||
| 451 | public synchronized TypeDefinition resolve(final TypeReference type) { | 414 | public synchronized TypeDefinition resolve(final TypeReference type) { |
| 452 | return super.resolve(type); | 415 | return super.resolve(type); |
| 453 | } | 416 | } |