summaryrefslogtreecommitdiff
path: root/src/main/java/cuchaz/enigma/Deobfuscator.java
diff options
context:
space:
mode:
authorGravatar Gegy2019-01-24 14:48:32 +0200
committerGravatar Adrian Siekierka2019-01-24 13:48:32 +0100
commit00fcd0550fcdda621c2e4662f6ddd55ce673b931 (patch)
tree6f9e4c24dbcc6d118fceec56adf7bf9d747a485c /src/main/java/cuchaz/enigma/Deobfuscator.java
parentmark as 0.13.0-SNAPSHOT for preliminary development (diff)
downloadenigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.tar.gz
enigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.tar.xz
enigma-fork-00fcd0550fcdda621c2e4662f6ddd55ce673b931.zip
[WIP] Mapping rework (#91)
* Move packages * Mapping & entry refactor: first pass * Fix deobf -> obf tree remapping * Resolve various issues * Give all entries the potential for parents and treat inner classes as children * Deobf UI tree elements * Tests pass * Sort mapping output * Fix delta tracking * Index separation and first pass for #97 * Keep track of remapped jar index * Fix child entries not being remapped * Drop non-root entries * Track dropped mappings * Fix enigma mapping ordering * EntryTreeNode interface * Small tweaks * Naive full index remap on rename * Entries can resolve to more than one root entry * Support alternative resolution strategies * Bridge method resolution * Tests pass * Fix mappings being used where there are none * Fix methods with different descriptors being considered unique. closes #89
Diffstat (limited to 'src/main/java/cuchaz/enigma/Deobfuscator.java')
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java483
1 files changed, 96 insertions, 387 deletions
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index 4a945cd..076c546 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -13,8 +13,6 @@ package cuchaz.enigma;
13 13
14import com.google.common.base.Stopwatch; 14import com.google.common.base.Stopwatch;
15import com.google.common.collect.Lists; 15import com.google.common.collect.Lists;
16import com.google.common.collect.Maps;
17import com.google.common.collect.Sets;
18import com.strobel.assembler.metadata.ITypeLoader; 16import com.strobel.assembler.metadata.ITypeLoader;
19import com.strobel.assembler.metadata.MetadataSystem; 17import com.strobel.assembler.metadata.MetadataSystem;
20import com.strobel.assembler.metadata.TypeDefinition; 18import com.strobel.assembler.metadata.TypeDefinition;
@@ -28,16 +26,17 @@ import com.strobel.decompiler.languages.java.ast.CompilationUnit;
28import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor; 26import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor;
29import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; 27import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform;
30import cuchaz.enigma.analysis.*; 28import cuchaz.enigma.analysis.*;
29import cuchaz.enigma.analysis.index.JarIndex;
31import cuchaz.enigma.api.EnigmaPlugin; 30import cuchaz.enigma.api.EnigmaPlugin;
32import cuchaz.enigma.mapping.*; 31import cuchaz.enigma.translation.mapping.*;
33import cuchaz.enigma.mapping.entry.*; 32import cuchaz.enigma.translation.mapping.tree.DeltaTrackingTree;
34import cuchaz.enigma.throwables.IllegalNameException; 33import cuchaz.enigma.translation.mapping.tree.EntryTree;
34import cuchaz.enigma.translation.representation.ReferencedEntryPool;
35import cuchaz.enigma.translation.representation.entry.ClassEntry;
36import cuchaz.enigma.translation.representation.entry.Entry;
37import cuchaz.enigma.translation.representation.entry.MethodEntry;
35import cuchaz.enigma.utils.Utils; 38import cuchaz.enigma.utils.Utils;
36import oml.ast.transformers.InvalidIdentifierFix; 39import oml.ast.transformers.*;
37import oml.ast.transformers.Java8Generics;
38import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform;
39import oml.ast.transformers.RemoveObjectCasts;
40import oml.ast.transformers.VarargsFixer;
41import org.objectweb.asm.ClassWriter; 40import org.objectweb.asm.ClassWriter;
42import org.objectweb.asm.tree.ClassNode; 41import org.objectweb.asm.tree.ClassNode;
43 42
@@ -49,6 +48,7 @@ import java.util.function.Consumer;
49import java.util.jar.JarEntry; 48import java.util.jar.JarEntry;
50import java.util.jar.JarFile; 49import java.util.jar.JarFile;
51import java.util.jar.JarOutputStream; 50import java.util.jar.JarOutputStream;
51import java.util.stream.Collectors;
52 52
53public class Deobfuscator { 53public class Deobfuscator {
54 54
@@ -57,24 +57,24 @@ public class Deobfuscator {
57 private final ParsedJar parsedJar; 57 private final ParsedJar parsedJar;
58 private final DecompilerSettings settings; 58 private final DecompilerSettings settings;
59 private final JarIndex jarIndex; 59 private final JarIndex jarIndex;
60 private final MappingsRenamer renamer; 60 private final IndexTreeBuilder indexTreeBuilder;
61 private final Map<TranslationDirection, Translator> translatorCache; 61 private EntryRemapper mapper;
62 private Mappings mappings;
63 62
64 public Deobfuscator(ParsedJar jar, Consumer<String> listener) { 63 public Deobfuscator(ParsedJar jar, Consumer<String> listener) {
65 this.parsedJar = jar; 64 this.parsedJar = jar;
66 65
67 // build the jar index 66 // build the jar index
68 listener.accept("Indexing JAR..."); 67 this.jarIndex = JarIndex.empty();
69 this.jarIndex = new JarIndex(entryPool); 68 this.jarIndex.indexJar(this.parsedJar, listener);
70 this.jarIndex.indexJar(this.parsedJar, true);
71 69
72 listener.accept("Initializing plugins..."); 70 listener.accept("Initializing plugins...");
73 for (EnigmaPlugin plugin : getPlugins()) { 71 for (EnigmaPlugin plugin : getPlugins()) {
74 plugin.onClassesLoaded(parsedJar.getClassDataMap(), parsedJar::getClassNode); 72 plugin.onClassesLoaded(parsedJar.getClassDataMap(), parsedJar::getClassNode);
75 } 73 }
76 74
77 listener.accept("Preparing..."); 75 this.indexTreeBuilder = new IndexTreeBuilder(jarIndex);
76
77 listener.accept("Preparing...");
78 // config the decompiler 78 // config the decompiler
79 this.settings = DecompilerSettings.javaDefaults(); 79 this.settings = DecompilerSettings.javaDefaults();
80 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); 80 this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true));
@@ -85,11 +85,8 @@ public class Deobfuscator {
85 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); 85 this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false));
86 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); 86 this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false));
87 87
88 // init defaults
89 this.translatorCache = Maps.newTreeMap();
90 this.renamer = new MappingsRenamer(this.jarIndex, null, this.entryPool);
91 // init mappings 88 // init mappings
92 setMappings(new Mappings()); 89 mapper = new EntryRemapper(jarIndex);
93 } 90 }
94 91
95 public Deobfuscator(JarFile jar, Consumer<String> listener) throws IOException { 92 public Deobfuscator(JarFile jar, Consumer<String> listener) throws IOException {
@@ -97,12 +94,14 @@ public class Deobfuscator {
97 } 94 }
98 95
99 public Deobfuscator(ParsedJar jar) throws IOException { 96 public Deobfuscator(ParsedJar jar) throws IOException {
100 this(jar, (msg) -> {}); 97 this(jar, (msg) -> {
101 } 98 });
99 }
102 100
103 public Deobfuscator(JarFile jar) throws IOException { 101 public Deobfuscator(JarFile jar) throws IOException {
104 this(jar, (msg) -> {}); 102 this(jar, (msg) -> {
105 } 103 });
104 }
106 105
107 public ServiceLoader<EnigmaPlugin> getPlugins() { 106 public ServiceLoader<EnigmaPlugin> getPlugins() {
108 return plugins; 107 return plugins;
@@ -116,56 +115,50 @@ public class Deobfuscator {
116 return this.jarIndex; 115 return this.jarIndex;
117 } 116 }
118 117
119 public Mappings getMappings() { 118 public IndexTreeBuilder getIndexTreeBuilder() {
120 return this.mappings; 119 return indexTreeBuilder;
121 } 120 }
122 121
123 public void setMappings(Mappings val) { 122 public EntryRemapper getMapper() {
124 setMappings(val, true); 123 return this.mapper;
125 } 124 }
126 125
127 public void setMappings(Mappings val, boolean warnAboutDrops) { 126 public void setMappings(EntryTree<EntryMapping> mappings) {
128 if (val == null) { 127 if (mappings != null) {
129 val = new Mappings(); 128 Collection<Entry<?>> dropped = dropMappings(mappings);
130 } 129 mapper = new EntryRemapper(jarIndex, mappings);
131 130
132 // drop mappings that don't match the jar 131 DeltaTrackingTree<EntryMapping> deobfToObf = mapper.getDeobfToObf();
133 MappingsChecker checker = new MappingsChecker(this.jarIndex); 132 for (Entry<?> entry : dropped) {
134 checker.dropBrokenMappings(val); 133 deobfToObf.trackDeletion(entry);
135 if (warnAboutDrops) {
136 for (Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) {
137 System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
138 }
139 for (Map.Entry<ClassEntry, ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) {
140 System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
141 }
142 for (Map.Entry<FieldEntry, FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) {
143 System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
144 }
145 for (Map.Entry<MethodEntry, MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) {
146 System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
147 } 134 }
135 } else {
136 mapper = new EntryRemapper(jarIndex);
148 } 137 }
149
150 this.mappings = val;
151 this.renamer.setMappings(mappings);
152 this.translatorCache.clear();
153 } 138 }
154 139
155 public Translator getTranslator(TranslationDirection direction) { 140 private Collection<Entry<?>> dropMappings(EntryTree<EntryMapping> mappings) {
156 return this.translatorCache.computeIfAbsent(direction, 141 // drop mappings that don't match the jar
157 k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex())); 142 MappingsChecker checker = new MappingsChecker(jarIndex, mappings);
143 MappingsChecker.Dropped dropped = checker.dropBrokenMappings();
144
145 Map<Entry<?>, String> droppedMappings = dropped.getDroppedMappings();
146 for (Map.Entry<Entry<?>, String> mapping : droppedMappings.entrySet()) {
147 System.out.println("WARNING: Couldn't find " + mapping.getKey() + " (" + mapping.getValue() + ") in jar. Mapping was dropped.");
148 }
149
150 return droppedMappings.keySet();
158 } 151 }
159 152
160 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) { 153 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) {
161 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { 154 for (ClassEntry obfClassEntry : this.jarIndex.getEntryIndex().getClasses()) {
162 // skip inner classes 155 // skip inner classes
163 if (obfClassEntry.isInnerClass()) { 156 if (obfClassEntry.isInnerClass()) {
164 continue; 157 continue;
165 } 158 }
166 159
167 // separate the classes 160 // separate the classes
168 ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry); 161 ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry);
169 if (!deobfClassEntry.equals(obfClassEntry)) { 162 if (!deobfClassEntry.equals(obfClassEntry)) {
170 // if the class has a mapping, clearly it's deobfuscated 163 // if the class has a mapping, clearly it's deobfuscated
171 deobfClasses.add(deobfClassEntry); 164 deobfClasses.add(deobfClassEntry);
@@ -184,8 +177,8 @@ public class Deobfuscator {
184 this.parsedJar, 177 this.parsedJar,
185 this.jarIndex, 178 this.jarIndex,
186 this.entryPool, 179 this.entryPool,
187 getTranslator(TranslationDirection.OBFUSCATING), 180 this.mapper.getObfuscator(),
188 getTranslator(TranslationDirection.DEOBFUSCATING) 181 this.mapper.getDeobfuscator()
189 ); 182 );
190 } 183 }
191 184
@@ -203,14 +196,7 @@ public class Deobfuscator {
203 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out 196 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out
204 // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one 197 // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one
205 198
206 // first, assume class name is deobf 199 String deobfClassName = mapper.deobfuscate(new ClassEntry(className)).getFullName();
207 String deobfClassName = className;
208
209 // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name
210 ClassMapping classMapping = this.mappings.getClassByObf(className);
211 if (classMapping != null && classMapping.getDeobfName() != null) {
212 deobfClassName = classMapping.getDeobfName();
213 }
214 200
215 // set the desc loader 201 // set the desc loader
216 this.settings.setTypeLoader(loader); 202 this.settings.setTypeLoader(loader);
@@ -236,43 +222,23 @@ public class Deobfuscator {
236 } 222 }
237 223
238 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) { 224 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) {
239 return getSourceIndex(sourceTree, source, null); 225 return getSourceIndex(sourceTree, source, true);
240 } 226 }
241 227
242 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) { 228 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, boolean ignoreBadTokens) {
243 229
244 // build the source index 230 // build the source index
245 SourceIndex index; 231 SourceIndex index = new SourceIndex(source, ignoreBadTokens);
246 if (ignoreBadTokens != null) {
247 index = new SourceIndex(source, ignoreBadTokens);
248 } else {
249 index = new SourceIndex(source);
250 }
251 sourceTree.acceptVisitor(new SourceIndexVisitor(entryPool), index); 232 sourceTree.acceptVisitor(new SourceIndexVisitor(entryPool), index);
252 233
253 // DEBUG 234 EntryResolver resolver = mapper.getDeobfResolver();
254 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null );
255
256 // resolve all the classes in the source references
257 for (Token token : index.referenceTokens()) {
258 EntryReference<Entry, Entry> deobfReference = index.getDeobfReference(token);
259
260 // get the obfuscated entry
261 Entry obfEntry = obfuscateEntry(deobfReference.entry);
262 235
263 // try to resolve the class 236 Collection<Token> tokens = Lists.newArrayList(index.referenceTokens());
264 ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryOwner(obfEntry);
265 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getOwnerClassEntry())) {
266 // change the class of the entry
267 obfEntry = obfEntry.updateOwnership(resolvedObfClassEntry);
268
269 // save the new deobfuscated reference
270 deobfReference.entry = deobfuscateEntry(obfEntry);
271 index.replaceDeobfReference(token, deobfReference);
272 }
273 237
274 // DEBUG 238 // resolve all the classes in the source references
275 // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) ); 239 for (Token token : tokens) {
240 EntryReference<Entry<?>, Entry<?>> deobfReference = index.getDeobfReference(token);
241 index.replaceDeobfReference(token, resolver.resolveFirstReference(deobfReference, ResolutionStrategy.RESOLVE_CLOSEST));
276 } 242 }
277 243
278 return index; 244 return index;
@@ -288,15 +254,9 @@ public class Deobfuscator {
288 254
289 public void writeSources(File dirOut, ProgressListener progress) { 255 public void writeSources(File dirOut, ProgressListener progress) {
290 // get the classes to decompile 256 // get the classes to decompile
291 Set<ClassEntry> classEntries = Sets.newHashSet(); 257 Set<ClassEntry> classEntries = jarIndex.getEntryIndex().getClasses().stream()
292 for (ClassEntry obfClassEntry : this.jarIndex.getObfClassEntries()) { 258 .filter(classEntry -> !classEntry.isInnerClass())
293 // skip inner classes 259 .collect(Collectors.toSet());
294 if (obfClassEntry.isInnerClass()) {
295 continue;
296 }
297
298 classEntries.add(obfClassEntry);
299 }
300 260
301 if (progress != null) { 261 if (progress != null) {
302 progress.init(classEntries.size(), "Decompiling classes..."); 262 progress.init(classEntries.size(), "Decompiling classes...");
@@ -313,9 +273,9 @@ public class Deobfuscator {
313 Stopwatch stopwatch = Stopwatch.createStarted(); 273 Stopwatch stopwatch = Stopwatch.createStarted();
314 AtomicInteger count = new AtomicInteger(); 274 AtomicInteger count = new AtomicInteger();
315 classEntries.parallelStream().forEach(obfClassEntry -> { 275 classEntries.parallelStream().forEach(obfClassEntry -> {
316 ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry)); 276 ClassEntry deobfClassEntry = mapper.deobfuscate(obfClassEntry);
317 if (progress != null) { 277 if (progress != null) {
318 progress.onProgress(count.getAndIncrement(), deobfClassEntry.toString()); 278 progress.step(count.getAndIncrement(), deobfClassEntry.toString());
319 } 279 }
320 280
321 try { 281 try {
@@ -332,131 +292,17 @@ public class Deobfuscator {
332 } catch (Throwable t) { 292 } catch (Throwable t) {
333 // don't crash the whole world here, just log the error and keep going 293 // don't crash the whole world here, just log the error and keep going
334 // TODO: set up logback via log4j 294 // TODO: set up logback via log4j
335 System.err.println("Unable to deobfuscate class " + deobfClassEntry + " (" + obfClassEntry + ")"); 295 System.err.println("Unable to decompile class " + deobfClassEntry + " (" + obfClassEntry + ")");
336 t.printStackTrace(System.err); 296 t.printStackTrace(System.err);
337 } 297 }
338 }); 298 });
339 stopwatch.stop(); 299 stopwatch.stop();
340 System.out.println("writeSources Done in : " + stopwatch.toString()); 300 System.out.println("writeSources Done in : " + stopwatch.toString());
341 if (progress != null) { 301 if (progress != null) {
342 progress.onProgress(count.get(), "Done:"); 302 progress.step(count.get(), "Done:");
343 }
344 }
345
346 private void addAllPotentialAncestors(Set<ClassEntry> classEntries, ClassEntry classObfEntry) {
347 for (ClassEntry interfaceEntry : jarIndex.getTranslationIndex().getInterfaces(classObfEntry)) {
348 if (classEntries.add(interfaceEntry)) {
349 addAllPotentialAncestors(classEntries, interfaceEntry);
350 }
351 }
352
353 ClassEntry superClassEntry = jarIndex.getTranslationIndex().getSuperclass(classObfEntry);
354 if (superClassEntry != null && classEntries.add(superClassEntry)) {
355 addAllPotentialAncestors(classEntries, superClassEntry);
356 }
357 }
358
359 public boolean isMethodProvider(MethodEntry methodEntry) {
360 Set<ClassEntry> classEntries = new HashSet<>();
361 addAllPotentialAncestors(classEntries, methodEntry.getOwnerClassEntry());
362
363 for (ClassEntry parentEntry : classEntries) {
364 MethodEntry ancestorMethodEntry = entryPool.getMethod(parentEntry, methodEntry.getName(), methodEntry.getDesc());
365 if (jarIndex.containsObfMethod(ancestorMethodEntry)) {
366 return false;
367 }
368 }
369
370 return true;
371 }
372
373 @Deprecated
374 public boolean isMethodProvider(ClassEntry classObfEntry, MethodEntry methodEntry) {
375 Set<ClassEntry> classEntries = new HashSet<>();
376 addAllPotentialAncestors(classEntries, classObfEntry);
377
378 for (ClassEntry parentEntry : classEntries) {
379 MethodEntry ancestorMethodEntry = entryPool.getMethod(parentEntry, methodEntry.getName(), methodEntry.getDesc());
380 if (jarIndex.containsObfMethod(ancestorMethodEntry)) {
381 return false;
382 }
383 }
384
385 return true;
386 }
387
388 public void rebuildMethodNames(ProgressListener progress) {
389 final AtomicInteger i = new AtomicInteger();
390 Map<ClassMapping, Map<Entry, String>> renameClassMap = new ConcurrentHashMap<>();
391
392 progress.init(getMappings().classes().size() * 3, "Rebuilding method names");
393
394 Lists.newArrayList(getMappings().classes()).parallelStream().forEach(classMapping -> {
395 progress.onProgress(i.getAndIncrement(), classMapping.getDeobfName());
396 rebuildMethodNames(classMapping, renameClassMap);
397 });
398
399
400 renameClassMap.entrySet().stream().forEach(renameClassMapEntry -> {
401 progress.onProgress(i.getAndIncrement(), renameClassMapEntry.getKey().getDeobfName());
402 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) {
403 Entry obfEntry = entry.getKey();
404
405 removeMapping(obfEntry, false);
406 }
407 });
408
409 translatorCache.clear();
410
411 renameClassMap.entrySet().stream().forEach(renameClassMapEntry -> {
412 progress.onProgress(i.getAndIncrement(), renameClassMapEntry.getKey().getDeobfName());
413
414 for (Map.Entry<Entry, String> entry : renameClassMapEntry.getValue().entrySet()) {
415 Entry obfEntry = entry.getKey();
416 String name = entry.getValue();
417
418 if (name != null) {
419 try {
420 rename(obfEntry, name);
421 } catch (IllegalNameException exception) {
422 System.out.println("WARNING: " + exception.getMessage());
423 }
424 }
425 }
426 });
427 }
428
429 private void rebuildMethodNames(ClassMapping classMapping, Map<ClassMapping, Map<Entry, String>> renameClassMap) {
430 Map<Entry, String> renameEntries = new HashMap<>();
431
432 for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) {
433 ClassEntry classObfEntry = classMapping.getObfEntry();
434 MethodEntry obfEntry = methodMapping.getObfEntry(classObfEntry);
435 boolean isProvider = isMethodProvider(obfEntry);
436
437 if (hasDeobfuscatedName(obfEntry)
438 && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) {
439 renameEntries.put(obfEntry, isProvider ? methodMapping.getDeobfName() : null);
440 }
441
442 if (isProvider) {
443 for (LocalVariableMapping localVariableMapping : methodMapping.arguments()) {
444 Entry argObfEntry = localVariableMapping.getObfEntry(obfEntry);
445 if (hasDeobfuscatedName(argObfEntry)) {
446 renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName());
447 }
448 }
449 }
450 }
451
452 classMapping.markDirty();
453 renameClassMap.put(classMapping, renameEntries);
454 for (ClassMapping innerClass : classMapping.innerClasses()) {
455 rebuildMethodNames(innerClass, renameClassMap);
456 } 303 }
457 } 304 }
458 305
459
460 public void writeJar(File out, ProgressListener progress) { 306 public void writeJar(File out, ProgressListener progress) {
461 transformJar(out, progress, createTypeLoader()::transformInto); 307 transformJar(out, progress, createTypeLoader()::transformInto);
462 } 308 }
@@ -470,7 +316,7 @@ public class Deobfuscator {
470 AtomicInteger i = new AtomicInteger(); 316 AtomicInteger i = new AtomicInteger();
471 parsedJar.visitNode(node -> { 317 parsedJar.visitNode(node -> {
472 if (progress != null) { 318 if (progress != null) {
473 progress.onProgress(i.getAndIncrement(), node.name); 319 progress.step(i.getAndIncrement(), node.name);
474 } 320 }
475 321
476 try { 322 try {
@@ -485,50 +331,31 @@ public class Deobfuscator {
485 }); 331 });
486 332
487 if (progress != null) { 333 if (progress != null) {
488 progress.onProgress(i.get(), "Done!"); 334 progress.step(i.get(), "Done!");
489 } 335 }
490 } catch (IOException ex) { 336 } catch (IOException ex) {
491 throw new Error("Unable to write to Jar file!"); 337 throw new Error("Unable to write to Jar file!");
492 } 338 }
493 } 339 }
494 340
495 public <T extends Entry> T obfuscateEntry(T deobfEntry) { 341 public AccessModifier getModifier(Entry<?> entry) {
496 if (deobfEntry == null) { 342 EntryMapping mapping = mapper.getDeobfMapping(entry);
497 return null; 343 if (mapping == null) {
498 } 344 return AccessModifier.UNCHANGED;
499 T translatedEntry = getTranslator(TranslationDirection.OBFUSCATING).getTranslatedEntry(deobfEntry);
500 if (translatedEntry == null) {
501 return deobfEntry;
502 }
503 return translatedEntry;
504 }
505
506 public <T extends Entry> T deobfuscateEntry(T obfEntry) {
507 if (obfEntry == null) {
508 return null;
509 }
510 T translatedEntry = getTranslator(TranslationDirection.DEOBFUSCATING).getTranslatedEntry(obfEntry);
511 if (translatedEntry == null) {
512 return obfEntry;
513 }
514 return translatedEntry;
515 }
516
517 public <E extends Entry, C extends Entry> EntryReference<E, C> obfuscateReference(EntryReference<E, C> deobfReference) {
518 if (deobfReference == null) {
519 return null;
520 } 345 }
521 return new EntryReference<>(obfuscateEntry(deobfReference.entry), obfuscateEntry(deobfReference.context), deobfReference); 346 return mapping.getAccessModifier();
522 } 347 }
523 348
524 public <E extends Entry, C extends Entry> EntryReference<E, C> deobfuscateReference(EntryReference<E, C> obfReference) { 349 public void changeModifier(Entry<?> entry, AccessModifier modifier) {
525 if (obfReference == null) { 350 EntryMapping mapping = mapper.getDeobfMapping(entry);
526 return null; 351 if (mapping != null) {
352 mapper.mapFromObf(entry, new EntryMapping(mapping.getTargetName(), modifier));
353 } else {
354 mapper.mapFromObf(entry, new EntryMapping(entry.getName(), modifier));
527 } 355 }
528 return new EntryReference<>(deobfuscateEntry(obfReference.entry), deobfuscateEntry(obfReference.context), obfReference);
529 } 356 }
530 357
531 public boolean isObfuscatedIdentifier(Entry obfEntry) { 358 public boolean isObfuscatedIdentifier(Entry<?> obfEntry) {
532 if (obfEntry instanceof MethodEntry) { 359 if (obfEntry instanceof MethodEntry) {
533 // HACKHACK: Object methods are not obfuscated identifiers 360 // HACKHACK: Object methods are not obfuscated identifiers
534 MethodEntry obfMethodEntry = (MethodEntry) obfEntry; 361 MethodEntry obfMethodEntry = (MethodEntry) obfEntry;
@@ -559,142 +386,30 @@ public class Deobfuscator {
559 } 386 }
560 } 387 }
561 388
562 return this.jarIndex.containsObfEntry(obfEntry); 389 return this.jarIndex.getEntryIndex().hasEntry(obfEntry);
563 } 390 }
564 391
565 public boolean isRenameable(EntryReference<Entry, Entry> obfReference) { 392 public boolean isRenameable(EntryReference<Entry<?>, Entry<?>> obfReference) {
566 return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry()); 393 return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry());
567 } 394 }
568 395
569 public boolean hasDeobfuscatedName(Entry obfEntry) { 396 public boolean hasDeobfuscatedName(Entry<?> obfEntry) {
570 Translator translator = getTranslator(TranslationDirection.DEOBFUSCATING); 397 return mapper.hasDeobfMapping(obfEntry);
571 if (obfEntry instanceof ClassEntry) {
572 ClassEntry obfClass = (ClassEntry) obfEntry;
573 List<ClassMapping> mappingChain = this.mappings.getClassMappingChain(obfClass);
574 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1);
575 return classMapping != null && classMapping.getDeobfName() != null;
576 } else if (obfEntry instanceof FieldEntry) {
577 return translator.hasFieldMapping((FieldEntry) obfEntry);
578 } else if (obfEntry instanceof MethodEntry) {
579 MethodEntry methodEntry = (MethodEntry) obfEntry;
580 if (methodEntry.isConstructor()) {
581 return false;
582 }
583 return translator.hasMethodMapping(methodEntry);
584 } else if (obfEntry instanceof LocalVariableEntry) {
585 return translator.hasLocalVariableMapping((LocalVariableEntry) obfEntry);
586 } else {
587 throw new Error("Unknown entry desc: " + obfEntry.getClass().getName());
588 }
589 } 398 }
590 399
591 public void rename(Entry obfEntry, String newName) { 400 public void rename(Entry<?> obfEntry, String newName) {
592 rename(obfEntry, newName, true); 401 mapper.mapFromObf(obfEntry, new EntryMapping(newName));
593 } 402 }
594 403
595 // NOTE: these methods are a bit messy... oh well 404 public void removeMapping(Entry<?> obfEntry) {
596 405 mapper.removeByObf(obfEntry);
597 public void rename(Entry obfEntry, String newName, boolean clearCache) {
598 if (obfEntry instanceof ClassEntry) {
599 this.renamer.setClassName((ClassEntry) obfEntry, newName);
600 } else if (obfEntry instanceof FieldEntry) {
601 this.renamer.setFieldName((FieldEntry) obfEntry, newName);
602 } else if (obfEntry instanceof MethodEntry) {
603 if (((MethodEntry) obfEntry).isConstructor()) {
604 throw new IllegalArgumentException("Cannot rename constructors");
605 }
606
607 this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName);
608 } else if (obfEntry instanceof LocalVariableEntry) {
609 // TODO: Discern between arguments (propagate) and local vars (don't)
610 this.renamer.setLocalVariableTreeName((LocalVariableEntry) obfEntry, newName);
611 } else {
612 throw new Error("Unknown entry desc: " + obfEntry.getClass().getName());
613 }
614
615 // clear caches
616 if (clearCache)
617 this.translatorCache.clear();
618 } 406 }
619 407
620 public void removeMapping(Entry obfEntry) { 408 public void markAsDeobfuscated(Entry<?> obfEntry) {
621 removeMapping(obfEntry, true); 409 mapper.mapFromObf(obfEntry, new EntryMapping(mapper.deobfuscate(obfEntry).getName()));
622 } 410 }
623 411
624 public void removeMapping(Entry obfEntry, boolean clearCache) { 412 public static void runCustomTransforms(AstBuilder builder, DecompilerContext context) {
625 if (obfEntry instanceof ClassEntry) {
626 this.renamer.removeClassMapping((ClassEntry) obfEntry);
627 } else if (obfEntry instanceof FieldEntry) {
628 this.renamer.removeFieldMapping((FieldEntry) obfEntry);
629 } else if (obfEntry instanceof MethodEntry) {
630 if (((MethodEntry) obfEntry).isConstructor()) {
631 throw new IllegalArgumentException("Cannot rename constructors");
632 }
633 this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry);
634 } else if (obfEntry instanceof LocalVariableEntry) {
635 this.renamer.removeLocalVariableMapping((LocalVariableEntry) obfEntry);
636 } else {
637 throw new Error("Unknown entry desc: " + obfEntry);
638 }
639
640 // clear caches
641 if (clearCache)
642 this.translatorCache.clear();
643 }
644
645 public void markAsDeobfuscated(Entry obfEntry) {
646 markAsDeobfuscated(obfEntry, true);
647 }
648
649 public void markAsDeobfuscated(Entry obfEntry, boolean clearCache) {
650 if (obfEntry instanceof ClassEntry) {
651 this.renamer.markClassAsDeobfuscated((ClassEntry) obfEntry);
652 } else if (obfEntry instanceof FieldEntry) {
653 this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry);
654 } else if (obfEntry instanceof MethodEntry) {
655 MethodEntry methodEntry = (MethodEntry) obfEntry;
656 if (methodEntry.isConstructor()) {
657 throw new IllegalArgumentException("Cannot rename constructors");
658 }
659 this.renamer.markMethodTreeAsDeobfuscated(methodEntry);
660 } else if (obfEntry instanceof LocalVariableEntry) {
661 this.renamer.markArgumentAsDeobfuscated((LocalVariableEntry) obfEntry);
662 } else {
663 throw new Error("Unknown entry desc: " + obfEntry);
664 }
665
666 // clear caches
667 if (clearCache)
668 this.translatorCache.clear();
669 }
670
671 public void changeModifier(Entry entry, Mappings.EntryModifier modifierEntry) {
672 Entry obfEntry = obfuscateEntry(entry);
673 if (obfEntry instanceof ClassEntry)
674 this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry);
675 else if (obfEntry instanceof FieldEntry)
676 this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry);
677 else if (obfEntry instanceof MethodEntry)
678 this.renamer.setMethodModifier((MethodEntry) obfEntry, modifierEntry);
679 else
680 throw new Error("Unknown entry desc: " + obfEntry);
681 }
682
683 public Mappings.EntryModifier getModifier(Entry obfEntry) {
684 Entry entry = obfuscateEntry(obfEntry);
685 if (entry != null)
686 obfEntry = entry;
687 if (obfEntry instanceof ClassEntry)
688 return this.renamer.getClassModifier((ClassEntry) obfEntry);
689 else if (obfEntry instanceof FieldEntry)
690 return this.renamer.getFieldModifier((FieldEntry) obfEntry);
691 else if (obfEntry instanceof MethodEntry)
692 return this.renamer.getMethodModfifier((MethodEntry) obfEntry);
693 else
694 throw new Error("Unknown entry desc: " + obfEntry);
695 }
696
697 public static void runCustomTransforms(AstBuilder builder, DecompilerContext context){
698 List<IAstTransform> transformers = Arrays.asList( 413 List<IAstTransform> transformers = Arrays.asList(
699 new ObfuscatedEnumSwitchRewriterTransform(context), 414 new ObfuscatedEnumSwitchRewriterTransform(context),
700 new VarargsFixer(context), 415 new VarargsFixer(context),
@@ -707,12 +422,6 @@ public class Deobfuscator {
707 } 422 }
708 } 423 }
709 424
710 public interface ProgressListener {
711 void init(int totalWork, String title);
712
713 void onProgress(int numDone, String message);
714 }
715
716 public interface ClassTransformer { 425 public interface ClassTransformer {
717 String transform(ClassNode node, ClassWriter writer); 426 String transform(ClassNode node, ClassWriter writer);
718 } 427 }