From 2b2249e873c4adfd2dd6e8f1f2489ccd9f6aa021 Mon Sep 17 00:00:00 2001 From: gegy1000 Date: Sat, 19 May 2018 17:02:46 +0200 Subject: Initial port to ASM --- src/main/java/cuchaz/enigma/Deobfuscator.java | 225 ++++++++++++++------------ 1 file changed, 119 insertions(+), 106 deletions(-) (limited to 'src/main/java/cuchaz/enigma/Deobfuscator.java') diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java index 1e99af2..e1454c7 100644 --- a/src/main/java/cuchaz/enigma/Deobfuscator.java +++ b/src/main/java/cuchaz/enigma/Deobfuscator.java @@ -31,50 +31,55 @@ import cuchaz.enigma.bytecode.ClassPublifier; import cuchaz.enigma.mapping.*; import cuchaz.enigma.throwables.IllegalNameException; import cuchaz.enigma.utils.Utils; -import javassist.CtClass; -import javassist.bytecode.Descriptor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; import java.io.*; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; public class Deobfuscator { + private final ReferencedEntryPool entryPool = new ReferencedEntryPool(); private final JarFile jar; + private final ParsedJar parsedJar; private final DecompilerSettings settings; private final JarIndex jarIndex; private final MappingsRenamer renamer; private final Map translatorCache; private Mappings mappings; - public Deobfuscator(JarFile jar) { + public Deobfuscator(JarFile jar) throws IOException { this.jar = jar; + this.parsedJar = new ParsedJar(jar); // build the jar index - this.jarIndex = new JarIndex(); - this.jarIndex.indexJar(this.jar, true); + this.jarIndex = new JarIndex(entryPool); + this.jarIndex.indexJar(this.parsedJar, true); // config the decompiler this.settings = DecompilerSettings.javaDefaults(); this.settings.setMergeVariables(Utils.getSystemPropertyAsBoolean("enigma.mergeVariables", true)); this.settings.setForceExplicitImports(Utils.getSystemPropertyAsBoolean("enigma.forceExplicitImports", true)); this.settings.setForceExplicitTypeArguments( - Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); + Utils.getSystemPropertyAsBoolean("enigma.forceExplicitTypeArguments", true)); // DEBUG this.settings.setShowDebugLineNumbers(Utils.getSystemPropertyAsBoolean("enigma.showDebugLineNumbers", false)); this.settings.setShowSyntheticMembers(Utils.getSystemPropertyAsBoolean("enigma.showSyntheticMembers", false)); // init defaults this.translatorCache = Maps.newTreeMap(); - this.renamer = new MappingsRenamer(this.jarIndex, null); + this.renamer = new MappingsRenamer(this.jarIndex, null, this.entryPool); // init mappings setMappings(new Mappings()); } - public JarFile getJar() { - return this.jar; + public ParsedJar getJar() { + return this.parsedJar; } public String getJarName() { @@ -102,16 +107,16 @@ public class Deobfuscator { MappingsChecker checker = new MappingsChecker(this.jarIndex); checker.dropBrokenMappings(val); if (warnAboutDrops) { - for (java.util.Map.Entry mapping : checker.getDroppedClassMappings().entrySet()) { + for (Map.Entry mapping : checker.getDroppedClassMappings().entrySet()) { System.out.println("WARNING: Couldn't find class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); } - for (java.util.Map.Entry mapping : checker.getDroppedInnerClassMappings().entrySet()) { + for (Map.Entry mapping : checker.getDroppedInnerClassMappings().entrySet()) { System.out.println("WARNING: Couldn't find inner class entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); } - for (java.util.Map.Entry mapping : checker.getDroppedFieldMappings().entrySet()) { + for (Map.Entry mapping : checker.getDroppedFieldMappings().entrySet()) { System.out.println("WARNING: Couldn't find field entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); } - for (java.util.Map.Entry mapping : checker.getDroppedMethodMappings().entrySet()) { + for (Map.Entry mapping : checker.getDroppedMethodMappings().entrySet()) { System.out.println("WARNING: Couldn't find behavior entry " + mapping.getKey() + " (" + mapping.getValue().getDeobfName() + ") in jar. Mapping was dropped."); } } @@ -123,7 +128,7 @@ public class Deobfuscator { public Translator getTranslator(TranslationDirection direction) { return this.translatorCache.computeIfAbsent(direction, - k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex())); + k -> this.mappings.getTranslator(direction, this.jarIndex.getTranslationIndex())); } public void getSeparatedClasses(List obfClasses, List deobfClasses) { @@ -150,10 +155,11 @@ public class Deobfuscator { public TranslatingTypeLoader createTypeLoader() { return new TranslatingTypeLoader( - this.jar, - this.jarIndex, - getTranslator(TranslationDirection.Obfuscating), - getTranslator(TranslationDirection.Deobfuscating) + this.parsedJar, + this.jarIndex, + this.entryPool, + getTranslator(TranslationDirection.OBFUSCATING), + getTranslator(TranslationDirection.DEOBFUSCATING) ); } @@ -172,15 +178,15 @@ public class Deobfuscator { deobfClassName = classMapping.getDeobfName(); } - // set the type loader + // set the desc loader TranslatingTypeLoader loader = createTypeLoader(); this.settings.setTypeLoader(loader); - // see if procyon can find the type + // see if procyon can find the desc TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); if (type == null) { - throw new Error(String.format("Unable to find type: %s (deobf: %s)\nTried class names: %s", - className, deobfClassName, loader.getClassNamesToTry(deobfClassName) + throw new Error(String.format("Unable to find desc: %s (deobf: %s)\nTried class names: %s", + className, deobfClassName, loader.getClassNamesToTry(deobfClassName) )); } TypeDefinition resolvedType = type.resolve(); @@ -208,7 +214,7 @@ public class Deobfuscator { } else { index = new SourceIndex(source); } - sourceTree.acceptVisitor(new SourceIndexVisitor(), index); + sourceTree.acceptVisitor(new SourceIndexVisitor(entryPool), index); // DEBUG // sourceTree.acceptVisitor( new TreeDumpVisitor( new File( "tree.txt" ) ), null ); @@ -221,10 +227,10 @@ public class Deobfuscator { Entry obfEntry = obfuscateEntry(deobfReference.entry); // try to resolve the class - ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryClass(obfEntry); - if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getClassEntry())) { + ClassEntry resolvedObfClassEntry = this.jarIndex.getTranslationIndex().resolveEntryOwner(obfEntry); + if (resolvedObfClassEntry != null && !resolvedObfClassEntry.equals(obfEntry.getOwnerClassEntry())) { // change the class of the entry - obfEntry = obfEntry.cloneToNewClass(resolvedObfClassEntry); + obfEntry = obfEntry.updateOwnership(resolvedObfClassEntry); // save the new deobfuscated reference deobfReference.entry = deobfuscateEntry(obfEntry); @@ -305,18 +311,14 @@ public class Deobfuscator { } } - private boolean isBehaviorProvider(ClassEntry classObfEntry, BehaviorEntry behaviorEntry) { - if (behaviorEntry instanceof MethodEntry) { - MethodEntry methodEntry = (MethodEntry) behaviorEntry; + private boolean isMethodProvider(ClassEntry classObfEntry, MethodEntry methodEntry) { + Set classEntries = new HashSet<>(); + addAllPotentialAncestors(classEntries, classObfEntry); - Set classEntries = new HashSet<>(); - addAllPotentialAncestors(classEntries, classObfEntry); - - for (ClassEntry parentEntry : classEntries) { - MethodEntry ancestorMethodEntry = new MethodEntry(parentEntry, methodEntry.getName(), methodEntry.getSignature()); - if (jarIndex.containsObfBehavior(ancestorMethodEntry)) { - return false; - } + for (ClassEntry parentEntry : classEntries) { + MethodEntry ancestorMethodEntry = entryPool.getMethod(parentEntry, methodEntry.getName(), methodEntry.getDesc()); + if (jarIndex.containsObfMethod(ancestorMethodEntry)) { + return false; } } @@ -332,7 +334,7 @@ public class Deobfuscator { for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) { progress.onProgress(i++, classMapping.getDeobfName()); rebuildMethodNames(classMapping, renameClassMap); - for(ClassMapping innerClass : classMapping.innerClasses()){ + for (ClassMapping innerClass : classMapping.innerClasses()) { rebuildMethodNames(innerClass, renameClassMap); } } @@ -356,29 +358,29 @@ public class Deobfuscator { try { rename(obfEntry, name); - } catch (IllegalNameException exception) - { + } catch (IllegalNameException exception) { System.out.println("WARNING: " + exception.getMessage()); } } } } - private void rebuildMethodNames(ClassMapping classMapping, Map> renameClassMap){ + private void rebuildMethodNames(ClassMapping classMapping, Map> renameClassMap) { Map renameEntries = new HashMap<>(); for (MethodMapping methodMapping : Lists.newArrayList(classMapping.methods())) { ClassEntry classObfEntry = classMapping.getObfEntry(); - BehaviorEntry obfEntry = methodMapping.getObfEntry(classObfEntry); + MethodEntry obfEntry = methodMapping.getObfEntry(classObfEntry); - if (isBehaviorProvider(classObfEntry, obfEntry)) { - if (hasDeobfuscatedName(obfEntry) && !(obfEntry instanceof ConstructorEntry) - && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) { + if (isMethodProvider(classObfEntry, obfEntry)) { + if (hasDeobfuscatedName(obfEntry) + && !(methodMapping.getDeobfName().equals(methodMapping.getObfName()))) { renameEntries.put(obfEntry, methodMapping.getDeobfName()); } - for (ArgumentMapping argumentMapping : Lists.newArrayList(methodMapping.arguments())) { - Entry argObfEntry = argumentMapping.getObfEntry(obfEntry); + ArrayList arguments = Lists.newArrayList(methodMapping.arguments()); + for (LocalVariableMapping localVariableMapping : arguments) { + Entry argObfEntry = localVariableMapping.getObfEntry(obfEntry); if (hasDeobfuscatedName(argObfEntry)) { renameEntries.put(argObfEntry, deobfuscateEntry(argObfEntry).getName()); } @@ -390,45 +392,44 @@ public class Deobfuscator { } - public void writeJar(File out, ProgressListener progress) { - transformJar(out, progress, createTypeLoader()::transformClass); + transformJar(out, progress, createTypeLoader()::createTransformer); } public void protectifyJar(File out, ProgressListener progress) { - transformJar(out, progress, ClassProtectifier::protectify); + transformJar(out, progress, (node, writer) -> node.accept(new ClassProtectifier(Opcodes.ASM5, writer))); } public void publifyJar(File out, ProgressListener progress) { - transformJar(out, progress, ClassPublifier::publify); + transformJar(out, progress, (node, writer) -> node.accept(new ClassPublifier(Opcodes.ASM5, writer))); } public void transformJar(File out, ProgressListener progress, ClassTransformer transformer) { try (JarOutputStream outJar = new JarOutputStream(new FileOutputStream(out))) { if (progress != null) { - progress.init(JarClassIterator.getClassEntries(this.jar).size(), "Transforming classes..."); + progress.init(parsedJar.getClassCount(), "Transforming classes..."); } - int i = 0; - for (CtClass c : JarClassIterator.classes(this.jar)) { + AtomicInteger i = new AtomicInteger(); + parsedJar.visit(node -> { if (progress != null) { - progress.onProgress(i++, c.getName()); + progress.onProgress(i.getAndIncrement(), node.name); } try { - c = transformer.transform(c); - outJar.putNextEntry(new JarEntry(c.getName().replace('.', '/') + ".class")); - outJar.write(c.toBytecode()); + ClassWriter writer = new ClassWriter(0); + transformer.write(node, writer); + outJar.putNextEntry(new JarEntry(node.name.replace('.', '/') + ".class")); + outJar.write(writer.toByteArray()); outJar.closeEntry(); } catch (Throwable t) { - throw new Error("Unable to transform class " + c.getName(), t); + throw new Error("Unable to transform class " + node.name, t); } - } + }); + if (progress != null) { - progress.onProgress(i, "Done!"); + progress.onProgress(i.get(), "Done!"); } - - outJar.close(); } catch (IOException ex) { throw new Error("Unable to write to Jar file!"); } @@ -438,14 +439,22 @@ public class Deobfuscator { if (deobfEntry == null) { return null; } - return getTranslator(TranslationDirection.Obfuscating).translateEntry(deobfEntry); + T translatedEntry = getTranslator(TranslationDirection.OBFUSCATING).getTranslatedEntry(deobfEntry); + if (translatedEntry == null) { + return deobfEntry; + } + return translatedEntry; } public T deobfuscateEntry(T obfEntry) { if (obfEntry == null) { return null; } - return getTranslator(TranslationDirection.Deobfuscating).translateEntry(obfEntry); + T translatedEntry = getTranslator(TranslationDirection.DEOBFUSCATING).getTranslatedEntry(obfEntry); + if (translatedEntry == null) { + return obfEntry; + } + return translatedEntry; } public EntryReference obfuscateReference(EntryReference deobfReference) { @@ -473,7 +482,7 @@ public class Deobfuscator { // HACKHACK: Object methods are not obfuscated identifiers MethodEntry obfMethodEntry = (MethodEntry) obfEntry; String name = obfMethodEntry.getName(); - String sig = obfMethodEntry.getSignature().toString(); + String sig = obfMethodEntry.getDesc().toString(); if (name.equals("clone") && sig.equals("()Ljava/lang/Object;")) { return false; } else if (name.equals("equals") && sig.equals("(Ljava/lang/Object;)Z")) { @@ -499,7 +508,7 @@ public class Deobfuscator { } // FIXME: HACK EVEN MORE HACK! - if (hack && this.jarIndex.containsObfEntry(obfEntry.getClassEntry())) + if (hack && this.jarIndex.containsObfEntry(obfEntry.getOwnerClassEntry())) return true; } @@ -515,27 +524,24 @@ public class Deobfuscator { } public boolean hasDeobfuscatedName(Entry obfEntry) { - Translator translator = getTranslator(TranslationDirection.Deobfuscating); + Translator translator = getTranslator(TranslationDirection.DEOBFUSCATING); if (obfEntry instanceof ClassEntry) { ClassEntry obfClass = (ClassEntry) obfEntry; List mappingChain = this.mappings.getClassMappingChain(obfClass); ClassMapping classMapping = mappingChain.get(mappingChain.size() - 1); return classMapping != null && classMapping.getDeobfName() != null; } else if (obfEntry instanceof FieldEntry) { - return translator.translate((FieldEntry) obfEntry) != null; + return translator.getTranslatedField((FieldEntry) obfEntry) != null; } else if (obfEntry instanceof MethodEntry) { - return translator.translate((MethodEntry) obfEntry) != null; - } else if (obfEntry instanceof ConstructorEntry) { - // constructors have no names - return false; - } else if (obfEntry instanceof ArgumentEntry) { - return translator.translate((ArgumentEntry) obfEntry) != null; + MethodEntry methodEntry = (MethodEntry) obfEntry; + if (methodEntry.isConstructor()) { + return false; + } + return translator.getTranslatedMethod(methodEntry) != null; } else if (obfEntry instanceof LocalVariableEntry) { - // TODO: Implement it - //return translator.translate((LocalVariableEntry)obfEntry) != null; - return false; + return translator.getTranslatedVariable((LocalVariableEntry) obfEntry) != null; } else { - throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); + throw new Error("Unknown entry desc: " + obfEntry.getClass().getName()); } } @@ -547,19 +553,18 @@ public class Deobfuscator { public void rename(Entry obfEntry, String newName, boolean clearCache) { if (obfEntry instanceof ClassEntry) { - this.renamer.setClassName((ClassEntry) obfEntry, Descriptor.toJvmName(newName)); + this.renamer.setClassName((ClassEntry) obfEntry, newName); } else if (obfEntry instanceof FieldEntry) { this.renamer.setFieldName((FieldEntry) obfEntry, newName); } else if (obfEntry instanceof MethodEntry) { + if (((MethodEntry) obfEntry).isConstructor()) { + throw new IllegalArgumentException("Cannot rename constructors"); + } this.renamer.setMethodTreeName((MethodEntry) obfEntry, newName); - } else if (obfEntry instanceof ConstructorEntry) { - throw new IllegalArgumentException("Cannot rename constructors"); - } else if (obfEntry instanceof ArgumentEntry) { - this.renamer.setArgumentTreeName((ArgumentEntry) obfEntry, newName); } else if (obfEntry instanceof LocalVariableEntry) { - // TODO: Implement it + this.renamer.setLocalVariableTreeName((LocalVariableEntry) obfEntry, newName); } else { - throw new Error("Unknown entry type: " + obfEntry.getClass().getName()); + throw new Error("Unknown entry desc: " + obfEntry.getClass().getName()); } // clear caches @@ -573,13 +578,14 @@ public class Deobfuscator { } else if (obfEntry instanceof FieldEntry) { this.renamer.removeFieldMapping((FieldEntry) obfEntry); } else if (obfEntry instanceof MethodEntry) { + if (((MethodEntry) obfEntry).isConstructor()) { + throw new IllegalArgumentException("Cannot rename constructors"); + } this.renamer.removeMethodTreeMapping((MethodEntry) obfEntry); - } else if (obfEntry instanceof ConstructorEntry) { - throw new IllegalArgumentException("Cannot rename constructors"); - } else if (obfEntry instanceof ArgumentEntry) { - this.renamer.removeArgumentMapping((ArgumentEntry) obfEntry); + } else if (obfEntry instanceof LocalVariableEntry) { + this.renamer.removeLocalVariableMapping((LocalVariableEntry) obfEntry); } else { - throw new Error("Unknown entry type: " + obfEntry); + throw new Error("Unknown entry desc: " + obfEntry); } // clear caches @@ -592,15 +598,15 @@ public class Deobfuscator { } else if (obfEntry instanceof FieldEntry) { this.renamer.markFieldAsDeobfuscated((FieldEntry) obfEntry); } else if (obfEntry instanceof MethodEntry) { - this.renamer.markMethodTreeAsDeobfuscated((MethodEntry) obfEntry); - } else if (obfEntry instanceof ConstructorEntry) { - throw new IllegalArgumentException("Cannot rename constructors"); - } else if (obfEntry instanceof ArgumentEntry) { - this.renamer.markArgumentAsDeobfuscated((ArgumentEntry) obfEntry); + MethodEntry methodEntry = (MethodEntry) obfEntry; + if (methodEntry.isConstructor()) { + throw new IllegalArgumentException("Cannot rename constructors"); + } + this.renamer.markMethodTreeAsDeobfuscated(methodEntry); } else if (obfEntry instanceof LocalVariableEntry) { - // TODO: Implement it + this.renamer.markArgumentAsDeobfuscated((LocalVariableEntry) obfEntry); } else { - throw new Error("Unknown entry type: " + obfEntry); + throw new Error("Unknown entry desc: " + obfEntry); } // clear caches @@ -613,17 +619,24 @@ public class Deobfuscator { this.renamer.setClassModifier((ClassEntry) obfEntry, modifierEntry); else if (obfEntry instanceof FieldEntry) this.renamer.setFieldModifier((FieldEntry) obfEntry, modifierEntry); - else if (obfEntry instanceof BehaviorEntry) - this.renamer.setMethodModifier((BehaviorEntry) obfEntry, modifierEntry); + else if (obfEntry instanceof MethodEntry) + this.renamer.setMethodModifier((MethodEntry) obfEntry, modifierEntry); else - throw new Error("Unknown entry type: " + obfEntry); + throw new Error("Unknown entry desc: " + obfEntry); } - public Mappings.EntryModifier getModifier(Entry obEntry) { - Entry entry = obfuscateEntry(obEntry); + public Mappings.EntryModifier getModifier(Entry obfEntry) { + Entry entry = obfuscateEntry(obfEntry); if (entry != null) - obEntry = entry; - return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry); + obfEntry = entry; + if (obfEntry instanceof ClassEntry) + return this.renamer.getClassModifier((ClassEntry) obfEntry); + else if (obfEntry instanceof FieldEntry) + return this.renamer.getFieldModifier((FieldEntry) obfEntry); + else if (obfEntry instanceof MethodEntry) + return this.renamer.getMethodModfifier((MethodEntry) obfEntry); + else + throw new Error("Unknown entry desc: " + obfEntry); } public interface ProgressListener { @@ -633,6 +646,6 @@ public class Deobfuscator { } public interface ClassTransformer { - CtClass transform(CtClass c) throws Exception; + void write(ClassNode node, ClassWriter writer); } } -- cgit v1.2.3