summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/Deobfuscator.java
diff options
context:
space:
mode:
authorGravatar lclc982016-06-30 00:49:21 +1000
committerGravatar GitHub2016-06-30 00:49:21 +1000
commit4be005617b3b8c3578cca07c5d085d12916f0d1d (patch)
treedb163431f38703e26da417ef05eaea2b27a498b9 /src/cuchaz/enigma/Deobfuscator.java
parentSome small changes to fix idea importing (diff)
downloadenigma-fork-4be005617b3b8c3578cca07c5d085d12916f0d1d.tar.gz
enigma-fork-4be005617b3b8c3578cca07c5d085d12916f0d1d.tar.xz
enigma-fork-4be005617b3b8c3578cca07c5d085d12916f0d1d.zip
Json format (#2)
* Added new format * Fixed bug * Updated Version
Diffstat (limited to 'src/cuchaz/enigma/Deobfuscator.java')
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java551
1 files changed, 0 insertions, 551 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java
deleted file mode 100644
index 82d1611..0000000
--- a/src/cuchaz/enigma/Deobfuscator.java
+++ /dev/null
@@ -1,551 +0,0 @@
1/*******************************************************************************
2 * Copyright (c) 2015 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Lesser General Public
5 * License v3.0 which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/lgpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma;
12
13import java.io.File;
14import java.io.FileOutputStream;
15import java.io.FileWriter;
16import java.io.IOException;
17import java.io.StringWriter;
18import java.util.List;
19import java.util.Map;
20import java.util.Set;
21import java.util.jar.JarEntry;
22import java.util.jar.JarFile;
23import java.util.jar.JarOutputStream;
24
25import javassist.CtClass;
26import javassist.bytecode.Descriptor;
27
28import com.google.common.collect.Maps;
29import com.google.common.collect.Sets;
30import com.strobel.assembler.metadata.MetadataSystem;
31import com.strobel.assembler.metadata.TypeDefinition;
32import com.strobel.assembler.metadata.TypeReference;
33import com.strobel.decompiler.DecompilerContext;
34import com.strobel.decompiler.DecompilerSettings;
35import com.strobel.decompiler.PlainTextOutput;
36import com.strobel.decompiler.languages.java.JavaOutputVisitor;
37import com.strobel.decompiler.languages.java.ast.AstBuilder;
38import com.strobel.decompiler.languages.java.ast.CompilationUnit;
39import com.strobel.decompiler.languages.java.ast.InsertParenthesesVisitor;
40
41import cuchaz.enigma.analysis.EntryReference;
42import cuchaz.enigma.analysis.JarClassIterator;
43import cuchaz.enigma.analysis.JarIndex;
44import cuchaz.enigma.analysis.SourceIndex;
45import cuchaz.enigma.analysis.SourceIndexVisitor;
46import cuchaz.enigma.analysis.Token;
47import cuchaz.enigma.bytecode.ClassProtectifier;
48import cuchaz.enigma.bytecode.ClassPublifier;
49import cuchaz.enigma.mapping.ArgumentEntry;
50import cuchaz.enigma.mapping.BehaviorEntry;
51import cuchaz.enigma.mapping.ClassEntry;
52import cuchaz.enigma.mapping.ClassMapping;
53import cuchaz.enigma.mapping.ConstructorEntry;
54import cuchaz.enigma.mapping.Entry;
55import cuchaz.enigma.mapping.FieldEntry;
56import cuchaz.enigma.mapping.FieldMapping;
57import cuchaz.enigma.mapping.Mappings;
58import cuchaz.enigma.mapping.MappingsChecker;
59import cuchaz.enigma.mapping.MappingsRenamer;
60import cuchaz.enigma.mapping.MethodEntry;
61import cuchaz.enigma.mapping.MethodMapping;
62import cuchaz.enigma.mapping.TranslationDirection;
63import cuchaz.enigma.mapping.Translator;
64
65public class Deobfuscator {
66
67 public interface ProgressListener {
68 void init(int totalWork, String title);
69 void onProgress(int numDone, String message);
70 }
71
72 private JarFile m_jar;
73 private DecompilerSettings m_settings;
74 private JarIndex m_jarIndex;
75 private Mappings m_mappings;
76 private MappingsRenamer m_renamer;
77 private Map<TranslationDirection,Translator> m_translatorCache;
78
79 public Deobfuscator(JarFile jar) throws IOException {
80 m_jar = jar;
81
82 // build the jar index
83 m_jarIndex = new JarIndex();
84 m_jarIndex.indexJar(m_jar, true);
85
86 // config the decompiler
87 m_settings = DecompilerSettings.javaDefaults();
88 m_settings.setMergeVariables(true);
89 m_settings.setForceExplicitImports(true);
90 m_settings.setForceExplicitTypeArguments(true);
91 m_settings.setShowDebugLineNumbers(true);
92 // DEBUG
93 //m_settings.setShowSyntheticMembers(true);
94
95 // init defaults
96 m_translatorCache = Maps.newTreeMap();
97
98 // init mappings
99 setMappings(new Mappings());
100 }
101
102 public JarFile getJar() {
103 return m_jar;
104 }
105
106 public String getJarName() {
107 return m_jar.getName();
108 }
109
110 public JarIndex getJarIndex() {
111 return m_jarIndex;
112 }
113
114 public Mappings getMappings() {
115 return m_mappings;
116 }
117
118 public void setMappings(Mappings val) {
119 setMappings(val, true);
120 }
121
122 public void setMappings(Mappings val, boolean warnAboutDrops) {
123 if (val == null) {
124 val = new Mappings();
125 }
126
127 // drop mappings that don't match the jar
128 MappingsChecker checker = new MappingsChecker(m_jarIndex);
129 checker.dropBrokenMappings(val);
130 if (warnAboutDrops) {
131 for (java.util.Map.Entry<ClassEntry,ClassMapping> mapping : checker.getDroppedClassMappings().entrySet()) {
132 System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
133 }
134 for (java.util.Map.Entry<ClassEntry,ClassMapping> mapping : checker.getDroppedInnerClassMappings().entrySet()) {
135 System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
136 }
137 for (java.util.Map.Entry<FieldEntry,FieldMapping> mapping : checker.getDroppedFieldMappings().entrySet()) {
138 System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
139 }
140 for (java.util.Map.Entry<BehaviorEntry,MethodMapping> mapping : checker.getDroppedMethodMappings().entrySet()) {
141 System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped.");
142 }
143 }
144
145 // check for related method inconsistencies
146 if (checker.getRelatedMethodChecker().hasProblems()) {
147 throw new Error("Related methods are inconsistent! Need to fix the mappings manually.\n" + checker.getRelatedMethodChecker().getReport());
148 }
149
150 m_mappings = val;
151 m_renamer = new MappingsRenamer(m_jarIndex, val);
152 m_translatorCache.clear();
153 }
154
155 public Translator getTranslator(TranslationDirection direction) {
156 Translator translator = m_translatorCache.get(direction);
157 if (translator == null) {
158 translator = m_mappings.getTranslator(direction, m_jarIndex.getTranslationIndex());
159 m_translatorCache.put(direction, translator);
160 }
161 return translator;
162 }
163
164 public void getSeparatedClasses(List<ClassEntry> obfClasses, List<ClassEntry> deobfClasses) {
165 for (ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries()) {
166 // skip inner classes
167 if (obfClassEntry.isInnerClass()) {
168 continue;
169 }
170
171 // separate the classes
172 ClassEntry deobfClassEntry = deobfuscateEntry(obfClassEntry);
173 if (!deobfClassEntry.equals(obfClassEntry)) {
174 // if the class has a mapping, clearly it's deobfuscated
175 deobfClasses.add(deobfClassEntry);
176 } else if (!obfClassEntry.getPackageName().equals(Constants.NonePackage)) {
177 // also call it deobufscated if it's not in the none package
178 deobfClasses.add(obfClassEntry);
179 } else {
180 // otherwise, assume it's still obfuscated
181 obfClasses.add(obfClassEntry);
182 }
183 }
184 }
185
186 public CompilationUnit getSourceTree(String className) {
187
188 // we don't know if this class name is obfuscated or deobfuscated
189 // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out
190 // the decompiler only sees classes after deobfuscation, so we need to load it by the deobfuscated name if there is one
191
192 // first, assume class name is deobf
193 String deobfClassName = className;
194
195 // if it wasn't actually deobf, then we can find a mapping for it and get the deobf name
196 ClassMapping classMapping = m_mappings.getClassByObf(className);
197 if (classMapping != null && classMapping.getDeobfName() != null) {
198 deobfClassName = classMapping.getDeobfName();
199 }
200
201 // set the type loader
202 TranslatingTypeLoader loader = new TranslatingTypeLoader(
203 m_jar,
204 m_jarIndex,
205 getTranslator(TranslationDirection.Obfuscating),
206 getTranslator(TranslationDirection.Deobfuscating)
207 );
208 m_settings.setTypeLoader(loader);
209
210 // see if procyon can find the type
211 TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName);
212 if (type == null) {
213 throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s",
214 className, deobfClassName, loader.getClassNamesToTry(deobfClassName)
215 ));
216 }
217 TypeDefinition resolvedType = type.resolve();
218
219 // decompile it!
220 DecompilerContext context = new DecompilerContext();
221 context.setCurrentType(resolvedType);
222 context.setSettings(m_settings);
223 AstBuilder builder = new AstBuilder(context);
224 builder.addType(resolvedType);
225 builder.runTransformations(null);
226 return builder.getCompilationUnit();
227 }
228
229 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source) {
230 return getSourceIndex(sourceTree, source, null);
231 }
232
233 public SourceIndex getSourceIndex(CompilationUnit sourceTree, String source, Boolean ignoreBadTokens) {
234
235 // build the source index
236 SourceIndex index;
237 if (ignoreBadTokens != null) {
238 index = new SourceIndex(source, ignoreBadTokens);
239 } else {
240 index = new SourceIndex(source);
241 }
242 sourceTree.acceptVisitor(new SourceIndexVisitor(), index);
243
244 // DEBUG
245 // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null );
246
247 // resolve all the classes in the source references
248 for (Token token : index.referenceTokens()) {
249 EntryReference<Entry,Entry> deobfReference = index.getDeobfReference(token);
250
251 // get the obfuscated entry
252 Entry obfEntry = obfuscateEntry(deobfReference.entry);
253
254 // try to resolve the class
255 ClassEntry resolvedObfClassEntry = m_jarIndex.getTranslationIndex().resolveEntryClass(obfEntry);
256 if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) {
257 // change the class of the entry
258 obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry);
259
260 // save the new deobfuscated reference
261 deobfReference.entry = deobfuscateEntry(obfEntry);
262 index.replaceDeobfReference(token, deobfReference);
263 }
264
265 // DEBUG
266 // System.out.println( token + " -> " + reference + " -> " + index.getReferenceToken( reference ) );
267 }
268
269 return index;
270 }
271
272 public String getSource(CompilationUnit sourceTree) {
273 // render the AST into source
274 StringWriter buf = new StringWriter();
275 sourceTree.acceptVisitor(new InsertParenthesesVisitor(), null);
276 sourceTree.acceptVisitor(new JavaOutputVisitor(new PlainTextOutput(buf), m_settings), null);
277 return buf.toString();
278 }
279
280 public void writeSources(File dirOut, ProgressListener progress) throws IOException {
281 // get the classes to decompile
282 Set<ClassEntry> classEntries = Sets.newHashSet();
283 for (ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries()) {
284 // skip inner classes
285 if (obfClassEntry.isInnerClass()) {
286 continue;
287 }
288
289 classEntries.add(obfClassEntry);
290 }
291
292 if (progress != null) {
293 progress.init(classEntries.size(), "Decompiling classes...");
294 }
295
296 // DEOBFUSCATE ALL THE THINGS!! @_@
297 int i = 0;
298 for (ClassEntry obfClassEntry : classEntries) {
299 ClassEntry deobfClassEntry = deobfuscateEntry(new ClassEntry(obfClassEntry));
300 if (progress != null) {
301 progress.onProgress(i++, deobfClassEntry.toString());
302 }
303
304 try {
305 // get the source
306 String source = getSource(getSourceTree(obfClassEntry.getName()));
307
308 // write the file
309 File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java");
310 file.getParentFile().mkdirs();
311 try (FileWriter out = new FileWriter(file)) {
312 out.write(source);
313 }
314 } catch (Throwable t) {
315 // don't crash the whole world here, just log the error and keep going
316 // TODO: set up logback via log4j
317 System.err.println("Unable to deobfuscate class " + deobfClassEntry.toString() + " (" + obfClassEntry.toString() + ")");
318 t.printStackTrace(System.err);
319 }
320 }
321 if (progress != null) {
322 progress.onProgress(i, "Done!");
323 }
324 }
325
326 public void writeJar(File out, ProgressListener progress) {
327 final TranslatingTypeLoader loader = new TranslatingTypeLoader(
328 m_jar,
329 m_jarIndex,
330 getTranslator(TranslationDirection.Obfuscating),
331 getTranslator(TranslationDirection.Deobfuscating)
332 );
333 transformJar(out, progress, new ClassTransformer() {
334
335 @Override
336 public CtClass transform(CtClass c) throws Exception {
337 return loader.transformClass(c);
338 }
339 });
340 }
341
342 public void protectifyJar(File out, ProgressListener progress) {
343 transformJar(out, progress, new ClassTransformer() {
344
345 @Override
346 public CtClass transform(CtClass c) throws Exception {
347 return ClassProtectifier.protectify(c);
348 }
349 });
350 }
351
352 public void publifyJar(File out, ProgressListener progress) {
353 transformJar(out, progress, new ClassTransformer() {
354
355 @Override
356 public CtClass transform(CtClass c) throws Exception {
357 return ClassPublifier.publify(c);
358 }
359 });
360 }
361
362 private interface ClassTransformer {
363 public CtClass transform(CtClass c) throws Exception;
364 }
365 private void transformJar(File out, ProgressListener progress, ClassTransformer transformer) {
366 try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) {
367 if (progress != null) {
368 progress.init(JarClassIterator.getClassEntries(m_jar).size(), "Transforming classes...");
369 }
370
371 int i = 0;
372 for (CtClass c : JarClassIterator.classes(m_jar)) {
373 if (progress != null) {
374 progress.onProgress(i++, c.getName());
375 }
376
377 try {
378 c = transformer.transform(c);
379 outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class"));
380 outJar.write(c.toBytecode());
381 outJar.closeEntry();
382 } catch (Throwable t) {
383 throw new Error("Unable to transform class " + c.getName(), t);
384 }
385 }
386 if (progress != null) {
387 progress.onProgress(i, "Done!");
388 }
389
390 outJar.close();
391 } catch (IOException ex) {
392 throw new Error("Unable to write to Jar file!");
393 }
394 }
395
396 public <T extends Entry> T obfuscateEntry(T deobfEntry) {
397 if (deobfEntry == null) {
398 return null;
399 }
400 return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry);
401 }
402
403 public <T extends Entry> T deobfuscateEntry(T obfEntry) {
404 if (obfEntry == null) {
405 return null;
406 }
407 return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry);
408 }
409
410 public <E extends Entry,C extends Entry> EntryReference<E,C> obfuscateReference(EntryReference<E,C> deobfReference) {
411 if (deobfReference == null) {
412 return null;
413 }
414 return new EntryReference<E,C>(
415 obfuscateEntry(deobfReference.entry),
416 obfuscateEntry(deobfReference.context),
417 deobfReference
418 );
419 }
420
421 public <E extends Entry,C extends Entry> EntryReference<E,C> deobfuscateReference(EntryReference<E,C> obfReference) {
422 if (obfReference == null) {
423 return null;
424 }
425 return new EntryReference<E,C>(
426 deobfuscateEntry(obfReference.entry),
427 deobfuscateEntry(obfReference.context),
428 obfReference
429 );
430 }
431
432 public boolean isObfuscatedIdentifier(Entry obfEntry) {
433
434 if (obfEntry instanceof MethodEntry) {
435
436 // HACKHACK: Object methods are not obfuscated identifiers
437 MethodEntry obfMethodEntry = (MethodEntry)obfEntry;
438 String name = obfMethodEntry.getName();
439 String sig = obfMethodEntry.getSignature().toString();
440 if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) {
441 return false;
442 } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) {
443 return false;
444 } else if (name.equals("finalize") && sig.equals("()V")) {
445 return false;
446 } else if (name.equals("getClass") && sig.equals("()Ljava/lang/Class;")) {
447 return false;
448 } else if (name.equals("hashCode") && sig.equals("()I")) {
449 return false;
450 } else if (name.equals("notify") && sig.equals("()V")) {
451 return false;
452 } else if (name.equals("notifyAll") && sig.equals("()V")) {
453 return false;
454 } else if (name.equals("toString") && sig.equals("()Ljava/lang/String;")) {
455 return false;
456 } else if (name.equals("wait") && sig.equals("()V")) {
457 return false;
458 } else if (name.equals("wait") && sig.equals("(J)V")) {
459 return false;
460 } else if (name.equals("wait") && sig.equals("(JI)V")) {
461 return false;
462 }
463 }
464
465 return m_jarIndex.containsObfEntry(obfEntry);
466 }
467
468 public boolean isRenameable(EntryReference<Entry,Entry> obfReference) {
469 return obfReference.isNamed() && isObfuscatedIdentifier(obfReference.getNameableEntry());
470 }
471
472 // NOTE: these methods are a bit messy... oh well
473
474 public boolean hasDeobfuscatedName(Entry obfEntry) {
475 Translator translator = getTranslator(TranslationDirection.Deobfuscating);
476 if (obfEntry instanceof ClassEntry) {
477 ClassEntry obfClass = (ClassEntry)obfEntry;
478 List<ClassMapping> mappingChain = m_mappings.getClassMappingChain(obfClass);
479 ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1);
480 return classMapping != null && classMapping.getDeobfName() != null;
481 } else if (obfEntry instanceof FieldEntry) {
482 return translator.translate((FieldEntry)obfEntry) != null;
483 } else if (obfEntry instanceof MethodEntry) {
484 return translator.translate((MethodEntry)obfEntry) != null;
485 } else if (obfEntry instanceof ConstructorEntry) {
486 // constructors have no names
487 return false;
488 } else if (obfEntry instanceof ArgumentEntry) {
489 return translator.translate((ArgumentEntry)obfEntry) != null;
490 } else {
491 throw new Error("Unknown entry type: " + obfEntry.getClass().getName());
492 }
493 }
494
495 public void rename(Entry obfEntry, String newName) {
496 if (obfEntry instanceof ClassEntry) {
497 m_renamer.setClassName((ClassEntry)obfEntry, Descriptor.toJvmName(newName));
498 } else if (obfEntry instanceof FieldEntry) {
499 m_renamer.setFieldName((FieldEntry)obfEntry, newName);
500 } else if (obfEntry instanceof MethodEntry) {
501 m_renamer.setMethodTreeName((MethodEntry)obfEntry, newName);
502 } else if (obfEntry instanceof ConstructorEntry) {
503 throw new IllegalArgumentException("Cannot rename constructors");
504 } else if (obfEntry instanceof ArgumentEntry) {
505 m_renamer.setArgumentName((ArgumentEntry)obfEntry, newName);
506 } else {
507 throw new Error("Unknown entry type: " + obfEntry.getClass().getName());
508 }
509
510 // clear caches
511 m_translatorCache.clear();
512 }
513
514 public void removeMapping(Entry obfEntry) {
515 if (obfEntry instanceof ClassEntry) {
516 m_renamer.removeClassMapping((ClassEntry)obfEntry);
517 } else if (obfEntry instanceof FieldEntry) {
518 m_renamer.removeFieldMapping((FieldEntry)obfEntry);
519 } else if (obfEntry instanceof MethodEntry) {
520 m_renamer.removeMethodTreeMapping((MethodEntry)obfEntry);
521 } else if (obfEntry instanceof ConstructorEntry) {
522 throw new IllegalArgumentException("Cannot rename constructors");
523 } else if (obfEntry instanceof ArgumentEntry) {
524 m_renamer.removeArgumentMapping((ArgumentEntry)obfEntry);
525 } else {
526 throw new Error("Unknown entry type: " + obfEntry);
527 }
528
529 // clear caches
530 m_translatorCache.clear();
531 }
532
533 public void markAsDeobfuscated(Entry obfEntry) {
534 if (obfEntry instanceof ClassEntry) {
535 m_renamer.markClassAsDeobfuscated((ClassEntry)obfEntry);
536 } else if (obfEntry instanceof FieldEntry) {
537 m_renamer.markFieldAsDeobfuscated((FieldEntry)obfEntry);
538 } else if (obfEntry instanceof MethodEntry) {
539 m_renamer.markMethodTreeAsDeobfuscated((MethodEntry)obfEntry);
540 } else if (obfEntry instanceof ConstructorEntry) {
541 throw new IllegalArgumentException("Cannot rename constructors");
542 } else if (obfEntry instanceof ArgumentEntry) {
543 m_renamer.markArgumentAsDeobfuscated((ArgumentEntry)obfEntry);
544 } else {
545 throw new Error("Unknown entry type: " + obfEntry);
546 }
547
548 // clear caches
549 m_translatorCache.clear();
550 }
551}