From 4479acf3df4faf9daac93a396f5bba7cddb0759b Mon Sep 17 00:00:00 2001 From: jeff Date: Wed, 25 Feb 2015 00:12:04 -0500 Subject: more work getting inner class trees working in obf'd and deobf'd land --- src/cuchaz/enigma/analysis/JarIndex.java | 26 +++- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 173 +++++++++++++++-------- src/cuchaz/enigma/mapping/ClassEntry.java | 3 + 3 files changed, 141 insertions(+), 61 deletions(-) (limited to 'src') diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 6e7c69d..1afcb76 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -28,6 +28,7 @@ import javassist.NotFoundException; import javassist.bytecode.AccessFlag; import javassist.bytecode.Descriptor; import javassist.bytecode.FieldInfo; +import javassist.bytecode.InnerClassesAttribute; import javassist.expr.ConstructorCall; import javassist.expr.ExprEditor; import javassist.expr.FieldAccess; @@ -160,9 +161,11 @@ public class JarIndex { ClassEntry innerClassEntry = mapEntry.getValue(); outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry); String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName(); - // DEBUG - //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); - renames.put(innerClassEntry.getName(), newName); + if (!innerClassEntry.getName().equals(newName)) { + // DEBUG + //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); + renames.put(innerClassEntry.getName(), newName); + } } EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); m_translationIndex.renameClasses(renames); @@ -299,6 +302,22 @@ public class JarIndex { private ClassEntry findOuterClass(CtClass c) { + ClassEntry classEntry = EntryFactory.getClassEntry(c); + + // does this class already have an outer class? + if (classEntry.isInnerClass()) { + return classEntry.getOuterClassEntry(); + } + InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag); + if (innerClassesAttribute != null) { + for (int i=0; i obfClassChain = Lists.newArrayList(); + ClassEntry checkClassEntry = obfClassEntry; + while (checkClassEntry != null) { + obfClassChain.add(checkClassEntry); + checkClassEntry = m_index.getOuterClass(checkClassEntry); + } - // see if we're right - if (obfOuterClassEntry == null) { - - // nope, it's an outer class - obfInnerClassEntry = null; - obfOuterClassEntry = EntryFactory.getClassEntry(c); - } else { + // change order: outer to inner + Collections.reverse(obfClassChain); + + // does this class have any inner classes? + Collection obfInnerClassEntries = m_index.getInnerClasses(obfClassEntry); + + boolean isInnerClass = obfClassChain.size() > 1; + if (isInnerClass) { - // yeah, it's an inner class, rename it to outer$inner - ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); - c.setName(obfClassEntry.getName()); + // it's an inner class, rename it to the fully qualified name + StringBuilder buf = new StringBuilder(); + for (ClassEntry obfChainClassEntry : obfClassChain) { + if (buf.length() == 0) { + buf.append(obfChainClassEntry.getName()); + } else { + buf.append("$"); + buf.append(obfChainClassEntry.getSimpleName()); + } + } + c.setName(buf.toString()); - BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassEntry); + BehaviorEntry caller = m_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())); @@ -58,50 +78,89 @@ public class InnerClassWriter { } } - // write the inner classes if needed - Collection obfInnerClassEntries = m_jarIndex.getInnerClasses(obfOuterClassEntry); - if (obfInnerClassEntries != null && !obfInnerClassEntries.isEmpty()) { - writeInnerClasses(c, obfOuterClassEntry, obfInnerClassEntries); - } - } - - private void writeInnerClasses(CtClass c, ClassEntry obfOuterClassEntry, Collection obfInnerClassEntries) { - InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); - c.getClassFile().addAttribute(attr); - for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { + if (isInnerClass || !obfInnerClassEntries.isEmpty()) { - // get the new inner class name - ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); + // create an inner class attribute + InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); + c.getClassFile().addAttribute(attr); - // here's what the JVM spec says about the InnerClasses attribute - // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); - - // update the attribute with this inner class - ConstPool constPool = c.getClassFile().getConstPool(); - int innerClassIndex = constPool.addClassInfo(obfClassEntry.getName()); - int outerClassIndex = 0; - int innerClassSimpleNameIndex = 0; - int accessFlags = 0; - if (!m_jarIndex.isAnonymousClass(obfInnerClassEntry)) { - outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); - innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); + // write the ancestry, but not the outermost class + for (int i=1; i 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() - )); - */ - - // make sure the outer class references only the new inner class names - c.replaceClassName(obfInnerClassEntry.getName(), obfClassEntry.getName()); + // write the inner classes + for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { + + // extend the class chain + List extendedObfClassChain = Lists.newArrayList(obfClassChain); + extendedObfClassChain.add(obfInnerClassEntry); + + String fullyQualifiedInnerClassName = writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); + + // make sure we only reference the fully qualified inner class name + c.replaceClassName(obfInnerClassEntry.getName(), fullyQualifiedInnerClassName); + } + } + } + + private String writeInnerClass(InnerClassesAttribute attr, List obfClassChain, ClassEntry obfClassEntry) { + + // get the new inner class name + String obfInnerClassName = getFullyQualifiedName(obfClassChain, obfClassEntry); + String obfParentClassName = getFullyQualifiedParentName(obfClassChain, obfClassEntry); + + // 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(obfInnerClassName); + int parentClassIndex = constPool.addClassInfo(obfParentClassName); + int innerClassSimpleNameIndex = 0; + int accessFlags = 0; + if (!m_index.isAnonymousClass(obfClassEntry)) { + innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getSimpleName()); + } + + attr.append(innerClassIndex, parentClassIndex, innerClassSimpleNameIndex, 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() + )); + */ + + return obfInnerClassName; + } + + private String getFullyQualifiedParentName(List classChain, ClassEntry classEntry) { + assert(classChain.size() > 1); + assert(classChain.contains(classEntry)); + StringBuilder buf = new StringBuilder(); + for (int i=0; classChain.get(i) != classEntry; i++) { + ClassEntry chainEntry = classChain.get(i); + if (buf.length() == 0) { + buf.append(chainEntry.getName()); + } else { + buf.append("$"); + buf.append(chainEntry.getSimpleName()); + } + } + return buf.toString(); + } + + private String getFullyQualifiedName(List classChain, ClassEntry classEntry) { + boolean isInner = classChain.size() > 1; + if (isInner) { + return getFullyQualifiedParentName(classChain, classEntry) + "$" + classEntry.getSimpleName(); + } else { + return classEntry.getName(); } } } diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java index cf41001..69f171a 100644 --- a/src/cuchaz/enigma/mapping/ClassEntry.java +++ b/src/cuchaz/enigma/mapping/ClassEntry.java @@ -114,6 +114,9 @@ public class ClassEntry implements Entry, Serializable { } public String getSimpleName() { + if (isInnerClass()) { + return getInnerClassName(); + } int pos = m_name.lastIndexOf('/'); if (pos > 0) { return m_name.substring(pos + 1); -- cgit v1.2.3