summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar jeff2014-08-15 01:43:48 -0400
committerGravatar jeff2014-08-15 01:43:48 -0400
commit37467e4a7b5e05e4da413a1e06e597fa806b72e4 (patch)
tree4c76a76aa3379fc236977646af48ec63dcf1712e
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...
-rw-r--r--.hgignore4
-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
6 files changed, 399 insertions, 109 deletions
diff --git a/.hgignore b/.hgignore
index 9581253..ddf86cc 100644
--- a/.hgignore
+++ b/.hgignore
@@ -10,4 +10,6 @@ syntax: regexp
10syntax: regexp 10syntax: regexp
11^libs$ 11^libs$
12syntax: regexp 12syntax: regexp
13^build$ \ No newline at end of file 13^build$
14syntax: regexp
15^data$ \ No newline at end of file
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}