summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Thog2016-10-17 14:37:27 +0200
committerGravatar Thog2016-10-17 14:37:27 +0200
commit17feccc4b5e8a6205a8ddb17b91ef61fcfa2186c (patch)
treeb6f0d33c8eed36fe4e2d72107b3710d58c661993
parentPreparing needed things for the modification (diff)
downloadenigma-17feccc4b5e8a6205a8ddb17b91ef61fcfa2186c.tar.gz
enigma-17feccc4b5e8a6205a8ddb17b91ef61fcfa2186c.tar.xz
enigma-17feccc4b5e8a6205a8ddb17b91ef61fcfa2186c.zip
Method Converter: Add bytecode matching to match what is identicalfeature/method-converter
-rw-r--r--src/main/java/cuchaz/enigma/ConvertMain.java4
-rw-r--r--src/main/java/cuchaz/enigma/Deobfuscator.java25
-rw-r--r--src/main/java/cuchaz/enigma/convert/MappingsConverter.java141
3 files changed, 156 insertions, 14 deletions
diff --git a/src/main/java/cuchaz/enigma/ConvertMain.java b/src/main/java/cuchaz/enigma/ConvertMain.java
index 77150038..1890aef1 100644
--- a/src/main/java/cuchaz/enigma/ConvertMain.java
+++ b/src/main/java/cuchaz/enigma/ConvertMain.java
@@ -239,9 +239,11 @@ public class ConvertMain {
239 System.out.println("Writing method matches..."); 239 System.out.println("Writing method matches...");
240 240
241 // get the matched and unmatched mappings 241 // get the matched and unmatched mappings
242 MemberMatches<BehaviorEntry> methodMatches = MappingsConverter.computeMemberMatches( 242 MemberMatches<BehaviorEntry> methodMatches = MappingsConverter.computeMethodsMatches(
243 destDeobfuscator, 243 destDeobfuscator,
244 destMappings, 244 destMappings,
245 sourceDeobfuscator,
246 sourceMappings,
245 classMatches, 247 classMatches,
246 MappingsConverter.getMethodDoer() 248 MappingsConverter.getMethodDoer()
247 ); 249 );
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java
index 2207999d..20bb8b98 100644
--- a/src/main/java/cuchaz/enigma/Deobfuscator.java
+++ b/src/main/java/cuchaz/enigma/Deobfuscator.java
@@ -156,6 +156,16 @@ public class Deobfuscator {
156 } 156 }
157 } 157 }
158 158
159 public TranslatingTypeLoader createTypeLoader()
160 {
161 return new TranslatingTypeLoader(
162 this.jar,
163 this.jarIndex,
164 getTranslator(TranslationDirection.Obfuscating),
165 getTranslator(TranslationDirection.Deobfuscating)
166 );
167 }
168
159 public CompilationUnit getSourceTree(String className) { 169 public CompilationUnit getSourceTree(String className) {
160 170
161 // we don't know if this class name is obfuscated or deobfuscated 171 // we don't know if this class name is obfuscated or deobfuscated
@@ -172,12 +182,7 @@ public class Deobfuscator {
172 } 182 }
173 183
174 // set the type loader 184 // set the type loader
175 TranslatingTypeLoader loader = new TranslatingTypeLoader( 185 TranslatingTypeLoader loader = createTypeLoader();
176 this.jar,
177 this.jarIndex,
178 getTranslator(TranslationDirection.Obfuscating),
179 getTranslator(TranslationDirection.Deobfuscating)
180 );
181 this.settings.setTypeLoader(loader); 186 this.settings.setTypeLoader(loader);
182 187
183 // see if procyon can find the type 188 // see if procyon can find the type
@@ -383,13 +388,7 @@ public class Deobfuscator {
383 } 388 }
384 389
385 public void writeJar(File out, ProgressListener progress) { 390 public void writeJar(File out, ProgressListener progress) {
386 final TranslatingTypeLoader loader = new TranslatingTypeLoader( 391 transformJar(out, progress, createTypeLoader()::transformClass);
387 this.jar,
388 this.jarIndex,
389 getTranslator(TranslationDirection.Obfuscating),
390 getTranslator(TranslationDirection.Deobfuscating)
391 );
392 transformJar(out, progress, loader::transformClass);
393 } 392 }
394 393
395 public void protectifyJar(File out, ProgressListener progress) { 394 public void protectifyJar(File out, ProgressListener progress) {
diff --git a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
index aa80014a..3d2cb86a 100644
--- a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
+++ b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java
@@ -13,10 +13,18 @@ package cuchaz.enigma.convert;
13import com.google.common.collect.*; 13import com.google.common.collect.*;
14import cuchaz.enigma.Constants; 14import cuchaz.enigma.Constants;
15import cuchaz.enigma.Deobfuscator; 15import cuchaz.enigma.Deobfuscator;
16import cuchaz.enigma.TranslatingTypeLoader;
16import cuchaz.enigma.analysis.JarIndex; 17import cuchaz.enigma.analysis.JarIndex;
17import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; 18import cuchaz.enigma.convert.ClassNamer.SidedClassNamer;
18import cuchaz.enigma.mapping.*; 19import cuchaz.enigma.mapping.*;
19import cuchaz.enigma.throwables.MappingConflict; 20import cuchaz.enigma.throwables.MappingConflict;
21import javassist.CtClass;
22import javassist.CtMethod;
23import javassist.NotFoundException;
24import javassist.bytecode.BadBytecode;
25import javassist.bytecode.CodeAttribute;
26import javassist.bytecode.CodeIterator;
27import javassist.bytecode.MethodInfo;
20 28
21import java.util.*; 29import java.util.*;
22import java.util.jar.JarFile; 30import java.util.jar.JarFile;
@@ -390,6 +398,139 @@ public class MappingsConverter {
390 }; 398 };
391 } 399 }
392 400
401 public static int compareMethodByteCode(CodeIterator sourceIt, CodeIterator destIt)
402 {
403 int sourcePos = 0;
404 int destPos = 0;
405 while (sourceIt.hasNext() && destIt.hasNext())
406 {
407 try
408 {
409 sourcePos = sourceIt.next();
410 destPos = destIt.next();
411 if (sourceIt.byteAt(sourcePos) != destIt.byteAt(destPos))
412 return sourcePos;
413 } catch (BadBytecode badBytecode)
414 {
415 // Ignore bad bytecode (it might be a little bit dangerous...)
416 }
417 }
418 if (sourcePos < destPos)
419 return sourcePos;
420 else if (destPos < sourcePos)
421 return destPos;
422 return sourcePos;
423 }
424
425 public static BehaviorEntry compareMethods(CtClass destCtClass, CtClass sourceCtClass, BehaviorEntry obfSourceEntry,
426 Set<BehaviorEntry> obfDestEntries)
427 {
428 try
429 {
430 // Get the source method with Javassist
431 CtMethod sourceCtClassMethod = sourceCtClass.getMethod(obfSourceEntry.getName(), obfSourceEntry.getSignature().toString());
432 CodeAttribute sourceAttribute = sourceCtClassMethod.getMethodInfo().getCodeAttribute();
433
434 // Empty method body, ignore!
435 if (sourceAttribute == null)
436 return null;
437 Iterator<BehaviorEntry> it = obfDestEntries.iterator();
438 while (it.hasNext())
439 {
440 BehaviorEntry desEntry = it.next();
441 try
442 {
443 CtMethod destCtClassMethod = destCtClass.getMethod(desEntry.getName(), desEntry.getSignature().toString());
444 CodeAttribute destAttribute = destCtClassMethod.getMethodInfo().getCodeAttribute();
445
446 // Ignore empty body methods
447 if (destAttribute == null)
448 continue;
449 CodeIterator destIterator = destAttribute.iterator();
450 int maxPos = compareMethodByteCode(sourceAttribute.iterator(), destIterator);
451
452 // The bytecode is identical to the original method, assuming that the method is correct!
453 if (sourceAttribute.getCodeLength() == (maxPos + 1) && maxPos > 1)
454 return desEntry;
455 } catch (NotFoundException e)
456 {
457 e.printStackTrace();
458 }
459 }
460 } catch (NotFoundException e)
461 {
462 e.printStackTrace();
463 return null;
464 }
465 return null;
466 }
467
468 public static MemberMatches<BehaviorEntry> computeMethodsMatches(Deobfuscator destDeobfuscator, Mappings destMappings, Deobfuscator sourceDeobfuscator, Mappings sourceMappings, ClassMatches classMatches, Doer<BehaviorEntry> doer) {
469
470 MemberMatches<BehaviorEntry> memberMatches = new MemberMatches<>();
471
472 // unmatched source fields are easy
473 MappingsChecker checker = new MappingsChecker(destDeobfuscator.getJarIndex());
474 checker.dropBrokenMappings(destMappings);
475 for (BehaviorEntry destObfEntry : doer.getDroppedEntries(checker)) {
476 BehaviorEntry srcObfEntry = translate(destObfEntry, classMatches.getUniqueMatches().inverse());
477 memberMatches.addUnmatchedSourceEntry(srcObfEntry);
478 }
479
480 // get matched fields (anything that's left after the checks/drops is matched(
481 for (ClassMapping classMapping : destMappings.classes())
482 collectMatchedFields(memberMatches, classMapping, classMatches, doer);
483
484 // get unmatched dest fields
485 doer.getObfEntries(destDeobfuscator.getJarIndex()).stream()
486 .filter(destEntry -> !memberMatches.isMatchedDestEntry(destEntry))
487 .forEach(memberMatches::addUnmatchedDestEntry);
488
489 // Apply mappings to deobfuscator
490
491 // Create type loader
492 TranslatingTypeLoader destTypeLoader = destDeobfuscator.createTypeLoader();
493 TranslatingTypeLoader sourceTypeLoader = sourceDeobfuscator.createTypeLoader();
494
495 System.out.println("Automatching " + memberMatches.getUnmatchedSourceEntries().size() + " unmatched source entries...");
496
497 // go through the unmatched source fields and try to pick out the easy matches
498 for (ClassEntry obfSourceClass : Lists.newArrayList(memberMatches.getSourceClassesWithUnmatchedEntries())) {
499 for (BehaviorEntry obfSourceEntry : Lists.newArrayList(memberMatches.getUnmatchedSourceEntries(obfSourceClass))) {
500
501 // get the possible dest matches
502 ClassEntry obfDestClass = classMatches.getUniqueMatches().get(obfSourceClass);
503
504 // filter by type/signature
505 Set<BehaviorEntry> obfDestEntries = doer.filterEntries(memberMatches.getUnmatchedDestEntries(obfDestClass), obfSourceEntry, classMatches);
506
507 if (obfDestEntries.size() == 1) {
508 // make the easy match
509 memberMatches.makeMatch(obfSourceEntry, obfDestEntries.iterator().next());
510 } else if (obfDestEntries.isEmpty()) {
511 // no match is possible =(
512 memberMatches.makeSourceUnmatchable(obfSourceEntry, null);
513 } else
514 {
515 // Multiple matches! Scan methods instructions
516 CtClass destCtClass = destTypeLoader.loadClass(obfDestClass.getClassName());
517 CtClass sourceCtClass = sourceTypeLoader.loadClass(obfSourceClass.getClassName());
518 BehaviorEntry match = compareMethods(destCtClass, sourceCtClass, obfSourceEntry, obfDestEntries);
519 // the method match correctly, match it on the member mapping!
520 if (match != null)
521 memberMatches.makeMatch(obfSourceEntry, match);
522 }
523 }
524 }
525
526 System.out.println(String.format("Ended up with %d ambiguous and %d unmatchable source entries",
527 memberMatches.getUnmatchedSourceEntries().size(),
528 memberMatches.getUnmatchableSourceEntries().size()
529 ));
530
531 return memberMatches;
532 }
533
393 public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) { 534 public static <T extends Entry> MemberMatches<T> computeMemberMatches(Deobfuscator destDeobfuscator, Mappings destMappings, ClassMatches classMatches, Doer<T> doer) {
394 535
395 MemberMatches<T> memberMatches = new MemberMatches<T>(); 536 MemberMatches<T> memberMatches = new MemberMatches<T>();