summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java26
-rw-r--r--src/cuchaz/enigma/bytecode/InnerClassWriter.java173
-rw-r--r--src/cuchaz/enigma/mapping/ClassEntry.java3
3 files changed, 141 insertions, 61 deletions
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;
28import javassist.bytecode.AccessFlag; 28import javassist.bytecode.AccessFlag;
29import javassist.bytecode.Descriptor; 29import javassist.bytecode.Descriptor;
30import javassist.bytecode.FieldInfo; 30import javassist.bytecode.FieldInfo;
31import javassist.bytecode.InnerClassesAttribute;
31import javassist.expr.ConstructorCall; 32import javassist.expr.ConstructorCall;
32import javassist.expr.ExprEditor; 33import javassist.expr.ExprEditor;
33import javassist.expr.FieldAccess; 34import javassist.expr.FieldAccess;
@@ -160,9 +161,11 @@ public class JarIndex {
160 ClassEntry innerClassEntry = mapEntry.getValue(); 161 ClassEntry innerClassEntry = mapEntry.getValue();
161 outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry); 162 outerClassEntry = EntryFactory.getChainedOuterClassName(this, outerClassEntry);
162 String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName(); 163 String newName = outerClassEntry.getName() + "$" + innerClassEntry.getSimpleName();
163 // DEBUG 164 if (!innerClassEntry.getName().equals(newName)) {
164 //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName); 165 // DEBUG
165 renames.put(innerClassEntry.getName(), newName); 166 //System.out.println("REPLACE: " + innerClassEntry.getName() + " WITH " + newName);
167 renames.put(innerClassEntry.getName(), newName);
168 }
166 } 169 }
167 EntryRenamer.renameClassesInSet(renames, m_obfClassEntries); 170 EntryRenamer.renameClassesInSet(renames, m_obfClassEntries);
168 m_translationIndex.renameClasses(renames); 171 m_translationIndex.renameClasses(renames);
@@ -299,6 +302,22 @@ public class JarIndex {
299 302
300 private ClassEntry findOuterClass(CtClass c) { 303 private ClassEntry findOuterClass(CtClass c) {
301 304
305 ClassEntry classEntry = EntryFactory.getClassEntry(c);
306
307 // does this class already have an outer class?
308 if (classEntry.isInnerClass()) {
309 return classEntry.getOuterClassEntry();
310 }
311 InnerClassesAttribute innerClassesAttribute = (InnerClassesAttribute)c.getClassFile().getAttribute(InnerClassesAttribute.tag);
312 if (innerClassesAttribute != null) {
313 for (int i=0; i<innerClassesAttribute.tableLength(); i++) {
314 ClassEntry innerClassEntry = new ClassEntry(Descriptor.toJvmName(innerClassesAttribute.innerClass(i)));
315 if (classEntry.equals(innerClassEntry)) {
316 return new ClassEntry(Descriptor.toJvmName(innerClassesAttribute.outerClass(i)));
317 }
318 }
319 }
320
302 // inner classes: 321 // inner classes:
303 // have constructors that can (illegally) set synthetic fields 322 // have constructors that can (illegally) set synthetic fields
304 // the outer class is the only class that calls constructors 323 // the outer class is the only class that calls constructors
@@ -310,7 +329,6 @@ public class JarIndex {
310 continue; 329 continue;
311 } 330 }
312 331
313 ClassEntry classEntry = new ClassEntry(Descriptor.toJvmName(c.getName()));
314 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor); 332 ConstructorEntry constructorEntry = EntryFactory.getConstructorEntry(constructor);
315 333
316 // gather the classes from the illegally-set synthetic fields 334 // gather the classes from the illegally-set synthetic fields
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}
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 {
114 } 114 }
115 115
116 public String getSimpleName() { 116 public String getSimpleName() {
117 if (isInnerClass()) {
118 return getInnerClassName();
119 }
117 int pos = m_name.lastIndexOf('/'); 120 int pos = m_name.lastIndexOf('/');
118 if (pos > 0) { 121 if (pos > 0) {
119 return m_name.substring(pos + 1); 122 return m_name.substring(pos + 1);