diff options
| author | 2015-02-25 00:12:04 -0500 | |
|---|---|---|
| committer | 2015-02-25 00:12:04 -0500 | |
| commit | 4479acf3df4faf9daac93a396f5bba7cddb0759b (patch) | |
| tree | 7893ff75cecdadfdc196e34b89c929bfe42d8955 /src/cuchaz/enigma/bytecode/InnerClassWriter.java | |
| parent | lots of work in better handling of inner classes (diff) | |
| download | enigma-fork-4479acf3df4faf9daac93a396f5bba7cddb0759b.tar.gz enigma-fork-4479acf3df4faf9daac93a396f5bba7cddb0759b.tar.xz enigma-fork-4479acf3df4faf9daac93a396f5bba7cddb0759b.zip | |
more work getting inner class trees working in obf'd and deobf'd land
Diffstat (limited to 'src/cuchaz/enigma/bytecode/InnerClassWriter.java')
| -rw-r--r-- | src/cuchaz/enigma/bytecode/InnerClassWriter.java | 173 |
1 files changed, 116 insertions, 57 deletions
diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index e82f56c..3bebd17 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java | |||
| @@ -11,11 +11,16 @@ | |||
| 11 | package cuchaz.enigma.bytecode; | 11 | package cuchaz.enigma.bytecode; |
| 12 | 12 | ||
| 13 | import java.util.Collection; | 13 | import java.util.Collection; |
| 14 | import java.util.Collections; | ||
| 15 | import java.util.List; | ||
| 14 | 16 | ||
| 15 | import javassist.CtClass; | 17 | import javassist.CtClass; |
| 16 | import javassist.bytecode.ConstPool; | 18 | import javassist.bytecode.ConstPool; |
| 17 | import javassist.bytecode.EnclosingMethodAttribute; | 19 | import javassist.bytecode.EnclosingMethodAttribute; |
| 18 | import javassist.bytecode.InnerClassesAttribute; | 20 | import javassist.bytecode.InnerClassesAttribute; |
| 21 | |||
| 22 | import com.beust.jcommander.internal.Lists; | ||
| 23 | |||
| 19 | import cuchaz.enigma.analysis.JarIndex; | 24 | import cuchaz.enigma.analysis.JarIndex; |
| 20 | import cuchaz.enigma.mapping.BehaviorEntry; | 25 | import cuchaz.enigma.mapping.BehaviorEntry; |
| 21 | import cuchaz.enigma.mapping.ClassEntry; | 26 | import cuchaz.enigma.mapping.ClassEntry; |
| @@ -23,32 +28,47 @@ import cuchaz.enigma.mapping.EntryFactory; | |||
| 23 | 28 | ||
| 24 | public class InnerClassWriter { | 29 | public class InnerClassWriter { |
| 25 | 30 | ||
| 26 | private JarIndex m_jarIndex; | 31 | private JarIndex m_index; |
| 27 | 32 | ||
| 28 | public InnerClassWriter(JarIndex jarIndex) { | 33 | public InnerClassWriter(JarIndex index) { |
| 29 | m_jarIndex = jarIndex; | 34 | m_index = index; |
| 30 | } | 35 | } |
| 31 | 36 | ||
| 32 | public void write(CtClass c) { | 37 | public void write(CtClass c) { |
| 33 | 38 | ||
| 34 | // first, assume this is an inner class | 39 | // build the class chain (inner to outer) |
| 35 | ClassEntry obfInnerClassEntry = EntryFactory.getClassEntry(c); | 40 | ClassEntry obfClassEntry = EntryFactory.getClassEntry(c); |
| 36 | ClassEntry obfOuterClassEntry = m_jarIndex.getOuterClass(obfInnerClassEntry); | 41 | List<ClassEntry> obfClassChain = Lists.newArrayList(); |
| 42 | ClassEntry checkClassEntry = obfClassEntry; | ||
| 43 | while (checkClassEntry != null) { | ||
| 44 | obfClassChain.add(checkClassEntry); | ||
| 45 | checkClassEntry = m_index.getOuterClass(checkClassEntry); | ||
| 46 | } | ||
| 37 | 47 | ||
| 38 | // see if we're right | 48 | // change order: outer to inner |
| 39 | if (obfOuterClassEntry == null) { | 49 | Collections.reverse(obfClassChain); |
| 40 | 50 | ||
| 41 | // nope, it's an outer class | 51 | // does this class have any inner classes? |
| 42 | obfInnerClassEntry = null; | 52 | Collection<ClassEntry> obfInnerClassEntries = m_index.getInnerClasses(obfClassEntry); |
| 43 | obfOuterClassEntry = EntryFactory.getClassEntry(c); | 53 | |
| 44 | } else { | 54 | boolean isInnerClass = obfClassChain.size() > 1; |
| 55 | if (isInnerClass) { | ||
| 45 | 56 | ||
| 46 | // yeah, it's an inner class, rename it to outer$inner | 57 | // it's an inner class, rename it to the fully qualified name |
| 47 | ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); | 58 | StringBuilder buf = new StringBuilder(); |
| 48 | c.setName(obfClassEntry.getName()); | 59 | for (ClassEntry obfChainClassEntry : obfClassChain) { |
| 60 | if (buf.length() == 0) { | ||
| 61 | buf.append(obfChainClassEntry.getName()); | ||
| 62 | } else { | ||
| 63 | buf.append("$"); | ||
| 64 | buf.append(obfChainClassEntry.getSimpleName()); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | c.setName(buf.toString()); | ||
| 49 | 68 | ||
| 50 | BehaviorEntry caller = m_jarIndex.getAnonymousClassCaller(obfInnerClassEntry); | 69 | BehaviorEntry caller = m_index.getAnonymousClassCaller(obfClassEntry); |
| 51 | if (caller != null) { | 70 | if (caller != null) { |
| 71 | |||
| 52 | // write the enclosing method attribute | 72 | // write the enclosing method attribute |
| 53 | if (caller.getName().equals("<clinit>")) { | 73 | if (caller.getName().equals("<clinit>")) { |
| 54 | c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); | 74 | c.getClassFile().addAttribute(new EnclosingMethodAttribute(c.getClassFile().getConstPool(), caller.getClassName())); |
| @@ -58,50 +78,89 @@ public class InnerClassWriter { | |||
| 58 | } | 78 | } |
| 59 | } | 79 | } |
| 60 | 80 | ||
| 61 | // write the inner classes if needed | 81 | if (isInnerClass || !obfInnerClassEntries.isEmpty()) { |
| 62 | Collection<ClassEntry> obfInnerClassEntries = m_jarIndex.getInnerClasses(obfOuterClassEntry); | ||
| 63 | if (obfInnerClassEntries != null && !obfInnerClassEntries.isEmpty()) { | ||
| 64 | writeInnerClasses(c, obfOuterClassEntry, obfInnerClassEntries); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | private void writeInnerClasses(CtClass c, ClassEntry obfOuterClassEntry, Collection<ClassEntry> obfInnerClassEntries) { | ||
| 69 | InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); | ||
| 70 | c.getClassFile().addAttribute(attr); | ||
| 71 | for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { | ||
| 72 | 82 | ||
| 73 | // get the new inner class name | 83 | // create an inner class attribute |
| 74 | ClassEntry obfClassEntry = new ClassEntry(obfOuterClassEntry.getName() + "$" + obfInnerClassEntry.getSimpleName()); | 84 | InnerClassesAttribute attr = new InnerClassesAttribute(c.getClassFile().getConstPool()); |
| 85 | c.getClassFile().addAttribute(attr); | ||
| 75 | 86 | ||
| 76 | // here's what the JVM spec says about the InnerClasses attribute | 87 | // write the ancestry, but not the outermost class |
| 77 | // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); | 88 | for (int i=1; i<obfClassChain.size(); i++) { |
| 78 | 89 | writeInnerClass(attr, obfClassChain, obfClassChain.get(i)); | |
| 79 | // update the attribute with this inner class | ||
| 80 | ConstPool constPool = c.getClassFile().getConstPool(); | ||
| 81 | int innerClassIndex = constPool.addClassInfo(obfClassEntry.getName()); | ||
| 82 | int outerClassIndex = 0; | ||
| 83 | int innerClassSimpleNameIndex = 0; | ||
| 84 | int accessFlags = 0; | ||
| 85 | if (!m_jarIndex.isAnonymousClass(obfInnerClassEntry)) { | ||
| 86 | outerClassIndex = constPool.addClassInfo(obfClassEntry.getOuterClassName()); | ||
| 87 | innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getInnerClassName()); | ||
| 88 | } | 90 | } |
| 89 | 91 | ||
| 90 | attr.append(innerClassIndex, outerClassIndex, innerClassSimpleNameIndex, accessFlags); | 92 | // write the inner classes |
| 91 | 93 | for (ClassEntry obfInnerClassEntry : obfInnerClassEntries) { | |
| 92 | /* DEBUG | 94 | |
| 93 | System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", | 95 | // extend the class chain |
| 94 | obfClassEntry, | 96 | List<ClassEntry> extendedObfClassChain = Lists.newArrayList(obfClassChain); |
| 95 | attr.innerClass(attr.tableLength() - 1), | 97 | extendedObfClassChain.add(obfInnerClassEntry); |
| 96 | attr.outerClass(attr.tableLength() - 1), | 98 | |
| 97 | attr.innerName(attr.tableLength() - 1), | 99 | String fullyQualifiedInnerClassName = writeInnerClass(attr, extendedObfClassChain, obfInnerClassEntry); |
| 98 | Constants.NonePackage + "/" + obfInnerClassName, | 100 | |
| 99 | obfClassEntry.getName() | 101 | // make sure we only reference the fully qualified inner class name |
| 100 | )); | 102 | c.replaceClassName(obfInnerClassEntry.getName(), fullyQualifiedInnerClassName); |
| 101 | */ | 103 | } |
| 102 | 104 | } | |
| 103 | // make sure the outer class references only the new inner class names | 105 | } |
| 104 | c.replaceClassName(obfInnerClassEntry.getName(), obfClassEntry.getName()); | 106 | |
| 107 | private String writeInnerClass(InnerClassesAttribute attr, List<ClassEntry> obfClassChain, ClassEntry obfClassEntry) { | ||
| 108 | |||
| 109 | // get the new inner class name | ||
| 110 | String obfInnerClassName = getFullyQualifiedName(obfClassChain, obfClassEntry); | ||
| 111 | String obfParentClassName = getFullyQualifiedParentName(obfClassChain, obfClassEntry); | ||
| 112 | |||
| 113 | // here's what the JVM spec says about the InnerClasses attribute | ||
| 114 | // append(inner, parent, 0 if anonymous else simple name, flags); | ||
| 115 | |||
| 116 | // update the attribute with this inner class | ||
| 117 | ConstPool constPool = attr.getConstPool(); | ||
| 118 | int innerClassIndex = constPool.addClassInfo(obfInnerClassName); | ||
| 119 | int parentClassIndex = constPool.addClassInfo(obfParentClassName); | ||
| 120 | int innerClassSimpleNameIndex = 0; | ||
| 121 | int accessFlags = 0; | ||
| 122 | if (!m_index.isAnonymousClass(obfClassEntry)) { | ||
| 123 | innerClassSimpleNameIndex = constPool.addUtf8Info(obfClassEntry.getSimpleName()); | ||
| 124 | } | ||
| 125 | |||
| 126 | attr.append(innerClassIndex, parentClassIndex, innerClassSimpleNameIndex, accessFlags); | ||
| 127 | |||
| 128 | /* DEBUG | ||
| 129 | System.out.println(String.format("\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", | ||
| 130 | obfClassEntry, | ||
| 131 | attr.innerClass(attr.tableLength() - 1), | ||
| 132 | attr.outerClass(attr.tableLength() - 1), | ||
| 133 | attr.innerName(attr.tableLength() - 1), | ||
| 134 | Constants.NonePackage + "/" + obfInnerClassName, | ||
| 135 | obfClassEntry.getName() | ||
| 136 | )); | ||
| 137 | */ | ||
| 138 | |||
| 139 | return obfInnerClassName; | ||
| 140 | } | ||
| 141 | |||
| 142 | private String getFullyQualifiedParentName(List<ClassEntry> classChain, ClassEntry classEntry) { | ||
| 143 | assert(classChain.size() > 1); | ||
| 144 | assert(classChain.contains(classEntry)); | ||
| 145 | StringBuilder buf = new StringBuilder(); | ||
| 146 | for (int i=0; classChain.get(i) != classEntry; i++) { | ||
| 147 | ClassEntry chainEntry = classChain.get(i); | ||
| 148 | if (buf.length() == 0) { | ||
| 149 | buf.append(chainEntry.getName()); | ||
| 150 | } else { | ||
| 151 | buf.append("$"); | ||
| 152 | buf.append(chainEntry.getSimpleName()); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | return buf.toString(); | ||
| 156 | } | ||
| 157 | |||
| 158 | private String getFullyQualifiedName(List<ClassEntry> classChain, ClassEntry classEntry) { | ||
| 159 | boolean isInner = classChain.size() > 1; | ||
| 160 | if (isInner) { | ||
| 161 | return getFullyQualifiedParentName(classChain, classEntry) + "$" + classEntry.getSimpleName(); | ||
| 162 | } else { | ||
| 163 | return classEntry.getName(); | ||
| 105 | } | 164 | } |
| 106 | } | 165 | } |
| 107 | } | 166 | } |