From b280104d2f926ab74772cef2bf1602663cefa312 Mon Sep 17 00:00:00 2001 From: Thog Date: Tue, 16 May 2017 00:24:29 +0200 Subject: Remove the converter + some reorganization --- .../cuchaz/enigma/bytecode/ClassTranslator.java | 165 --------------------- .../cuchaz/enigma/bytecode/InnerClassWriter.java | 151 ------------------- .../enigma/bytecode/LocalVariableRenamer.java | 150 ------------------- .../enigma/bytecode/MethodParameterWriter.java | 67 --------- .../bytecode/translators/ClassTranslator.java | 161 ++++++++++++++++++++ .../bytecode/translators/InnerClassWriter.java | 144 ++++++++++++++++++ .../translators/LocalVariableTranslator.java | 142 ++++++++++++++++++ .../translators/MethodParameterTranslator.java | 62 ++++++++ 8 files changed, 509 insertions(+), 533 deletions(-) delete mode 100644 src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java delete mode 100644 src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java create mode 100644 src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java (limited to 'src/main/java/cuchaz/enigma/bytecode') diff --git a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java deleted file mode 100644 index 1ebf656..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/ClassTranslator.java +++ /dev/null @@ -1,165 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.bytecode; - -import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.CtField; -import javassist.CtMethod; -import javassist.bytecode.*; - -public class ClassTranslator { - - private Translator translator; - - public ClassTranslator(Translator translator) { - this.translator = translator; - } - - public void translate(CtClass c) { - - // NOTE: the order of these translations is very important - - // translate all the field and method references in the code by editing the constant pool - ConstPool constants = c.getClassFile().getConstPool(); - ConstPoolEditor editor = new ConstPoolEditor(constants); - for (int i = 1; i < constants.getSize(); i++) { - switch (constants.getTag(i)) { - - case ConstPool.CONST_Fieldref: { - - // translate the name and type - FieldEntry entry = EntryFactory.getFieldEntry( - Descriptor.toJvmName(constants.getFieldrefClassName(i)), - constants.getFieldrefName(i), - constants.getFieldrefType(i) - ); - FieldEntry translatedEntry = this.translator.translateEntry(entry); - if (!entry.equals(translatedEntry)) { - editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); - } - } - break; - - case ConstPool.CONST_Methodref: - case ConstPool.CONST_InterfaceMethodref: { - - // translate the name and type (ie signature) - BehaviorEntry entry = EntryFactory.getBehaviorEntry( - Descriptor.toJvmName(editor.getMemberrefClassname(i)), - editor.getMemberrefName(i), - editor.getMemberrefType(i) - ); - BehaviorEntry translatedEntry = this.translator.translateEntry(entry); - if (!entry.equals(translatedEntry)) { - editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); - } - } - break; - default: - break; - } - } - - 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()) { - - // 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); - } - - // translate the type - Type translatedType = this.translator.translateType(entry.getType()); - field.getFieldInfo().setDescriptor(translatedType.toString()); - } - - // translate all the methods and constructors - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - - 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; - - // translate the name - String translatedName = this.translator.translate(entry); - if (translatedName != null) { - method.setName(translatedName); - } - } - - if (entry.getSignature() != null) { - // translate the signature - Signature translatedSignature = this.translator.translateSignature(entry.getSignature()); - behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); - } - } - - // translate the EnclosingMethod attribute - EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); - if (enclosingMethodAttr != null) { - - if (enclosingMethodAttr.methodIndex() == 0) { - BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); - BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); - c.getClassFile().addAttribute(new EnclosingMethodAttribute( - constants, - deobfBehaviorEntry.getClassName() - )); - } else { - BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( - Descriptor.toJvmName(enclosingMethodAttr.className()), - enclosingMethodAttr.methodName(), - enclosingMethodAttr.methodDescriptor() - ); - BehaviorEntry deobfBehaviorEntry = this.translator.translateEntry(obfBehaviorEntry); - c.getClassFile().addAttribute(new EnclosingMethodAttribute( - constants, - deobfBehaviorEntry.getClassName(), - deobfBehaviorEntry.getName(), - deobfBehaviorEntry.getSignature().toString() - )); - } - } - - // translate all the class names referenced in the code - // the above code only changed method/field/reference names and types, but not the rest of the class references - ClassRenamer.renameClasses(c, this.translator); - - // translate the source file attribute too - ClassEntry deobfClassEntry = this.translator.translateEntry(classEntry); - if (deobfClassEntry != null) { - 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 deleted file mode 100644 index f1c3dd7..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/InnerClassWriter.java +++ /dev/null @@ -1,151 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.bytecode; - -import com.google.common.collect.Lists; -import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.mapping.*; -import javassist.ClassPool; -import javassist.CtClass; -import javassist.NotFoundException; -import javassist.bytecode.*; - -import java.util.Collection; -import java.util.List; - -public class InnerClassWriter { - - private JarIndex index; - private Translator deobfuscatorTranslator; - - public InnerClassWriter(JarIndex index, Translator deobfuscatorTranslator) { - this.index = index; - this.deobfuscatorTranslator = deobfuscatorTranslator; - } - - // FIXME: modifier 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(); - } - } - } - - public void write(CtClass c) { - - // don't change anything if there's already an attribute there - InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); - if (oldAttr != null) { - // bail! - return; - } - - ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); - List obfClassChain = this.index.getObfClassChain(obfClassEntry); - - boolean isInnerClass = obfClassChain.size() > 1; - if (isInnerClass) { - - // it's an inner class, rename it to the fully qualified name - c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); - - BehaviorEntry caller = this.index.getAnonymousClassCaller(obfClassEntry); - if (caller != null) { - - // write the enclosing method attribute - if (caller.getName().equals("")) { - c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); - } else { - c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); - } - } - } - - // does this class have any inner classes? - Collection obfInnerClassEntries = this.index.getInnerClasses(obfClassEntry); - - if (isInnerClass || !obfInnerClassEntries.isEmpty()) { - - // create an inner class attribute - InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); - c.getClassFile().addAttribute(attr); - - // write the ancestry, but not the outermost class - for (int i = 1; i < obfClassChain.size(); i++) { - ClassEntry obfInnerClassEntry = obfClassChain.get(i); - writeInnerClass(attr, obfClassChain, obfInnerClassEntry); - - // update references to use the fully qualified inner class name - c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); - } - - // write the inner classes - for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { - - // extend the class chain - List extendedObfClassChain = Lists.newArrayList(obfClassChain); - extendedObfClassChain.add(obfInnerClassEntry); - - writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); - - // update references to use the fully qualified inner class name - c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); - } - } - } - - private void writeInnerClass(InnerClassesAttribute attr, List obfClassChain, ClassEntry obfClassEntry) { - - // get the new inner class name - ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); - ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); - - // here's what the JVM spec says about the InnerClasses attribute - // append(inner, parent, 0 if anonymous else simple name, flags); - - // update the attribute with this inner class - ConstPool constPool = attr.getConstPool(); - int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); - int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); - int innerClassNameIndex = 0; - int accessFlags = AccessFlag.PUBLIC; - // TODO: need to figure out if we can put static or not - if (!this.index.isAnonymousClass(obfClassEntry)) { - innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); - } - - attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); - - /* DEBUG - System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", - obfClassEntry, - attr.innerClass(attr.tableLength() - 1), - attr.outerClass(attr.tableLength() - 1), - attr.innerName(attr.tableLength() - 1), - Constants.NonePackage + "/" + obfInnerClassName, - obfClassEntry.getName() - )); - */ - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java b/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java deleted file mode 100644 index 878e30a..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/LocalVariableRenamer.java +++ /dev/null @@ -1,150 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.bytecode; - -import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.bytecode.*; - -public class LocalVariableRenamer { - - private Translator translator; - - public LocalVariableRenamer(Translator translator) { - this.translator = translator; - } - - public void rename(CtClass c) { - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - - // if there's a local variable table, just rename everything to v1, v2, v3, ... for now - CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); - if (codeAttribute == null) { - continue; - } - - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - ConstPool constants = c.getClassFile().getConstPool(); - - LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); - if (table != null) { - renameLVT(behaviorEntry, constants, table, c); - } - - LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag); - if (typeTable != null) { - renameLVTT(typeTable, table); - } - } - } - - // DEBUG - @SuppressWarnings("unused") - private void dumpTable(LocalVariableAttribute table) { - for (int i = 0; i < table.tableLength(); i++) { - System.out.println(String.format("\t%d (%d): %s %s", - i, table.index(i), table.variableName(i), table.descriptor(i) - )); - } - } - - private void renameLVT(BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table, CtClass ctClass) { - - // skip empty tables - if (table.tableLength() <= 0) { - return; - } - - // where do we start counting variables? - int starti = 0; - if (table.variableName(0).equals("this")) { - // skip the "this" variable - starti++; - } - - // rename method arguments first - int numArgs = 0; - if (behaviorEntry.getSignature() != null) { - numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); - boolean isNestedClassConstructor = false; - - // If the behavior is a constructor and if it have more than one arg, it's probably from a nested! - if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) { - // Get the first arg type - Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0); - - // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class - if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) { - isNestedClassConstructor = true; - numArgs--; - } - } - - for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { - int argi = i - starti; - if (ctClass.isEnum()) - argi += 2; - if (behaviorEntry.getClassEntry().getName().contains("ahd") && behaviorEntry instanceof ConstructorEntry) - System.out.println(behaviorEntry.getClassEntry() + " " + i); - String argName = this.translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); - if (argName == null) { - int argIndex = isNestedClassConstructor ? argi + 1 : argi; - if (ctClass.isEnum()) - argIndex -= 2; - Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argIndex); - // Unfortunately each of these have different name getters, so they have different code paths - if (argType.isPrimitive()) { - Type.Primitive argCls = argType.getPrimitive(); - argName = "a" + argCls.name() + (argIndex + 1); - } else if (argType.isArray()) { - // List types would require this whole block again, so just go with aListx - argName = "aList" + (argIndex + 1); - } else if (argType.isClass()) { - ClassEntry argClsTrans = this.translator.translateEntry(argType.getClassEntry()); - argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argIndex + 1); - } else { - argName = "a" + (argIndex + 1); - } - } - renameVariable(table, i, constants.addUtf8Info(argName)); - } - } - - // then rename the rest of the args, if any - for (int i = starti + numArgs; i < table.tableLength(); i++) { - int firstIndex = Math.min(table.index(starti + numArgs), table.index(i)); - renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); - } - } - - private void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { - // rename args to the same names as in the LVT - for (int i = 0; i < typeTable.tableLength(); i++) { - renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); - } - } - - private void renameVariable(LocalVariableAttribute table, int i, int stringId) { - // based off of LocalVariableAttribute.nameIndex() - ByteArray.write16bit(stringId, table.get(), i * 10 + 6); - } - - private int getNameIndex(LocalVariableAttribute table, int index) { - for (int i = 0; i < table.tableLength(); i++) { - if (table.index(i) == index) { - return table.nameIndex(i); - } - } - return 0; - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java b/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java deleted file mode 100644 index d63572e..0000000 --- a/src/main/java/cuchaz/enigma/bytecode/MethodParameterWriter.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2015 Jeff Martin. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Lesser General Public - * License v3.0 which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/lgpl.html - *

- * Contributors: - * Jeff Martin - initial API and implementation - ******************************************************************************/ - -package cuchaz.enigma.bytecode; - -import cuchaz.enigma.mapping.*; -import javassist.CtBehavior; -import javassist.CtClass; -import javassist.bytecode.CodeAttribute; -import javassist.bytecode.LocalVariableAttribute; - -import java.util.ArrayList; -import java.util.List; - -public class MethodParameterWriter { - - private Translator translator; - - public MethodParameterWriter(Translator translator) { - this.translator = translator; - } - - public void writeMethodArguments(CtClass c) { - - // Procyon will read method arguments from the "MethodParameters" attribute, so write those - for (CtBehavior behavior : c.getDeclaredBehaviors()) { - - // if there's a local variable table here, don't write a MethodParameters attribute - // let the local variable writer deal with it instead - // procyon starts doing really weird things if we give it both attributes - CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); - if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) { - continue; - } - - BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); - - // get the number of arguments - Signature signature = behaviorEntry.getSignature(); - if (signature == null) { - // static initializers have no signatures, or arguments - continue; - } - int numParams = signature.getArgumentTypes().size(); - if (numParams <= 0) { - continue; - } - - // get the list of argument names - List names = new ArrayList<>(numParams); - for (int i = 0; i < numParams; i++) { - names.add(this.translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); - } - - // save the mappings to the class - MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); - } - } -} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java new file mode 100644 index 0000000..4ac5a8b --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/ClassTranslator.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.bytecode.translators; + +import cuchaz.enigma.bytecode.ClassRenamer; +import cuchaz.enigma.bytecode.ConstPoolEditor; +import cuchaz.enigma.mapping.*; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.CtField; +import javassist.CtMethod; +import javassist.bytecode.*; + +public class ClassTranslator { + + public static void translate(Translator translator, CtClass c) { + + // NOTE: the order of these translations is very important + + // translate all the field and method references in the code by editing the constant pool + ConstPool constants = c.getClassFile().getConstPool(); + ConstPoolEditor editor = new ConstPoolEditor(constants); + for (int i = 1; i < constants.getSize(); i++) { + switch (constants.getTag(i)) { + + case ConstPool.CONST_Fieldref: { + + // translate the name and type + FieldEntry entry = EntryFactory.getFieldEntry( + Descriptor.toJvmName(constants.getFieldrefClassName(i)), + constants.getFieldrefName(i), + constants.getFieldrefType(i) + ); + FieldEntry translatedEntry = translator.translateEntry(entry); + if (!entry.equals(translatedEntry)) { + editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getType().toString()); + } + } + break; + + case ConstPool.CONST_Methodref: + case ConstPool.CONST_InterfaceMethodref: { + + // translate the name and type (ie signature) + BehaviorEntry entry = EntryFactory.getBehaviorEntry( + Descriptor.toJvmName(editor.getMemberrefClassname(i)), + editor.getMemberrefName(i), + editor.getMemberrefType(i) + ); + BehaviorEntry translatedEntry = translator.translateEntry(entry); + if (!entry.equals(translatedEntry)) { + editor.changeMemberrefNameAndType(i, translatedEntry.getName(), translatedEntry.getSignature().toString()); + } + } + break; + default: + break; + } + } + + ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName())); + Mappings.EntryModifier modifier = translator.getModifier(classEntry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(c, modifier); + + // translate all the fields + for (CtField field : c.getDeclaredFields()) { + + // translate the name + FieldEntry entry = EntryFactory.getFieldEntry(field); + String translatedName = translator.translate(entry); + modifier = translator.getModifier(entry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(field, modifier); + + if (translatedName != null) { + field.setName(translatedName); + } + + // translate the type + Type translatedType = translator.translateType(entry.getType()); + field.getFieldInfo().setDescriptor(translatedType.toString()); + } + + // translate all the methods and constructors + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + + BehaviorEntry entry = EntryFactory.getBehaviorEntry(behavior); + + modifier = translator.getModifier(entry); + if (modifier != null && modifier != Mappings.EntryModifier.UNCHANGED) + ClassRenamer.applyModifier(behavior, modifier); + + if (behavior instanceof CtMethod) { + CtMethod method = (CtMethod) behavior; + + // translate the name + String translatedName = translator.translate(entry); + if (translatedName != null) { + method.setName(translatedName); + } + } + + if (entry.getSignature() != null) { + // translate the signature + Signature translatedSignature = translator.translateSignature(entry.getSignature()); + behavior.getMethodInfo().setDescriptor(translatedSignature.toString()); + } + } + + // translate the EnclosingMethod attribute + EnclosingMethodAttribute enclosingMethodAttr = (EnclosingMethodAttribute) c.getClassFile().getAttribute(EnclosingMethodAttribute.tag); + if (enclosingMethodAttr != null) { + + if (enclosingMethodAttr.methodIndex() == 0) { + BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry(Descriptor.toJvmName(enclosingMethodAttr.className())); + BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry); + c.getClassFile().addAttribute(new EnclosingMethodAttribute( + constants, + deobfBehaviorEntry.getClassName() + )); + } else { + BehaviorEntry obfBehaviorEntry = EntryFactory.getBehaviorEntry( + Descriptor.toJvmName(enclosingMethodAttr.className()), + enclosingMethodAttr.methodName(), + enclosingMethodAttr.methodDescriptor() + ); + BehaviorEntry deobfBehaviorEntry = translator.translateEntry(obfBehaviorEntry); + c.getClassFile().addAttribute(new EnclosingMethodAttribute( + constants, + deobfBehaviorEntry.getClassName(), + deobfBehaviorEntry.getName(), + deobfBehaviorEntry.getSignature().toString() + )); + } + } + + // translate all the class names referenced in the code + // the above code only changed method/field/reference names and types, but not the rest of the class references + ClassRenamer.renameClasses(c, translator); + + // translate the source file attribute too + ClassEntry deobfClassEntry = translator.translateEntry(classEntry); + if (deobfClassEntry != null) { + 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/translators/InnerClassWriter.java b/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java new file mode 100644 index 0000000..0e35938 --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/InnerClassWriter.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.bytecode.translators; + +import com.google.common.collect.Lists; +import cuchaz.enigma.analysis.JarIndex; +import cuchaz.enigma.bytecode.ClassRenamer; +import cuchaz.enigma.mapping.*; +import javassist.ClassPool; +import javassist.CtClass; +import javassist.NotFoundException; +import javassist.bytecode.*; + +import java.util.Collection; +import java.util.List; + +public class InnerClassWriter { + + // FIXME: modifier 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(); + } + } + } + + public static void write(JarIndex index, CtClass c) { + + // don't change anything if there's already an attribute there + InnerClassesAttribute oldAttr = (InnerClassesAttribute) c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (oldAttr != null) { + // bail! + return; + } + + ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); + List obfClassChain = index.getObfClassChain(obfClassEntry); + + boolean isInnerClass = obfClassChain.size() > 1; + if (isInnerClass) { + + // it's an inner class, rename it to the fully qualified name + c.setName(obfClassEntry.buildClassEntry(obfClassChain).getName()); + + BehaviorEntry caller = index.getAnonymousClassCaller(obfClassEntry); + if (caller != null) { + + // write the enclosing method attribute + if (caller.getName().equals("")) { + c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); + } else { + c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName(), caller.getName(), caller.getSignature().toString())); + } + } + } + + // does this class have any inner classes? + Collection obfInnerClassEntries = index.getInnerClasses(obfClassEntry); + + if (isInnerClass || !obfInnerClassEntries.isEmpty()) { + + // create an inner class attribute + InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); + c.getClassFile().addAttribute(attr); + + // write the ancestry, but not the outermost class + for (int i = 1; i < obfClassChain.size(); i++) { + ClassEntry obfInnerClassEntry = obfClassChain.get(i); + writeInnerClass(index, attr, obfClassChain, obfInnerClassEntry); + + // update references to use the fully qualified inner class name + c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(obfClassChain).getName()); + } + + // write the inner classes + for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { + + // extend the class chain + List extendedObfClassChain = Lists.newArrayList(obfClassChain); + extendedObfClassChain.add(obfInnerClassEntry); + + writeInnerClass(index, attr, extendedObfClassChain, obfInnerClassEntry); + + // update references to use the fully qualified inner class name + c.replaceClassName(obfInnerClassEntry.getName(), obfInnerClassEntry.buildClassEntry(extendedObfClassChain).getName()); + } + } + } + + private static void writeInnerClass(JarIndex index, InnerClassesAttribute attr, List obfClassChain, ClassEntry obfClassEntry) { + + // get the new inner class name + ClassEntry obfInnerClassEntry = obfClassEntry.buildClassEntry(obfClassChain); + ClassEntry obfOuterClassEntry = obfInnerClassEntry.getOuterClassEntry(); + + // here's what the JVM spec says about the InnerClasses attribute + // append(inner, parent, 0 if anonymous else simple name, flags); + + // update the attribute with this inner class + ConstPool constPool = attr.getConstPool(); + int innerClassIndex = constPool.addClassInfo(obfInnerClassEntry.getName()); + int parentClassIndex = constPool.addClassInfo(obfOuterClassEntry.getName()); + int innerClassNameIndex = 0; + int accessFlags = AccessFlag.PUBLIC; + // TODO: need to figure out if we can put static or not + if (!index.isAnonymousClass(obfClassEntry)) { + innerClassNameIndex = constPool.addUtf8Info(obfInnerClassEntry.getInnermostClassName()); + } + + attr.append(innerClassIndex, parentClassIndex, innerClassNameIndex, accessFlags); + + /* DEBUG + System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", + obfClassEntry, + attr.innerClass(attr.tableLength() - 1), + attr.outerClass(attr.tableLength() - 1), + attr.innerName(attr.tableLength() - 1), + Constants.NonePackage + "/" + obfInnerClassName, + obfClassEntry.getName() + )); + */ + } +} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java new file mode 100644 index 0000000..51b3d2d --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/LocalVariableTranslator.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.bytecode.translators; + +import cuchaz.enigma.mapping.*; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.bytecode.*; + +public class LocalVariableTranslator { + + public static void translate(Translator translator, CtClass c) { + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + + // if there's a local variable table, just rename everything to v1, v2, v3, ... for now + CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); + if (codeAttribute == null) { + continue; + } + + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + ConstPool constants = c.getClassFile().getConstPool(); + + LocalVariableAttribute table = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag); + if (table != null) { + renameLVT(translator, behaviorEntry, constants, table, c); + } + + LocalVariableTypeAttribute typeTable = (LocalVariableTypeAttribute) codeAttribute.getAttribute(LocalVariableAttribute.typeTag); + if (typeTable != null) { + renameLVTT(typeTable, table); + } + } + } + + // DEBUG + @SuppressWarnings("unused") + private static void dumpTable(LocalVariableAttribute table) { + for (int i = 0; i < table.tableLength(); i++) { + System.out.println(String.format("\t%d (%d): %s %s", + i, table.index(i), table.variableName(i), table.descriptor(i) + )); + } + } + + private static void renameLVT(Translator translator, BehaviorEntry behaviorEntry, ConstPool constants, LocalVariableAttribute table, CtClass ctClass) { + + // skip empty tables + if (table.tableLength() <= 0) { + return; + } + + // where do we start counting variables? + int starti = 0; + if (table.variableName(0).equals("this")) { + // skip the "this" variable + starti++; + } + + // rename method arguments first + int numArgs = 0; + if (behaviorEntry.getSignature() != null) { + numArgs = behaviorEntry.getSignature().getArgumentTypes().size(); + boolean isNestedClassConstructor = false; + + // If the behavior is a constructor and if it have more than one arg, it's probably from a nested! + if (behaviorEntry instanceof ConstructorEntry && behaviorEntry.getClassEntry() != null && behaviorEntry.getClassEntry().isInnerClass() && numArgs >= 1) { + // Get the first arg type + Type firstArg = behaviorEntry.getSignature().getArgumentTypes().get(0); + + // If the arg is a class and if the class name match the outer class name of the constructor, it's definitely a constructor of a nested class + if (firstArg.isClass() && firstArg.getClassEntry().equals(behaviorEntry.getClassEntry().getOuterClassEntry())) { + isNestedClassConstructor = true; + numArgs--; + } + } + + for (int i = starti; i < starti + numArgs && i < table.tableLength(); i++) { + int argi = i - starti; + if (ctClass.isEnum()) + argi += 2; + String argName = translator.translate(new ArgumentEntry(behaviorEntry, argi, "")); + if (argName == null) { + int argIndex = isNestedClassConstructor ? argi + 1 : argi; + if (ctClass.isEnum()) + argIndex -= 2; + Type argType = behaviorEntry.getSignature().getArgumentTypes().get(argIndex); + // Unfortunately each of these have different name getters, so they have different code paths + if (argType.isPrimitive()) { + Type.Primitive argCls = argType.getPrimitive(); + argName = "a" + argCls.name() + (argIndex + 1); + } else if (argType.isArray()) { + // List types would require this whole block again, so just go with aListx + argName = "aList" + (argIndex + 1); + } else if (argType.isClass()) { + ClassEntry argClsTrans = translator.translateEntry(argType.getClassEntry()); + argName = "a" + argClsTrans.getSimpleName().replace("$", "") + (argIndex + 1); + } else { + argName = "a" + (argIndex + 1); + } + } + renameVariable(table, i, constants.addUtf8Info(argName)); + } + } + + // then rename the rest of the args, if any + for (int i = starti + numArgs; i < table.tableLength(); i++) { + int firstIndex = Math.min(table.index(starti + numArgs), table.index(i)); + renameVariable(table, i, constants.addUtf8Info("v" + (table.index(i) - firstIndex + 1))); + } + } + + private static void renameLVTT(LocalVariableTypeAttribute typeTable, LocalVariableAttribute table) { + // rename args to the same names as in the LVT + for (int i = 0; i < typeTable.tableLength(); i++) { + renameVariable(typeTable, i, getNameIndex(table, typeTable.index(i))); + } + } + + private static void renameVariable(LocalVariableAttribute table, int i, int stringId) { + // based off of LocalVariableAttribute.nameIndex() + ByteArray.write16bit(stringId, table.get(), i * 10 + 6); + } + + private static int getNameIndex(LocalVariableAttribute table, int index) { + for (int i = 0; i < table.tableLength(); i++) { + if (table.index(i) == index) { + return table.nameIndex(i); + } + } + return 0; + } +} diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java b/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java new file mode 100644 index 0000000..4e632b9 --- /dev/null +++ b/src/main/java/cuchaz/enigma/bytecode/translators/MethodParameterTranslator.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2015 Jeff Martin. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Lesser General Public + * License v3.0 which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/lgpl.html + *

+ * Contributors: + * Jeff Martin - initial API and implementation + ******************************************************************************/ + +package cuchaz.enigma.bytecode.translators; + +import cuchaz.enigma.bytecode.MethodParametersAttribute; +import cuchaz.enigma.mapping.*; +import javassist.CtBehavior; +import javassist.CtClass; +import javassist.bytecode.CodeAttribute; +import javassist.bytecode.LocalVariableAttribute; + +import java.util.ArrayList; +import java.util.List; + +public class MethodParameterTranslator { + + public static void translate(Translator translator, CtClass c) { + + // Procyon will read method arguments from the "MethodParameters" attribute, so write those + for (CtBehavior behavior : c.getDeclaredBehaviors()) { + + // if there's a local variable table here, don't write a MethodParameters attribute + // let the local variable writer deal with it instead + // procyon starts doing really weird things if we give it both attributes + CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute(); + if (codeAttribute != null && codeAttribute.getAttribute(LocalVariableAttribute.tag) != null) { + continue; + } + + BehaviorEntry behaviorEntry = EntryFactory.getBehaviorEntry(behavior); + + // get the number of arguments + Signature signature = behaviorEntry.getSignature(); + if (signature == null) { + // static initializers have no signatures, or arguments + continue; + } + int numParams = signature.getArgumentTypes().size(); + if (numParams <= 0) { + continue; + } + + // get the list of argument names + List names = new ArrayList<>(numParams); + for (int i = 0; i < numParams; i++) { + names.add(translator.translate(new ArgumentEntry(behaviorEntry, i, ""))); + } + + // save the mappings to the class + MethodParametersAttribute.updateClass(behavior.getMethodInfo(), names); + } + } +} -- cgit v1.2.3