From 4ceb8d490058e48df666bf7227ce020e60928be5 Mon Sep 17 00:00:00 2001 From: jeff Date: Sun, 8 Mar 2015 20:48:30 -0400 Subject: more tweaks, improvements, and bug fixes --- src/cuchaz/enigma/ConvertMain.java | 22 +++++-- src/cuchaz/enigma/Deobfuscator.java | 25 ++++++-- src/cuchaz/enigma/convert/ClassIdentity.java | 25 +++++++- src/cuchaz/enigma/convert/MappingsConverter.java | 60 +++++++++++++++--- src/cuchaz/enigma/gui/MatchingGui.java | 79 ++++++++++++++---------- src/cuchaz/enigma/mapping/ArgumentMapping.java | 5 ++ src/cuchaz/enigma/mapping/ClassMapping.java | 11 ++++ src/cuchaz/enigma/mapping/FieldMapping.java | 26 ++++++++ src/cuchaz/enigma/mapping/MethodMapping.java | 16 ++++- src/cuchaz/enigma/mapping/Signature.java | 5 ++ src/cuchaz/enigma/mapping/Type.java | 13 ++++ 11 files changed, 233 insertions(+), 54 deletions(-) (limited to 'src') diff --git a/src/cuchaz/enigma/ConvertMain.java b/src/cuchaz/enigma/ConvertMain.java index cad49f58..975cdcc8 100644 --- a/src/cuchaz/enigma/ConvertMain.java +++ b/src/cuchaz/enigma/ConvertMain.java @@ -2,6 +2,7 @@ package cuchaz.enigma; import java.io.File; import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.util.jar.JarFile; @@ -14,6 +15,7 @@ import cuchaz.enigma.gui.MatchingGui.SaveListener; import cuchaz.enigma.mapping.MappingParseException; import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.MappingsReader; +import cuchaz.enigma.mapping.MappingsWriter; public class ConvertMain { @@ -32,7 +34,7 @@ public class ConvertMain { //computeMatches(matchingFile, sourceJar, destJar, mappings); editMatches(matchingFile, sourceJar, destJar, mappings); - //convertMappings(outMappingsFile, mappings, matchingFile); + //convertMappings(outMappingsFile, sourceJar, destJar, mappings, matchingFile); /* TODO // write out the converted mappings @@ -56,7 +58,7 @@ public class ConvertMain { Matches matches = MatchesReader.read(matchingFile); System.out.println("Indexing source jar..."); Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); - sourceDeobfuscator.setMappings(mappings); + sourceDeobfuscator.setMappings(mappings, false); System.out.println("Indexing dest jar..."); Deobfuscator destDeobfuscator = new Deobfuscator(destJar); System.out.println("Starting GUI..."); @@ -72,9 +74,21 @@ public class ConvertMain { }); } - private static void convertMappings(File outMappingsFile, Mappings mappings, File matchingFile) + private static void convertMappings(File outMappingsFile, JarFile sourceJar, JarFile destJar, Mappings mappings, File matchingFile) throws IOException { + System.out.println("Reading matches..."); Matches matches = MatchesReader.read(matchingFile); - MappingsConverter.convertMappings(mappings, matches.getUniqueMatches()); + System.out.println("Indexing source jar..."); + Deobfuscator sourceDeobfuscator = new Deobfuscator(sourceJar); + sourceDeobfuscator.setMappings(mappings); + System.out.println("Indexing dest jar..."); + Deobfuscator destDeobfuscator = new Deobfuscator(destJar); + + Mappings newMappings = MappingsConverter.newMappings(matches, mappings, sourceDeobfuscator, destDeobfuscator); + + try (FileWriter out = new FileWriter(outMappingsFile)) { + new MappingsWriter().write(out, newMappings); + } + System.out.println("Write converted mappings to: " + outMappingsFile.getAbsolutePath()); } } diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index e5d0e3d9..9b0d3db4 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -116,6 +116,10 @@ public class Deobfuscator { } public void setMappings(Mappings val) { + setMappings(val, true); + } + + public void setMappings(Mappings val, boolean warnAboutDrops) { if (val == null) { val = new Mappings(); } @@ -123,7 +127,10 @@ public class Deobfuscator { // drop mappings that don't match the jar RelatedMethodChecker relatedMethodChecker = new RelatedMethodChecker(m_jarIndex); for (ClassMapping classMapping : Lists.newArrayList(val.classes())) { - if (!checkClassMapping(relatedMethodChecker, classMapping)) { + if (!checkClassMapping(relatedMethodChecker, classMapping, warnAboutDrops)) { + if (warnAboutDrops) { + System.err.println("WARNING: unable to find class " + classMapping.getObfFullName() + ". dropping mapping"); + } val.removeClassMapping(classMapping); } } @@ -138,7 +145,7 @@ public class Deobfuscator { m_translatorCache.clear(); } - private boolean checkClassMapping(RelatedMethodChecker relatedMethodChecker, ClassMapping classMapping) { + private boolean checkClassMapping(RelatedMethodChecker relatedMethodChecker, ClassMapping classMapping, boolean warnAboutDrops) { // check the class ClassEntry classEntry = EntryFactory.getObfClassEntry(m_jarIndex, classMapping); @@ -150,7 +157,9 @@ public class Deobfuscator { for (FieldMapping fieldMapping : Lists.newArrayList(classMapping.fields())) { FieldEntry fieldEntry = new FieldEntry(classEntry, fieldMapping.getObfName(), fieldMapping.getObfType()); if (!m_jarIndex.containsObfField(fieldEntry)) { - System.err.println("WARNING: unable to find field " + fieldEntry + ". dropping mapping."); + if (warnAboutDrops) { + System.err.println("WARNING: unable to find field " + fieldEntry + ". dropping mapping."); + } classMapping.removeFieldMapping(fieldMapping); } } @@ -159,7 +168,9 @@ public class Deobfuscator { for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { BehaviorEntry obfBehaviorEntry = EntryFactory.getObfBehaviorEntry(classEntry, methodMapping); if (!m_jarIndex.containsObfBehavior(obfBehaviorEntry)) { - System.err.println("WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping."); + if (warnAboutDrops) { + System.err.println("WARNING: unable to find behavior " + obfBehaviorEntry + ". dropping mapping."); + } classMapping.removeMethodMapping(methodMapping); } @@ -168,8 +179,10 @@ public class Deobfuscator { // check inner classes for (ClassMapping innerClassMapping : Lists.newArrayList(classMapping.innerClasses())) { - if (!checkClassMapping(relatedMethodChecker, innerClassMapping)) { - System.err.println("WARNING: unable to find inner class " + EntryFactory.getObfClassEntry(m_jarIndex, classMapping) + ". dropping mapping."); + if (!checkClassMapping(relatedMethodChecker, innerClassMapping, warnAboutDrops)) { + if (warnAboutDrops) { + System.err.println("WARNING: unable to find inner class " + EntryFactory.getObfClassEntry(m_jarIndex, classMapping) + ". dropping mapping."); + } classMapping.removeInnerClassMapping(innerClassMapping); } } diff --git a/src/cuchaz/enigma/convert/ClassIdentity.java b/src/cuchaz/enigma/convert/ClassIdentity.java index d07e0a40..35667b05 100644 --- a/src/cuchaz/enigma/convert/ClassIdentity.java +++ b/src/cuchaz/enigma/convert/ClassIdentity.java @@ -16,6 +16,7 @@ import java.security.NoSuchAlgorithmException; import java.util.Enumeration; import java.util.List; import java.util.Map; +import java.util.Set; import javassist.CannotCompileException; import javassist.CtBehavior; @@ -38,6 +39,7 @@ import com.google.common.collect.HashMultiset; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multiset; +import com.google.common.collect.Sets; import cuchaz.enigma.Constants; import cuchaz.enigma.Util; @@ -67,6 +69,7 @@ public class ClassIdentity { private String m_staticInitializer; private String m_extends; private Multiset m_implements; + private Set m_stringLiterals; private Multiset m_implementations; private Multiset m_references; private String m_outer; @@ -140,6 +143,14 @@ public class ClassIdentity { m_implements.add(scrubClassName(Descriptor.toJvmName(interfaceName))); } + m_stringLiterals = Sets.newHashSet(); + ConstPool constants = c.getClassFile().getConstPool(); + for (int i=1; i a, Set b) { + int numMatches = 0; + for (String val : a) { + if (b.contains(val)) { + numMatches++; + } + } + return numMatches; + } + private int getNumMatches(Multiset a, Multiset b) { int numMatches = 0; for (String val : a) { diff --git a/src/cuchaz/enigma/convert/MappingsConverter.java b/src/cuchaz/enigma/convert/MappingsConverter.java index f38723f7..5883878c 100644 --- a/src/cuchaz/enigma/convert/MappingsConverter.java +++ b/src/cuchaz/enigma/convert/MappingsConverter.java @@ -30,7 +30,10 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.convert.ClassNamer.SidedClassNamer; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassMapping; +import cuchaz.enigma.mapping.ClassNameReplacer; +import cuchaz.enigma.mapping.FieldMapping; import cuchaz.enigma.mapping.Mappings; +import cuchaz.enigma.mapping.MethodMapping; public class MappingsConverter { @@ -129,15 +132,20 @@ public class MappingsConverter { for (Entry match : matchesByDestChainSize.get(chainSize)) { // get class info - ClassEntry sourceClassEntry = match.getKey(); - ClassEntry deobfClassEntry = sourceDeobfuscator.deobfuscateEntry(sourceClassEntry); - ClassEntry destClassEntry = match.getValue(); - List destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(destClassEntry); + ClassEntry obfSourceClassEntry = match.getKey(); + ClassEntry obfDestClassEntry = match.getValue(); + List destClassChain = destDeobfuscator.getJarIndex().getObfClassChain(obfDestClassEntry); + + ClassMapping sourceMapping = sourceDeobfuscator.getMappings().getClassByObf(obfSourceClassEntry); + if (sourceMapping == null) { + // if this class was never deobfuscated, don't try to match it + continue; + } // find out where to make the dest class mapping if (destClassChain.size() == 1) { // not an inner class, add directly to mappings - newMappings.addClassMapping(new ClassMapping(destClassEntry.getName(), deobfClassEntry.getName())); + newMappings.addClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, false)); } else { // inner class, find the outer class mapping ClassMapping destMapping = null; @@ -157,14 +165,52 @@ public class MappingsConverter { } } } - String deobfName = deobfClassEntry.isInnerClass() ? deobfClassEntry.getInnerClassName() : deobfClassEntry.getSimpleName(); - destMapping.addInnerClassMapping(new ClassMapping(destClassEntry.getName(), deobfName)); + destMapping.addInnerClassMapping(migrateClassMapping(obfDestClassEntry, sourceMapping, matches, true)); } } } return newMappings; } + private static ClassMapping migrateClassMapping(ClassEntry newObfClass, ClassMapping mapping, final Matches matches, boolean useSimpleName) { + + ClassNameReplacer replacer = new ClassNameReplacer() { + @Override + public String replace(String className) { + ClassEntry newClassEntry = matches.getUniqueMatches().get(new ClassEntry(className)); + if (newClassEntry != null) { + return newClassEntry.getName(); + } + return null; + } + }; + + ClassMapping newMapping; + String deobfName = mapping.getDeobfName(); + if (deobfName != null) { + if (useSimpleName) { + deobfName = new ClassEntry(deobfName).getSimpleName(); + } + newMapping = new ClassMapping(newObfClass.getName(), deobfName); + } else { + newMapping = new ClassMapping(newObfClass.getName()); + } + + // copy fields + for (FieldMapping fieldMapping : mapping.fields()) { + // TODO: map field obf names too... + newMapping.addFieldMapping(new FieldMapping(fieldMapping, replacer)); + } + + // copy methods + for (MethodMapping methodMapping : mapping.methods()) { + // TODO: map method obf names too... + newMapping.addMethodMapping(new MethodMapping(methodMapping, replacer)); + } + + return newMapping; + } + public static void convertMappings(Mappings mappings, BiMap changes) { // sort the changes so classes are renamed in the correct order diff --git a/src/cuchaz/enigma/gui/MatchingGui.java b/src/cuchaz/enigma/gui/MatchingGui.java index 1e618d08..85842c12 100644 --- a/src/cuchaz/enigma/gui/MatchingGui.java +++ b/src/cuchaz/enigma/gui/MatchingGui.java @@ -6,10 +6,8 @@ import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; @@ -29,9 +27,7 @@ import javax.swing.WindowConstants; import com.beust.jcommander.internal.Lists; import com.beust.jcommander.internal.Maps; -import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BiMap; -import com.google.common.collect.Multimap; import com.strobel.decompiler.languages.java.ast.CompilationUnit; import cuchaz.enigma.Constants; @@ -272,7 +268,7 @@ public class MatchingGui { m_sourceDeobfuscator.getMappings(), m_sourceDeobfuscator, m_destDeobfuscator - )); + ), false); } protected void setSourceType(SourceType val) { @@ -307,7 +303,18 @@ public class MatchingGui { } protected void setSourceClass(ClassEntry classEntry) { - setSourceClass(classEntry, null); + + Runnable onGetDestClasses = null; + if (m_advanceCheck.isSelected()) { + onGetDestClasses = new Runnable() { + @Override + public void run() { + pickBestDestClass(); + } + }; + } + + setSourceClass(classEntry, onGetDestClasses); } protected void setSourceClass(ClassEntry classEntry, final Runnable onGetDestClasses) { @@ -596,36 +603,40 @@ public class MatchingGui { setSourceClass(sourceClass, new Runnable() { @Override public void run() { - - // then, pick the best dest class - ClassEntry firstClass = null; - ScoredClassEntry bestDestClass = null; - for (ClassSelectorPackageNode packageNode : m_destClasses.packageNodes()) { - for (ClassSelectorClassNode classNode : m_destClasses.classNodes(packageNode)) { - if (firstClass == null) { - firstClass = classNode.getClassEntry(); - } - if (classNode.getClassEntry() instanceof ScoredClassEntry) { - ScoredClassEntry scoredClass = (ScoredClassEntry)classNode.getClassEntry(); - if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { - bestDestClass = scoredClass; - } - } - } - } - - // pick the entry to show - ClassEntry destClass = null; - if (bestDestClass != null) { - destClass = bestDestClass; - } else if (firstClass != null) { - destClass = firstClass; - } - - setDestClass(destClass); - m_destClasses.setSelectionClass(destClass); + pickBestDestClass(); } }); m_sourceClasses.setSelectionClass(sourceClass); } + + private void pickBestDestClass() { + + // then, pick the best dest class + ClassEntry firstClass = null; + ScoredClassEntry bestDestClass = null; + for (ClassSelectorPackageNode packageNode : m_destClasses.packageNodes()) { + for (ClassSelectorClassNode classNode : m_destClasses.classNodes(packageNode)) { + if (firstClass == null) { + firstClass = classNode.getClassEntry(); + } + if (classNode.getClassEntry() instanceof ScoredClassEntry) { + ScoredClassEntry scoredClass = (ScoredClassEntry)classNode.getClassEntry(); + if (bestDestClass == null || bestDestClass.getScore() < scoredClass.getScore()) { + bestDestClass = scoredClass; + } + } + } + } + + // pick the entry to show + ClassEntry destClass = null; + if (bestDestClass != null) { + destClass = bestDestClass; + } else if (firstClass != null) { + destClass = firstClass; + } + + setDestClass(destClass); + m_destClasses.setSelectionClass(destClass); + } } diff --git a/src/cuchaz/enigma/mapping/ArgumentMapping.java b/src/cuchaz/enigma/mapping/ArgumentMapping.java index f4d8e774..9f366a04 100644 --- a/src/cuchaz/enigma/mapping/ArgumentMapping.java +++ b/src/cuchaz/enigma/mapping/ArgumentMapping.java @@ -25,6 +25,11 @@ public class ArgumentMapping implements Serializable, Comparable { } } + // rename field types + for (FieldMapping fieldMapping : new ArrayList(m_fieldsByObf.values())) { + String oldFieldKey = getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()); + if (fieldMapping.renameObfClass(oldObfClassName, newObfClassName)) { + boolean wasRemoved = m_fieldsByObf.remove(oldFieldKey) != null; + assert (wasRemoved); + boolean wasAdded = m_fieldsByObf.put(getFieldKey(fieldMapping.getObfName(), fieldMapping.getObfType()), fieldMapping) == null; + assert (wasAdded); + } + } + // rename method signatures for (MethodMapping methodMapping : new ArrayList(m_methodsByObf.values())) { String oldMethodKey = getMethodKey(methodMapping.getObfName(), methodMapping.getObfSignature()); diff --git a/src/cuchaz/enigma/mapping/FieldMapping.java b/src/cuchaz/enigma/mapping/FieldMapping.java index 14b20dd4..55b0a195 100644 --- a/src/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/cuchaz/enigma/mapping/FieldMapping.java @@ -26,6 +26,12 @@ public class FieldMapping implements Serializable, Comparable { m_obfType = obfType; } + public FieldMapping(FieldMapping other, ClassNameReplacer obfClassNameReplacer) { + m_obfName = other.m_obfName; + m_deobfName = other.m_deobfName; + m_obfType = new Type(other.m_obfType, obfClassNameReplacer); + } + public String getObfName() { return m_obfName; } @@ -46,4 +52,24 @@ public class FieldMapping implements Serializable, Comparable { public int compareTo(FieldMapping other) { return (m_obfName + m_obfType).compareTo(other.m_obfName + other.m_obfType); } + + public boolean renameObfClass(final String oldObfClassName, final String newObfClassName) { + + // rename obf classes in the type + Type newType = new Type(m_obfType, new ClassNameReplacer() { + @Override + public String replace(String className) { + if (className.equals(oldObfClassName)) { + return newObfClassName; + } + return null; + } + }); + + if (!newType.equals(m_obfType)) { + m_obfType = newType; + return true; + } + return false; + } } diff --git a/src/cuchaz/enigma/mapping/MethodMapping.java b/src/cuchaz/enigma/mapping/MethodMapping.java index 1704428a..bf8a94f3 100644 --- a/src/cuchaz/enigma/mapping/MethodMapping.java +++ b/src/cuchaz/enigma/mapping/MethodMapping.java @@ -12,7 +12,9 @@ package cuchaz.enigma.mapping; import java.io.Serializable; import java.util.Map; -import java.util.TreeMap; +import java.util.Map.Entry; + +import com.google.common.collect.Maps; public class MethodMapping implements Serializable, Comparable { @@ -37,9 +39,19 @@ public class MethodMapping implements Serializable, Comparable { m_obfName = obfName; m_deobfName = NameValidator.validateMethodName(deobfName); m_obfSignature = obfSignature; - m_arguments = new TreeMap(); + m_arguments = Maps.newTreeMap(); } + public MethodMapping(MethodMapping other, ClassNameReplacer obfClassNameReplacer) { + m_obfName = other.m_obfName; + m_deobfName = other.m_deobfName; + m_obfSignature = new Signature(other.m_obfSignature, obfClassNameReplacer); + m_arguments = Maps.newTreeMap(); + for (Entry entry : other.m_arguments.entrySet()) { + m_arguments.put(entry.getKey(), new ArgumentMapping(entry.getValue())); + } + } + public String getObfName() { return m_obfName; } diff --git a/src/cuchaz/enigma/mapping/Signature.java b/src/cuchaz/enigma/mapping/Signature.java index 273a77b9..ea83e40e 100644 --- a/src/cuchaz/enigma/mapping/Signature.java +++ b/src/cuchaz/enigma/mapping/Signature.java @@ -39,6 +39,11 @@ public class Signature implements Serializable { } } + public Signature(Signature other) { + m_argumentTypes = Lists.newArrayList(other.m_argumentTypes); + m_returnType = new Type(other.m_returnType); + } + public Signature(Signature other, ClassNameReplacer replacer) { m_argumentTypes = Lists.newArrayList(other.m_argumentTypes); for (int i=0; i