diff options
Diffstat (limited to 'src/main/java/cuchaz/enigma/convert/MappingsConverter.java')
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/MappingsConverter.java | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/src/main/java/cuchaz/enigma/convert/MappingsConverter.java b/src/main/java/cuchaz/enigma/convert/MappingsConverter.java index aa80014..3d2cb86 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; | |||
| 13 | import com.google.common.collect.*; | 13 | import com.google.common.collect.*; |
| 14 | import cuchaz.enigma.Constants; | 14 | import cuchaz.enigma.Constants; |
| 15 | import cuchaz.enigma.Deobfuscator; | 15 | import cuchaz.enigma.Deobfuscator; |
| 16 | import cuchaz.enigma.TranslatingTypeLoader; | ||
| 16 | import cuchaz.enigma.analysis.JarIndex; | 17 | import cuchaz.enigma.analysis.JarIndex; |
| 17 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; | 18 | import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; |
| 18 | import cuchaz.enigma.mapping.*; | 19 | import cuchaz.enigma.mapping.*; |
| 19 | import cuchaz.enigma.throwables.MappingConflict; | 20 | import cuchaz.enigma.throwables.MappingConflict; |
| 21 | import javassist.CtClass; | ||
| 22 | import javassist.CtMethod; | ||
| 23 | import javassist.NotFoundException; | ||
| 24 | import javassist.bytecode.BadBytecode; | ||
| 25 | import javassist.bytecode.CodeAttribute; | ||
| 26 | import javassist.bytecode.CodeIterator; | ||
| 27 | import javassist.bytecode.MethodInfo; | ||
| 20 | 28 | ||
| 21 | import java.util.*; | 29 | import java.util.*; |
| 22 | import java.util.jar.JarFile; | 30 | import 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>(); |