summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java18
-rw-r--r--src/cuchaz/enigma/TranslatingTypeLoader.java86
-rw-r--r--src/cuchaz/enigma/analysis/JarClassIterator.java134
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java187
-rw-r--r--src/cuchaz/enigma/bytecode/InnerClassWriter.java79
5 files changed, 396 insertions, 108 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java
index 770172e..127a0d9 100644
--- a/src/cuchaz/enigma/Deobfuscator.java
+++ b/src/cuchaz/enigma/Deobfuscator.java
@@ -11,9 +11,7 @@
11package cuchaz.enigma; 11package cuchaz.enigma;
12 12
13import java.io.File; 13import java.io.File;
14import java.io.FileInputStream;
15import java.io.IOException; 14import java.io.IOException;
16import java.io.InputStream;
17import java.io.StringWriter; 15import java.io.StringWriter;
18import java.util.List; 16import java.util.List;
19import java.util.jar.JarFile; 17import java.util.jar.JarFile;
@@ -58,18 +56,9 @@ public class Deobfuscator
58 m_file = file; 56 m_file = file;
59 m_jar = new JarFile( m_file ); 57 m_jar = new JarFile( m_file );
60 58
61 // build the ancestries 59 // build the jar index
62 InputStream jarIn = null; 60 m_jarIndex = new JarIndex();
63 try 61 m_jarIndex.indexJar( m_jar );
64 {
65 m_jarIndex = new JarIndex( m_jar );
66 jarIn = new FileInputStream( m_file );
67 m_jarIndex.indexJar( jarIn );
68 }
69 finally
70 {
71 Util.closeQuietly( jarIn );
72 }
73 62
74 // config the decompiler 63 // config the decompiler
75 m_settings = DecompilerSettings.javaDefaults(); 64 m_settings = DecompilerSettings.javaDefaults();
@@ -105,6 +94,7 @@ public class Deobfuscator
105 // update decompiler options 94 // update decompiler options
106 m_settings.setTypeLoader( new TranslatingTypeLoader( 95 m_settings.setTypeLoader( new TranslatingTypeLoader(
107 m_jar, 96 m_jar,
97 m_jarIndex,
108 getTranslator( TranslationDirection.Obfuscating ), 98 getTranslator( TranslationDirection.Obfuscating ),
109 getTranslator( TranslationDirection.Deobfuscating ) 99 getTranslator( TranslationDirection.Deobfuscating )
110 ) ); 100 ) );
diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java
index f5112e0..fdfcea0 100644
--- a/src/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/cuchaz/enigma/TranslatingTypeLoader.java
@@ -17,42 +17,77 @@ import java.util.jar.JarEntry;
17import java.util.jar.JarFile; 17import java.util.jar.JarFile;
18 18
19import javassist.ByteArrayClassPath; 19import javassist.ByteArrayClassPath;
20import javassist.CannotCompileException;
20import javassist.ClassPool; 21import javassist.ClassPool;
21import javassist.CtClass; 22import javassist.CtClass;
23import javassist.NotFoundException;
22import javassist.bytecode.Descriptor; 24import javassist.bytecode.Descriptor;
23 25
24import com.strobel.assembler.metadata.Buffer; 26import com.strobel.assembler.metadata.Buffer;
25import com.strobel.assembler.metadata.ITypeLoader; 27import com.strobel.assembler.metadata.ITypeLoader;
26 28
29import cuchaz.enigma.analysis.JarIndex;
27import cuchaz.enigma.bytecode.ClassTranslator; 30import cuchaz.enigma.bytecode.ClassTranslator;
31import cuchaz.enigma.bytecode.InnerClassWriter;
28import cuchaz.enigma.bytecode.MethodParameterWriter; 32import cuchaz.enigma.bytecode.MethodParameterWriter;
29import cuchaz.enigma.mapping.Translator; 33import cuchaz.enigma.mapping.Translator;
30 34
31public class TranslatingTypeLoader implements ITypeLoader 35public class TranslatingTypeLoader implements ITypeLoader
32{ 36{
33 private JarFile m_jar; 37 private JarFile m_jar;
38 private JarIndex m_jarIndex;
34 private Translator m_obfuscatingTranslator; 39 private Translator m_obfuscatingTranslator;
35 private Translator m_deobfuscatingTranslator; 40 private Translator m_deobfuscatingTranslator;
36 41
37 public TranslatingTypeLoader( JarFile jar, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) 42 public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator )
38 { 43 {
39 m_jar = jar; 44 m_jar = jar;
45 m_jarIndex = jarIndex;
40 m_obfuscatingTranslator = obfuscatingTranslator; 46 m_obfuscatingTranslator = obfuscatingTranslator;
41 m_deobfuscatingTranslator = deobfuscatingTranslator; 47 m_deobfuscatingTranslator = deobfuscatingTranslator;
42 } 48 }
43 49
44 @Override 50 @Override
45 public boolean tryLoadType( String name, Buffer out ) 51 public boolean tryLoadType( String deobfClassName, Buffer out )
46 { 52 {
47 // is this a deobufscated class name? 53 // TEMP
48 String obfName = m_obfuscatingTranslator.translateClass( name ); 54 if( !deobfClassName.startsWith( "java" ) && !deobfClassName.startsWith( "org" ) )
49 if( obfName != null )
50 { 55 {
51 // point to the obfuscated class 56 System.out.println( "Looking for: " + deobfClassName );
52 name = obfName;
53 } 57 }
54 58
55 JarEntry entry = m_jar.getJarEntry( name + ".class" ); 59 // what class file should we actually load?
60 String obfClassName = m_obfuscatingTranslator.translateClass( deobfClassName );
61 if( obfClassName == null )
62 {
63 obfClassName = deobfClassName;
64 }
65 String classFileName = obfClassName;
66
67 // is this a properly-referenced inner class?
68 boolean isInnerClass = deobfClassName.indexOf( '$' ) >= 0;
69 if( isInnerClass )
70 {
71 // get just the bare inner class name
72 String[] parts = deobfClassName.split( "\\$" );
73 String deobfClassFileName = parts[parts.length - 1];
74
75 // make sure the bare inner class name is obfuscated
76 classFileName = m_obfuscatingTranslator.translateClass( deobfClassFileName );
77 if( classFileName == null )
78 {
79 classFileName = deobfClassFileName;
80 }
81 }
82
83 // TEMP
84 if( !deobfClassName.startsWith( "java" ) && !deobfClassName.startsWith( "org" ) )
85 {
86 System.out.println( "\tLooking at class file: " + classFileName );
87 }
88
89 // get the jar entry
90 JarEntry entry = m_jar.getJarEntry( classFileName + ".class" );
56 if( entry == null ) 91 if( entry == null )
57 { 92 {
58 return false; 93 return false;
@@ -73,32 +108,43 @@ public class TranslatingTypeLoader implements ITypeLoader
73 } 108 }
74 data.write( buf, 0, bytesRead ); 109 data.write( buf, 0, bytesRead );
75 } 110 }
111 data.close();
112 in.close();
76 buf = data.toByteArray(); 113 buf = data.toByteArray();
77 114
78 // translate the class 115 // load the javassist handle to the class
79 String javaName = Descriptor.toJavaName( name ); 116 String javaClassFileName = Descriptor.toJavaName( classFileName );
80 ClassPool classPool = new ClassPool(); 117 ClassPool classPool = new ClassPool();
81 classPool.insertClassPath( new ByteArrayClassPath( javaName, buf ) ); 118 classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) );
82 try 119 CtClass c = classPool.get( javaClassFileName );
120
121 if( isInnerClass )
83 { 122 {
84 CtClass c = classPool.get( javaName ); 123 // rename the class to what procyon expects
85 new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); 124 c.setName( deobfClassName );
86 new ClassTranslator( m_deobfuscatingTranslator ).translate( c );
87 buf = c.toBytecode();
88 } 125 }
89 catch( Exception ex ) 126 else
90 { 127 {
91 throw new Error( ex ); 128 // maybe it's an outer class
129 new InnerClassWriter( m_deobfuscatingTranslator, m_jarIndex ).writeInnerClasses( c );
92 } 130 }
93 131
94 // pass it along to the decompiler 132 new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c );
133 new ClassTranslator( m_deobfuscatingTranslator ).translate( c );
134
135 assert( Descriptor.toJvmName( c.getName() ).equals( deobfClassName ) );
136 assert( c.getClassFile().getName().equals( deobfClassName ) );
137
138 buf = c.toBytecode();
139
140 // pass the transformed class along to the decompiler
95 out.reset( buf.length ); 141 out.reset( buf.length );
96 System.arraycopy( buf, 0, out.array(), out.position(), buf.length ); 142 System.arraycopy( buf, 0, out.array(), out.position(), buf.length );
97 out.position( 0 ); 143 out.position( 0 );
98 144
99 return true; 145 return true;
100 } 146 }
101 catch( IOException ex ) 147 catch( IOException | NotFoundException | CannotCompileException ex )
102 { 148 {
103 throw new Error( ex ); 149 throw new Error( ex );
104 } 150 }
diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java
new file mode 100644
index 0000000..cf6df80
--- /dev/null
+++ b/src/cuchaz/enigma/analysis/JarClassIterator.java
@@ -0,0 +1,134 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.analysis;
12
13import java.io.ByteArrayOutputStream;
14import java.io.IOException;
15import java.io.InputStream;
16import java.util.Enumeration;
17import java.util.Iterator;
18import java.util.List;
19import java.util.jar.JarEntry;
20import java.util.jar.JarFile;
21
22import javassist.ByteArrayClassPath;
23import javassist.ClassPool;
24import javassist.CtClass;
25import javassist.NotFoundException;
26import javassist.bytecode.Descriptor;
27
28import com.beust.jcommander.internal.Lists;
29
30import cuchaz.enigma.Constants;
31
32public class JarClassIterator implements Iterator<CtClass>
33{
34 private JarFile m_jar;
35 private Iterator<JarEntry> m_iter;
36
37 public JarClassIterator( JarFile jar )
38 {
39 this( jar, getClassEntries( jar ) );
40 }
41
42 public JarClassIterator( JarFile jar, List<JarEntry> entries )
43 {
44 m_jar = jar;
45 m_iter = entries.iterator();
46 }
47
48 @Override
49 public boolean hasNext( )
50 {
51 return m_iter.hasNext();
52 }
53
54 @Override
55 public CtClass next( )
56 {
57 JarEntry entry = m_iter.next();
58 try
59 {
60 // read the class into a buffer
61 ByteArrayOutputStream bos = new ByteArrayOutputStream();
62 byte[] buf = new byte[Constants.KiB];
63 int totalNumBytesRead = 0;
64 InputStream in = m_jar.getInputStream( entry );
65 while( in.available() > 0 )
66 {
67 int numBytesRead = in.read( buf );
68 if( numBytesRead < 0 )
69 {
70 break;
71 }
72 bos.write( buf, 0, numBytesRead );
73
74 // sanity checking
75 totalNumBytesRead += numBytesRead;
76 if( totalNumBytesRead > Constants.MiB )
77 {
78 throw new Error( "Class file " + entry.getName() + " larger than 1 MiB! Something is wrong!" );
79 }
80 }
81
82 // determine the class name (ie chop off the ".class")
83 String className = Descriptor.toJavaName( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) );
84
85 // get a javassist handle for the class
86 ClassPool classPool = new ClassPool();
87 classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) );
88 return classPool.get( className );
89 }
90 catch( IOException ex )
91 {
92 throw new Error( "Unable to read class: " + entry.getName() );
93 }
94 catch( NotFoundException ex )
95 {
96 throw new Error( "Unable to load class: " + entry.getName() );
97 }
98 }
99
100 @Override
101 public void remove( )
102 {
103 throw new UnsupportedOperationException();
104 }
105
106 public static List<JarEntry> getClassEntries( JarFile jar )
107 {
108 List<JarEntry> classes = Lists.newArrayList();
109 Enumeration<JarEntry> entries = jar.entries();
110 while( entries.hasMoreElements() )
111 {
112 JarEntry entry = entries.nextElement();
113
114 // is this a class file?
115 if( !entry.isDirectory() && entry.getName().endsWith( ".class" ) )
116 {
117 classes.add( entry );
118 }
119 }
120 return classes;
121 }
122
123 public static Iterable<CtClass> classes( final JarFile jar )
124 {
125 return new Iterable<CtClass>( )
126 {
127 @Override
128 public Iterator<CtClass> iterator( )
129 {
130 return new JarClassIterator( jar );
131 }
132 };
133 }
134}
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}
diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java
new file mode 100644
index 0000000..d4abe4e
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java
@@ -0,0 +1,79 @@
1/*******************************************************************************
2 * Copyright (c) 2014 Jeff Martin.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the GNU Public License v3.0
5 * which accompanies this distribution, and is available at
6 * http://www.gnu.org/licenses/gpl.html
7 *
8 * Contributors:
9 * Jeff Martin - initial API and implementation
10 ******************************************************************************/
11package cuchaz.enigma.bytecode;
12
13import java.util.Collection;
14
15import javassist.CtClass;
16import javassist.bytecode.AccessFlag;
17import javassist.bytecode.Descriptor;
18import javassist.bytecode.InnerClassesAttribute;
19import cuchaz.enigma.analysis.JarIndex;
20import cuchaz.enigma.mapping.Translator;
21
22public class InnerClassWriter
23{
24 private Translator m_deobfuscatingTranslator;
25 private JarIndex m_jarIndex;
26
27 public InnerClassWriter( Translator deobfuscatingTranslator, JarIndex jarIndex )
28 {
29 m_deobfuscatingTranslator = deobfuscatingTranslator;
30 m_jarIndex = jarIndex;
31 }
32
33 public void writeInnerClasses( CtClass c )
34 {
35 // is this an outer class with inner classes?
36 String obfOuterClassName = Descriptor.toJvmName( c.getName() );
37 Collection<String> obfInnerClassNames = m_jarIndex.getInnerClasses( obfOuterClassName );
38 if( obfInnerClassNames != null && !obfInnerClassNames.isEmpty() )
39 {
40 writeInnerClasses( c, obfInnerClassNames );
41 }
42 }
43
44 private void writeInnerClasses( CtClass c, Collection<String> obfInnerClassNames )
45 {
46 String obfOuterClassName = Descriptor.toJvmName( c.getName() );
47 InnerClassesAttribute attr = new InnerClassesAttribute( c.getClassFile().getConstPool() );
48 c.getClassFile().addAttribute( attr );
49 for( String obfInnerClassName : obfInnerClassNames )
50 {
51 // deobfuscate the class names
52 String deobfOuterClassName = m_deobfuscatingTranslator.translateClass( obfOuterClassName );
53 if( deobfOuterClassName == null )
54 {
55 deobfOuterClassName = obfOuterClassName;
56 }
57 String deobfInnerClassName = m_deobfuscatingTranslator.translateClass( obfInnerClassName );
58 if( deobfInnerClassName == null )
59 {
60 deobfInnerClassName = obfInnerClassName;
61 }
62
63 // update the attribute
64 String deobfOuterInnerClassName = deobfOuterClassName + "$" + deobfInnerClassName;
65 attr.append(
66 deobfOuterInnerClassName,
67 deobfOuterClassName,
68 deobfInnerClassName,
69 c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER
70 );
71
72 // make sure the outer class references only the new inner class names
73 c.replaceClassName( obfInnerClassName, deobfOuterInnerClassName );
74
75 // TEMP
76 System.out.println( "\tInner " + obfInnerClassName + " -> " + deobfOuterInnerClassName );
77 }
78 }
79}