From 6235bfc5ab85cdd471e315c52c413991fb9dbddf Mon Sep 17 00:00:00 2001 From: Thog Date: Sun, 30 Oct 2016 23:37:29 +0100 Subject: BREAKING CHANGE: Implement modifier transformer Known Bugs: - Inner class modifier isn't saved to mapping - Inner class modifier isn't applied to bytecode --- src/main/java/cuchaz/enigma/Deobfuscator.java | 21 +++++++ .../java/cuchaz/enigma/TranslatingTypeLoader.java | 2 +- .../java/cuchaz/enigma/bytecode/ClassRenamer.java | 42 ++++++++++++- .../cuchaz/enigma/bytecode/ClassTranslator.java | 25 +++++--- .../cuchaz/enigma/bytecode/InnerClassWriter.java | 39 +++++++++--- src/main/java/cuchaz/enigma/gui/Gui.java | 24 ++++++++ src/main/java/cuchaz/enigma/gui/GuiController.java | 16 +++++ .../java/cuchaz/enigma/mapping/ClassMapping.java | 71 ++++++++++++++++++---- .../java/cuchaz/enigma/mapping/FieldMapping.java | 15 ++++- src/main/java/cuchaz/enigma/mapping/Mappings.java | 10 +++ .../enigma/mapping/MappingsEnigmaReader.java | 46 +++++++++++--- .../enigma/mapping/MappingsEnigmaWriter.java | 13 ++-- .../cuchaz/enigma/mapping/MappingsRenamer.java | 18 ++++++ .../java/cuchaz/enigma/mapping/MethodMapping.java | 19 +++++- .../java/cuchaz/enigma/mapping/Translator.java | 23 +++++++ 15 files changed, 339 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java index ca38754..fe0e31d 100644 --- a/src/main/java/cuchaz/enigma/Deobfuscator.java +++ b/src/main/java/cuchaz/enigma/Deobfuscator.java @@ -607,4 +607,25 @@ public class Deobfuscator { // clear caches this.translatorCache.clear(); } + + public void changeModifier(Entry entry, Mappings.EntryModifier modifierEntry) + { + Entry obfEntry = obfuscateEntry(entry); + if (obfEntry instanceof ClassEntry) + 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 + throw new Error("Unknown entry type: " + obfEntry); + } + + public Mappings.EntryModifier getModifier(Entry obEntry) + { + Entry entry = obfuscateEntry(obEntry); + if (entry != null) + obEntry = entry; + return getTranslator(TranslationDirection.Deobfuscating).getModifier(obEntry); + } } diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java index e4c162d..7340566 100644 --- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java @@ -198,7 +198,7 @@ public class TranslatingTypeLoader implements ITypeLoader { throws IOException, NotFoundException, CannotCompileException { // reconstruct inner classes - new InnerClassWriter(this.jarIndex).write(c); + new InnerClassWriter(this.jarIndex, this.deobfuscatingTranslator).write(c); // re-get the javassist handle since we changed class names ClassEntry obfClassEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java index eb7e9a1..d49f13a 100644 --- a/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java +++ b/src/main/java/cuchaz/enigma/bytecode/ClassRenamer.java @@ -19,8 +19,9 @@ import java.util.Map; import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.ClassNameReplacer; +import cuchaz.enigma.mapping.Mappings; import cuchaz.enigma.mapping.Translator; -import javassist.CtClass; +import javassist.*; import javassist.bytecode.*; import javassist.bytecode.SignatureAttribute.*; @@ -70,6 +71,41 @@ public class ClassRenamer { } } + public static void applyModifier(Object obj, Mappings.EntryModifier modifier) + { + int mod = -1; + if (obj instanceof CtField) + mod = ((CtField) obj).getModifiers(); + else if (obj instanceof CtBehavior) + mod = ((CtBehavior) obj).getModifiers(); + else if (obj instanceof CtClass) + mod = ((CtClass) obj).getModifiers(); + + if (mod != -1) + { + switch (modifier) + { + case PRIVATE: + mod = Modifier.setPrivate(mod); + break; + case PROTECTED: + mod = Modifier.setProtected(mod); + break; + case PUBLIC: + mod = Modifier.setPublic(mod); + break; + default: + break; + } + if (obj instanceof CtField) + ((CtField) obj).setModifiers(mod); + else if (obj instanceof CtBehavior) + ((CtBehavior) obj).setModifiers(mod); + else + ((CtClass) obj).setModifiers(mod); + } + } + public static void renameClasses(CtClass c, final Translator translator) { renameClasses(c, className -> { ClassEntry entry = translator.translateEntry(new ClassEntry(className)); @@ -110,6 +146,7 @@ public class ClassRenamer { // rename the constant pool (covers ClassInfo, MethodTypeInfo, and NameAndTypeInfo) ConstPool constPool = c.getClassFile().getConstPool(); + String className = constPool.getClassName(); constPool.renameClass(map); // rename class attributes @@ -140,8 +177,9 @@ public class ClassRenamer { if (attr != null) { for (int i = 0; i < attr.tableLength(); i++) { + String innerName = attr.innerClass(i); // get the inner class full name (which has already been translated) - ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(attr.innerClass(i))); + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); if (attr.innerNameIndex(i) != 0) { // update the inner name diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java index 6c05b83..62ebfaf 100644 --- a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java @@ -11,14 +11,9 @@ package cuchaz.enigma.bytecode; import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtField; -import javassist.CtMethod; -import javassist.bytecode.ConstPool; -import javassist.bytecode.Descriptor; -import javassist.bytecode.EnclosingMethodAttribute; -import javassist.bytecode.SourceFileAttribute; +import cuchaz.enigma.mapping.Translator; +import javassist.*; +import javassist.bytecode.*; public class ClassTranslator { @@ -74,6 +69,9 @@ public class ClassTranslator { } ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + Mappings.EntryModifier modifier = this.translator.getModifier(classEntry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(c, modifier); // translate all the fields for (CtField field : c.getDeclaredFields()) { @@ -81,6 +79,10 @@ public class ClassTranslator { // translate the name FieldEntry entry = EntryFactory.getFieldEntry(field); String translatedName = this.translator.translate(entry); + modifier = this.translator.getModifier(entry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(field, modifier); + if (translatedName != null) { field.setName(translatedName); } @@ -95,6 +97,10 @@ public class ClassTranslator { BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); + modifier = this.translator.getModifier(entry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(behavior, modifier); + if (behavior instanceof CtMethod) { CtMethod method = (CtMethod) behavior; @@ -149,5 +155,8 @@ public class ClassTranslator { String sourceFile = Descriptor.toJvmName(deobfClassEntry.getOutermostClassEntry().getSimpleName()) + ".java"; c.getClassFile().addAttribute(new SourceFileAttribute(constants, sourceFile)); } + InnerClassesAttribute attr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (attr != null) + InnerClassWriter.changeModifier(c, attr, translator); } } diff --git a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java index 6d92610..6e2a29d 100644 --- a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -15,22 +15,22 @@ import com.google.common.collect.Lists; import java.util.Collection; import java.util.List; +import cuchaz.enigma.Deobfuscator; import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.mapping.BehaviorEntry; -import cuchaz.enigma.mapping.ClassEntry; -import cuchaz.enigma.mapping.EntryFactory; +import cuchaz.enigma.mapping.*; +import javassist.ClassPool; import javassist.CtClass; -import javassist.bytecode.AccessFlag; -import javassist.bytecode.ConstPool; -import javassist.bytecode.EnclosingMethodAttribute; -import javassist.bytecode.InnerClassesAttribute; +import javassist.NotFoundException; +import javassist.bytecode.*; public class InnerClassWriter { private JarIndex index; + private Translator deobfuscatorTranslator; - public InnerClassWriter(JarIndex index) { + public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) { this.index = index; + this.deobfuscatorTranslator = deobfuscatorTranslator; } public void write(CtClass c) { @@ -96,6 +96,29 @@ public class InnerClassWriter { } } + // FIXME: modiffier is not applied to inner class + public static void changeModifier(CtClass c, InnerClassesAttribute attr, Translator translator) + { + ClassPool pool = c.getClassPool(); + for (int i = 0; i < attr.tableLength(); i++) { + + String innerName = attr.innerClass(i); + // get the inner class full name (which has already been translated) + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(innerName)); + try + { + CtClass innerClass = pool.get(innerName); + Mappings.EntryModifier modifier = translator.getModifier(classEntry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(innerClass, modifier); + } catch (NotFoundException e) + { + // This shouldn't be possible in theory + //e.printStackTrace(); + } + } + } + private void writeInnerClass(InnerClassesAttribute attr, List obfClassChain, ClassEntry obfClassEntry) { // get the new inner class name diff --git a/src/main/java/cuchaz/enigma/gui/Gui.java b/src/main/java/cuchaz/enigma/gui/Gui.java index 6d9ea1d..ed18777 100644 --- a/src/main/java/cuchaz/enigma/gui/Gui.java +++ b/src/main/java/cuchaz/enigma/gui/Gui.java @@ -461,24 +461,29 @@ public class Gui { private void showClassEntry(ClassEntry entry) { addNameValue(m_infoPanel, "Class", entry.getName()); + addModifierComboBox(m_infoPanel, "Modifier", entry); } private void showFieldEntry(FieldEntry entry) { addNameValue(m_infoPanel, "Field", entry.getName()); addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); addNameValue(m_infoPanel, "Type", entry.getType().toString()); + addModifierComboBox(m_infoPanel, "Modifier", entry); } private void showMethodEntry(MethodEntry entry) { addNameValue(m_infoPanel, "Method", entry.getName()); addNameValue(m_infoPanel, "Class", entry.getClassEntry().getName()); addNameValue(m_infoPanel, "Signature", entry.getSignature().toString()); + addModifierComboBox(m_infoPanel, "Modifier", entry); + } private void showConstructorEntry(ConstructorEntry entry) { addNameValue(m_infoPanel, "Constructor", entry.getClassEntry().getName()); if (!entry.isStatic()) { addNameValue(m_infoPanel, "Signature", entry.getSignature().toString()); + addModifierComboBox(m_infoPanel, "Modifier", entry); } } @@ -501,6 +506,25 @@ public class Gui { panel.add(Utils.unboldLabel(new JLabel(value, JLabel.LEFT))); } + private JComboBox addModifierComboBox(JPanel container, String name, Entry entry) + { + if (!getController().entryIsInJar(entry)) + return null; + JPanel panel = new JPanel(); + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 6, 0)); + container.add(panel); + JLabel label = new JLabel(name + ":", JLabel.RIGHT); + label.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); + panel.add(label); + JComboBox combo = new JComboBox<>(Mappings.EntryModifier.values()); + ((JLabel)combo.getRenderer()).setHorizontalAlignment(JLabel.LEFT); + combo.setPreferredSize(new Dimension(100, label.getPreferredSize().height)); + combo.setSelectedIndex(getController().getDeobfuscator().getModifier(entry).ordinal()); + combo.addItemListener(getController()::modifierChange); + panel.add(combo); + return combo; + } + public void onCaretMove(int pos) { Token token = this.controller.getToken(pos); diff --git a/src/main/java/cuchaz/enigma/gui/GuiController.java b/src/main/java/cuchaz/enigma/gui/GuiController.java index 70fb4cf..c2e202e 100644 --- a/src/main/java/cuchaz/enigma/gui/GuiController.java +++ b/src/main/java/cuchaz/enigma/gui/GuiController.java @@ -20,6 +20,7 @@ import cuchaz.enigma.mapping.*; import cuchaz.enigma.throwables.MappingParseException; import cuchaz.enigma.utils.ReadableToken; +import java.awt.event.ItemEvent; import java.io.File; import java.io.IOException; import java.util.Collection; @@ -338,4 +339,19 @@ public class GuiController { } }.start(); } + + public Deobfuscator getDeobfuscator() + { + return deobfuscator; + } + + public void modifierChange(ItemEvent event) + { + if (event.getStateChange() == ItemEvent.SELECTED) + { + deobfuscator.changeModifier(gui.m_reference.entry, (Mappings.EntryModifier) event.getItem()); + this.isDirty = true; + refreshCurrentClass(); + } + } } diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java index 923c8ec..017a5b9 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java @@ -30,12 +30,20 @@ public class ClassMapping implements Comparable { private Map m_methodsByObf; private Map m_methodsByDeobf; private boolean isDirty; + private Mappings.EntryModifier modifier; - public ClassMapping(String obfFullName) { - this(obfFullName, null); + public ClassMapping(String obfFullName) + { + this(obfFullName, null, Mappings.EntryModifier.UNCHANGED); } - public ClassMapping(String obfFullName, String deobfName) { + public ClassMapping(String obfFullName, String deobfName) + { + this(obfFullName, deobfName, Mappings.EntryModifier.UNCHANGED); + } + + public ClassMapping(String obfFullName, String deobfName, Mappings.EntryModifier modifier) + { m_obfFullName = obfFullName; ClassEntry classEntry = new ClassEntry(obfFullName); m_obfSimpleName = classEntry.isInnerClass() ? classEntry.getInnermostClassName() : classEntry.getSimpleName(); @@ -48,6 +56,7 @@ public class ClassMapping implements Comparable { m_methodsByObf = Maps.newHashMap(); m_methodsByDeobf = Maps.newHashMap(); isDirty = true; + this.modifier = modifier; } public String getObfFullName() { @@ -186,15 +195,16 @@ public class ClassMapping implements Comparable { if (m_fieldsByObf.containsKey(obfKey)) { throw new Error("Already have mapping for " + m_obfFullName + "." + obfKey); } - String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); - if (m_fieldsByDeobf.containsKey(deobfKey)) { - throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey); + if (fieldMapping.getDeobfName() != null) { + String deobfKey = getFieldKey(fieldMapping.getDeobfName(), fieldMapping.getObfType()); + if (m_fieldsByDeobf.containsKey(deobfKey)) { + throw new Error("Already have mapping for " + m_deobfName + "." + deobfKey); + } + boolean deobfWasAdded = m_fieldsByDeobf.put(deobfKey, fieldMapping) == null; + assert (deobfWasAdded); } boolean obfWasAdded = m_fieldsByObf.put(obfKey, fieldMapping) == null; assert (obfWasAdded); - boolean deobfWasAdded = m_fieldsByDeobf.put(deobfKey, fieldMapping) == null; - assert (deobfWasAdded); - assert (m_fieldsByObf.size() == m_fieldsByDeobf.size()); this.isDirty = true; } @@ -242,12 +252,11 @@ public class ClassMapping implements Comparable { return name + ":" + type; } - public void setFieldName(String obfName, Type obfType, String deobfName) { assert (deobfName != null); FieldMapping fieldMapping = m_fieldsByObf.get(getFieldKey(obfName, obfType)); if (fieldMapping == null) { - fieldMapping = new FieldMapping(obfName, obfType, deobfName); + fieldMapping = new FieldMapping(obfName, obfType, deobfName, Mappings.EntryModifier.UNCHANGED); boolean obfWasAdded = m_fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping) == null; assert (obfWasAdded); } else { @@ -492,4 +501,44 @@ public class ClassMapping implements Comparable { { this.isDirty = false; } + + public void setModifier(Mappings.EntryModifier modifier) + { + if (this.modifier != modifier) + this.isDirty = true; + this.modifier = modifier; + } + + public Mappings.EntryModifier getModifier() + { + return modifier; + } + + public void setFieldModifier(String obfName, Type obfType, Mappings.EntryModifier modifier) { + FieldMapping fieldMapping = m_fieldsByObf.get(getFieldKey(obfName, obfType)); + if (fieldMapping == null) { + fieldMapping = new FieldMapping(obfName, obfType, null, Mappings.EntryModifier.UNCHANGED); + m_fieldsByObf.put(getFieldKey(obfName, obfType), fieldMapping); + } + + if (fieldMapping.getModifier() != modifier) + { + fieldMapping.setModifier(modifier); + this.isDirty = true; + } + } + + public void setMethodModifier(String obfName, Signature sig, Mappings.EntryModifier modifier) { + MethodMapping methodMapping = m_methodsByObf.get(getMethodKey(obfName, sig)); + if (methodMapping == null) { + methodMapping = new MethodMapping(obfName, sig, null, Mappings.EntryModifier.UNCHANGED); + m_methodsByObf.put(getMethodKey(obfName, sig), methodMapping); + } + + if (methodMapping.getModifier() != modifier) + { + methodMapping.setModifier(modifier); + this.isDirty = true; + } + } } diff --git a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java index 1b59660..e75485c 100644 --- a/src/main/java/cuchaz/enigma/mapping/FieldMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/FieldMapping.java @@ -15,16 +15,19 @@ public class FieldMapping implements Comparable, MemberMapping, MemberMapping, MemberMapping arguments; + private Mappings.EntryModifier modifier; public MethodMapping(String obfName, Signature obfSignature) { - this(obfName, obfSignature, null); + this(obfName, obfSignature, null,Mappings.EntryModifier.UNCHANGED); } public MethodMapping(String obfName, Signature obfSignature, String deobfName) { + this(obfName, obfSignature, deobfName, Mappings.EntryModifier.UNCHANGED); + } + + public MethodMapping(String obfName, Signature obfSignature, String deobfName, Mappings.EntryModifier modifier) { if (obfName == null) { throw new IllegalArgumentException("obf name cannot be null!"); } @@ -38,11 +43,13 @@ public class MethodMapping implements Comparable, MemberMapping entry : other.arguments.entrySet()) { @@ -187,4 +194,14 @@ public class MethodMapping implements Comparable, MemberMapping")) + { + if (entry instanceof ClassEntry) + return classMapping.getModifier(); + else if (entry instanceof FieldEntry) + { + FieldMapping fieldMapping = classMapping.getFieldByObf(entry.getName(), ((FieldEntry) entry).getType()); + return fieldMapping != null ? fieldMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; + } + else if (entry instanceof BehaviorEntry) + { + MethodMapping methodMapping = classMapping.getMethodByObf(entry.getName(), ((BehaviorEntry) entry).getSignature()); + return methodMapping != null ? methodMapping.getModifier() : Mappings.EntryModifier.UNCHANGED; + } + else + throw new Error("Unknown entry type: " + entry.getClass().getName()); + } + return Mappings.EntryModifier.UNCHANGED; + } } -- cgit v1.2.3