summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/bytecode/InnerClassWriter.java
diff options
context:
space:
mode:
authorGravatar jeff2015-02-25 00:12:04 -0500
committerGravatar jeff2015-02-25 00:12:04 -0500
commit4479acf3df4faf9daac93a396f5bba7cddb0759b (patch)
tree7893ff75cecdadfdc196e34b89c929bfe42d8955 /src/cuchaz/enigma/bytecode/InnerClassWriter.java
parentlots of work in better handling of inner classes (diff)
downloadenigma-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.java173
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 @@
11package cuchaz.enigma.bytecode; 11package cuchaz.enigma.bytecode;
12 12
13import java.util.Collection; 13import java.util.Collection;
14import java.util.Collections;
15import java.util.List;
14 16
15import javassist.CtClass; 17import javassist.CtClass;
16import javassist.bytecode.ConstPool; 18import javassist.bytecode.ConstPool;
17import javassist.bytecode.EnclosingMethodAttribute; 19import javassist.bytecode.EnclosingMethodAttribute;
18import javassist.bytecode.InnerClassesAttribute; 20import javassist.bytecode.InnerClassesAttribute;
21
22import com.beust.jcommander.internal.Lists;
23
19import cuchaz.enigma.analysis.JarIndex; 24import cuchaz.enigma.analysis.JarIndex;
20import cuchaz.enigma.mapping.BehaviorEntry; 25import cuchaz.enigma.mapping.BehaviorEntry;
21import cuchaz.enigma.mapping.ClassEntry; 26import cuchaz.enigma.mapping.ClassEntry;
@@ -23,32 +28,47 @@ import cuchaz.enigma.mapping.EntryFactory;
23 28
24public class InnerClassWriter { 29public 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}