summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/analysis/JarIndex.java
diff options
context:
space:
mode:
authorGravatar jeff2014-08-15 01:43:48 -0400
committerGravatar jeff2014-08-15 01:43:48 -0400
commit37467e4a7b5e05e4da413a1e06e597fa806b72e4 (patch)
tree4c76a76aa3379fc236977646af48ec63dcf1712e /src/cuchaz/enigma/analysis/JarIndex.java
parentAdded tag v0.1 beta for changeset 7beed0616320 (diff)
downloadenigma-fork-37467e4a7b5e05e4da413a1e06e597fa806b72e4.tar.gz
enigma-fork-37467e4a7b5e05e4da413a1e06e597fa806b72e4.tar.xz
enigma-fork-37467e4a7b5e05e4da413a1e06e597fa806b72e4.zip
trying to get inner/anonymous classes working... I have a working heuristic in place to detect anonymous classes, but I can't seem to get Procyon to decompile them correctly. I'm writing the InnerClasses attribute and translating all the inner class names, but there must be something else I'm missing...
Diffstat (limited to 'src/cuchaz/enigma/analysis/JarIndex.java')
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java187
1 files changed, 113 insertions, 74 deletions
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index 845be60..9962bfa 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -10,27 +10,21 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.analysis; 11package cuchaz.enigma.analysis;
12 12
13import java.io.ByteArrayOutputStream;
14import java.io.IOException;
15import java.io.InputStream;
16import java.util.Collection; 13import java.util.Collection;
17import java.util.Enumeration;
18import java.util.List; 14import java.util.List;
15import java.util.Map;
19import java.util.Set; 16import java.util.Set;
20import java.util.jar.JarEntry; 17import java.util.jar.JarEntry;
21import java.util.jar.JarFile; 18import java.util.jar.JarFile;
22import java.util.zip.ZipEntry;
23import java.util.zip.ZipInputStream;
24 19
25import javassist.ByteArrayClassPath;
26import javassist.CannotCompileException; 20import javassist.CannotCompileException;
27import javassist.ClassPool;
28import javassist.CtBehavior; 21import javassist.CtBehavior;
29import javassist.CtClass; 22import javassist.CtClass;
30import javassist.CtConstructor; 23import javassist.CtConstructor;
31import javassist.CtMethod; 24import javassist.CtMethod;
32import javassist.NotFoundException; 25import javassist.bytecode.AccessFlag;
33import javassist.bytecode.Descriptor; 26import javassist.bytecode.Descriptor;
27import javassist.bytecode.FieldInfo;
34import javassist.expr.ConstructorCall; 28import javassist.expr.ConstructorCall;
35import javassist.expr.ExprEditor; 29import javassist.expr.ExprEditor;
36import javassist.expr.FieldAccess; 30import javassist.expr.FieldAccess;
@@ -39,10 +33,10 @@ import javassist.expr.NewExpr;
39 33
40import com.google.common.collect.HashMultimap; 34import com.google.common.collect.HashMultimap;
41import com.google.common.collect.Lists; 35import com.google.common.collect.Lists;
36import com.google.common.collect.Maps;
42import com.google.common.collect.Multimap; 37import com.google.common.collect.Multimap;
43import com.google.common.collect.Sets; 38import com.google.common.collect.Sets;
44 39
45import cuchaz.enigma.Constants;
46import cuchaz.enigma.mapping.ClassEntry; 40import cuchaz.enigma.mapping.ClassEntry;
47import cuchaz.enigma.mapping.ConstructorEntry; 41import cuchaz.enigma.mapping.ConstructorEntry;
48import cuchaz.enigma.mapping.Entry; 42import cuchaz.enigma.mapping.Entry;
@@ -57,89 +51,52 @@ public class JarIndex
57 private Multimap<String,MethodEntry> m_methodImplementations; 51 private Multimap<String,MethodEntry> m_methodImplementations;
58 private Multimap<Entry,Entry> m_methodCalls; 52 private Multimap<Entry,Entry> m_methodCalls;
59 private Multimap<FieldEntry,Entry> m_fieldCalls; 53 private Multimap<FieldEntry,Entry> m_fieldCalls;
54 private Multimap<String,String> m_innerClasses;
55 private Map<String,String> m_outerClasses;
60 56
61 public JarIndex( JarFile jar ) 57 public JarIndex( )
62 { 58 {
63 m_obfClassNames = Sets.newHashSet(); 59 m_obfClassNames = Sets.newHashSet();
64 m_ancestries = new Ancestries(); 60 m_ancestries = new Ancestries();
65 m_methodImplementations = HashMultimap.create(); 61 m_methodImplementations = HashMultimap.create();
66 m_methodCalls = HashMultimap.create(); 62 m_methodCalls = HashMultimap.create();
67 m_fieldCalls = HashMultimap.create(); 63 m_fieldCalls = HashMultimap.create();
68 64 m_innerClasses = HashMultimap.create();
69 // read the class names 65 m_outerClasses = Maps.newHashMap();
70 Enumeration<JarEntry> enumeration = jar.entries(); 66 }
71 while( enumeration.hasMoreElements() ) 67
68 public void indexJar( JarFile jar )
69 {
70 // pass 1: read the class names
71 for( JarEntry entry : JarClassIterator.getClassEntries( jar ) )
72 { 72 {
73 JarEntry entry = enumeration.nextElement();
74
75 // filter out non-classes
76 if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) )
77 {
78 continue;
79 }
80
81 String className = entry.getName().substring( 0, entry.getName().length() - 6 ); 73 String className = entry.getName().substring( 0, entry.getName().length() - 6 );
82 m_obfClassNames.add( Descriptor.toJvmName( className ) ); 74 m_obfClassNames.add( Descriptor.toJvmName( className ) );
83 } 75 }
84 }
85
86 public void indexJar( InputStream in )
87 throws IOException
88 {
89 ClassPool classPool = new ClassPool();
90 76
91 ZipInputStream zin = new ZipInputStream( in ); 77 // pass 2: index the types, methods
92 ZipEntry entry; 78 for( CtClass c : JarClassIterator.classes( jar ) )
93 while( ( entry = zin.getNextEntry() ) != null )
94 { 79 {
95 // filter out non-classes 80 m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() );
96 if( entry.isDirectory() || !entry.getName().endsWith( ".class" ) ) 81 for( CtBehavior behavior : c.getDeclaredBehaviors() )
97 { 82 {
98 continue; 83 indexBehavior( behavior );
99 } 84 }
100 85 }
101 // read the class into a buffer 86
102 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 87 // pass 2: index inner classes
103 byte[] buf = new byte[Constants.KiB]; 88 for( CtClass c : JarClassIterator.classes( jar ) )
104 int totalNumBytesRead = 0; 89 {
105 while( zin.available() > 0 ) 90 String outerClassName = isInnerClass( c );
106 { 91 if( outerClassName != null )
107 int numBytesRead = zin.read( buf );
108 if( numBytesRead < 0 )
109 {
110 break;
111 }
112 bos.write( buf, 0, numBytesRead );
113
114 // sanity checking
115 totalNumBytesRead += numBytesRead;
116 if( totalNumBytesRead > Constants.MiB )
117 {
118 throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" );
119 }
120 }
121
122 // determine the class name (ie chop off the ".class")
123 String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) );
124
125 // get a javassist handle for the class
126 classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) );
127 try
128 {
129 CtClass c = classPool.get( className );
130 m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() );
131 for( CtBehavior behavior : c.getDeclaredBehaviors() )
132 {
133 indexBehavior( behavior );
134 }
135 }
136 catch( NotFoundException ex )
137 { 92 {
138 throw new Error( "Unable to load class: " + className ); 93 String innerClassName = Descriptor.toJvmName( c.getName() );
94 m_innerClasses.put( outerClassName, innerClassName );
95 m_outerClasses.put( innerClassName, outerClassName );
139 } 96 }
140 } 97 }
141 } 98 }
142 99
143 private void indexBehavior( CtBehavior behavior ) 100 private void indexBehavior( CtBehavior behavior )
144 { 101 {
145 // get the method entry 102 // get the method entry
@@ -226,6 +183,78 @@ public class JarIndex
226 } 183 }
227 } 184 }
228 185
186 @SuppressWarnings( "unchecked" )
187 private String isInnerClass( CtClass c )
188 {
189 String innerClassName = Descriptor.toJvmName( c.getName() );
190
191 // first, is this an anonymous class?
192 // for anonymous classes:
193 // the outer class is always a synthetic field
194 // there's at least one constructor with the type of the synthetic field as an argument
195 // this constructor is called exactly once by the class of the synthetic field
196
197 for( FieldInfo field : (List<FieldInfo>)c.getClassFile().getFields() )
198 {
199 boolean isSynthetic = (field.getAccessFlags() & AccessFlag.SYNTHETIC) != 0;
200 if( !isSynthetic )
201 {
202 continue;
203 }
204
205 // skip non-class types
206 if( !field.getDescriptor().startsWith( "L" ) )
207 {
208 continue;
209 }
210
211 // get the outer class from the field type
212 String outerClassName = Descriptor.toJvmName( Descriptor.toClassName( field.getDescriptor() ) );
213
214 // look for a constructor where this type is the first parameter
215 CtConstructor targetConstructor = null;
216 for( CtConstructor constructor : c.getDeclaredConstructors() )
217 {
218 String signature = Descriptor.getParamDescriptor( constructor.getMethodInfo().getDescriptor() );
219 if( Descriptor.numOfParameters( signature ) < 1 )
220 {
221 continue;
222 }
223
224 // match the first parameter to the outer class
225 Descriptor.Iterator iter = new Descriptor.Iterator( signature );
226 int pos = iter.next();
227 if( iter.isParameter() && signature.charAt( pos ) == 'L' )
228 {
229 String argumentDesc = signature.substring( pos, signature.indexOf(';', pos) + 1 );
230 String argumentClassName = Descriptor.toJvmName( Descriptor.toClassName( argumentDesc ) );
231 if( argumentClassName.equals( outerClassName ) )
232 {
233 // is this constructor called exactly once?
234 ConstructorEntry constructorEntry = new ConstructorEntry(
235 new ClassEntry( innerClassName ),
236 constructor.getMethodInfo().getDescriptor()
237 );
238 if( this.getMethodCallers( constructorEntry ).size() == 1 )
239 {
240 targetConstructor = constructor;
241 break;
242 }
243 }
244 }
245 }
246 if( targetConstructor == null )
247 {
248 continue;
249 }
250
251 // yeah, this is an inner class
252 return outerClassName;
253 }
254
255 return null;
256 }
257
229 public Set<String> getObfClassNames( ) 258 public Set<String> getObfClassNames( )
230 { 259 {
231 return m_obfClassNames; 260 return m_obfClassNames;
@@ -304,4 +333,14 @@ public class JarIndex
304 { 333 {
305 return m_methodCalls.get( entry ); 334 return m_methodCalls.get( entry );
306 } 335 }
336
337 public Collection<String> getInnerClasses( String obfOuterClassName )
338 {
339 return m_innerClasses.get( obfOuterClassName );
340 }
341
342 public String getOuterClass( String obfInnerClassName )
343 {
344 return m_outerClasses.get( obfInnerClassName );
345 }
307} 346}