diff options
Diffstat (limited to 'src/main/java')
| -rw-r--r-- | src/main/java/cuchaz/enigma/ConvertMain.java | 4 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/Deobfuscator.java | 25 | ||||
| -rw-r--r-- | src/main/java/cuchaz/enigma/convert/MappingsConverter.java | 141 |
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; | |||
| 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>(); |