summaryrefslogtreecommitdiff
path: root/src/cuchaz/enigma/TranslatingTypeLoader.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/cuchaz/enigma/TranslatingTypeLoader.java')
-rw-r--r--src/cuchaz/enigma/TranslatingTypeLoader.java103
1 files changed, 80 insertions, 23 deletions
diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java
index c1d96ae3..ae27f374 100644
--- a/src/cuchaz/enigma/TranslatingTypeLoader.java
+++ b/src/cuchaz/enigma/TranslatingTypeLoader.java
@@ -13,6 +13,7 @@ package cuchaz.enigma;
13import java.io.ByteArrayOutputStream; 13import java.io.ByteArrayOutputStream;
14import java.io.IOException; 14import java.io.IOException;
15import java.io.InputStream; 15import java.io.InputStream;
16import java.util.Map;
16import java.util.jar.JarEntry; 17import java.util.jar.JarEntry;
17import java.util.jar.JarFile; 18import java.util.jar.JarFile;
18 19
@@ -23,6 +24,7 @@ import javassist.CtClass;
23import javassist.NotFoundException; 24import javassist.NotFoundException;
24import javassist.bytecode.Descriptor; 25import javassist.bytecode.Descriptor;
25 26
27import com.beust.jcommander.internal.Maps;
26import com.strobel.assembler.metadata.Buffer; 28import com.strobel.assembler.metadata.Buffer;
27import com.strobel.assembler.metadata.ITypeLoader; 29import com.strobel.assembler.metadata.ITypeLoader;
28 30
@@ -31,6 +33,7 @@ import cuchaz.enigma.analysis.JarIndex;
31import cuchaz.enigma.bytecode.ClassTranslator; 33import cuchaz.enigma.bytecode.ClassTranslator;
32import cuchaz.enigma.bytecode.InnerClassWriter; 34import cuchaz.enigma.bytecode.InnerClassWriter;
33import cuchaz.enigma.bytecode.MethodParameterWriter; 35import cuchaz.enigma.bytecode.MethodParameterWriter;
36import cuchaz.enigma.mapping.ClassEntry;
34import cuchaz.enigma.mapping.Translator; 37import cuchaz.enigma.mapping.Translator;
35 38
36public class TranslatingTypeLoader implements ITypeLoader 39public class TranslatingTypeLoader implements ITypeLoader
@@ -39,6 +42,7 @@ public class TranslatingTypeLoader implements ITypeLoader
39 private JarIndex m_jarIndex; 42 private JarIndex m_jarIndex;
40 private Translator m_obfuscatingTranslator; 43 private Translator m_obfuscatingTranslator;
41 private Translator m_deobfuscatingTranslator; 44 private Translator m_deobfuscatingTranslator;
45 private Map<String,byte[]> m_cache;
42 46
43 public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) 47 public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator )
44 { 48 {
@@ -46,32 +50,71 @@ public class TranslatingTypeLoader implements ITypeLoader
46 m_jarIndex = jarIndex; 50 m_jarIndex = jarIndex;
47 m_obfuscatingTranslator = obfuscatingTranslator; 51 m_obfuscatingTranslator = obfuscatingTranslator;
48 m_deobfuscatingTranslator = deobfuscatingTranslator; 52 m_deobfuscatingTranslator = deobfuscatingTranslator;
53 m_cache = Maps.newHashMap();
49 } 54 }
50 55
51 @Override 56 @Override
52 public boolean tryLoadType( String deobfClassName, Buffer out ) 57 public boolean tryLoadType( String deobfClassName, Buffer out )
53 { 58 {
59 // check the cache
60 byte[] data;
61 if( m_cache.containsKey( deobfClassName ) )
62 {
63 data = m_cache.get( deobfClassName );
64 }
65 else
66 {
67 data = loadType( deobfClassName );
68 m_cache.put( deobfClassName, data );
69 }
70
71 if( data == null )
72 {
73 return false;
74 }
75
76 // send the class to the decompiler
77 out.reset( data.length );
78 System.arraycopy( data, 0, out.array(), out.position(), data.length );
79 out.position( 0 );
80 return true;
81 }
82
83 private byte[] loadType( String deobfClassName )
84 {
54 // what class file should we actually load? 85 // what class file should we actually load?
55 String obfClassName = m_obfuscatingTranslator.translateClass( deobfClassName ); 86 ClassEntry deobfClassEntry = new ClassEntry( deobfClassName );
56 if( obfClassName == null ) 87 ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry( deobfClassEntry );
88
89 // is this an inner class referenced directly?
90 if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null )
57 { 91 {
58 obfClassName = deobfClassName; 92 // this class doesn't really exist. Reference it by outer$inner instead
93 System.err.println( String.format( "WARNING: class %s referenced by bare inner name", deobfClassName ) );
94 return null;
59 } 95 }
60 String classFileName = obfClassName;
61 96
62 // is this an inner class? 97 /* DEBUG
63 if( obfClassName.indexOf( '$' ) >= 0 ) 98 if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) )
64 { 99 {
65 // the file name is the bare inner class name 100 System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) );
66 String[] parts = obfClassName.split( "\\$" );
67 classFileName = parts[parts.length - 1];
68 } 101 }
102 */
69 103
70 // get the jar entry 104 // get the jar entry
105 String classFileName;
106 if( obfClassEntry.isInnerClass() )
107 {
108 classFileName = obfClassEntry.getInnerClassName();
109 }
110 else
111 {
112 classFileName = obfClassEntry.getOuterClassName();
113 }
71 JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); 114 JarEntry entry = m_jar.getJarEntry( classFileName + ".class" );
72 if( entry == null ) 115 if( entry == null )
73 { 116 {
74 return false; 117 return null;
75 } 118 }
76 119
77 try 120 try
@@ -93,35 +136,49 @@ public class TranslatingTypeLoader implements ITypeLoader
93 in.close(); 136 in.close();
94 buf = data.toByteArray(); 137 buf = data.toByteArray();
95 138
96 // load the javassist handle to the class 139 // load the javassist handle to the raw class
97 String javaClassFileName = Descriptor.toJavaName( classFileName ); 140 String javaClassFileName = Descriptor.toJavaName( classFileName );
98 ClassPool classPool = new ClassPool(); 141 ClassPool classPool = new ClassPool();
99 classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); 142 classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) );
100 CtClass c = classPool.get( javaClassFileName ); 143 CtClass c = classPool.get( javaClassFileName );
101 144
145 // reconstruct inner classes
146 new InnerClassWriter( m_jarIndex ).write( c );
147
148 // re-get the javassist handle since we changed class names
149 String javaClassReconstructedName = Descriptor.toJavaName( obfClassEntry.getName() );
150 classPool = new ClassPool();
151 classPool.insertClassPath( new ByteArrayClassPath( javaClassReconstructedName, c.toBytecode() ) );
152 c = classPool.get( javaClassReconstructedName );
153
154 // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong)
155 assertClassName( c, obfClassEntry );
156
102 // do all kinds of deobfuscating transformations on the class 157 // do all kinds of deobfuscating transformations on the class
103 new InnerClassWriter( m_deobfuscatingTranslator, m_jarIndex ).write( c );
104 new BridgeFixer().fixBridges( c ); 158 new BridgeFixer().fixBridges( c );
105 new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); 159 new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c );
106 new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); 160 new ClassTranslator( m_deobfuscatingTranslator ).translate( c );
107 161
108 // sanity checking 162 // sanity checking
109 assert( Descriptor.toJvmName( c.getName() ).equals( deobfClassName ) ) 163 assertClassName( c, deobfClassEntry );
110 : String.format( "%s is not %s", Descriptor.toJvmName( c.getName() ), deobfClassName );
111 assert( Descriptor.toJvmName( c.getClassFile().getName() ).equals( deobfClassName ) )
112 : String.format( "%s is not %s", Descriptor.toJvmName( c.getClassFile().getName() ), deobfClassName );
113
114 // pass the transformed class along to the decompiler
115 buf = c.toBytecode();
116 out.reset( buf.length );
117 System.arraycopy( buf, 0, out.array(), out.position(), buf.length );
118 out.position( 0 );
119 164
120 return true; 165 // we have a transformed class!
166 return c.toBytecode();
121 } 167 }
122 catch( IOException | NotFoundException | CannotCompileException ex ) 168 catch( IOException | NotFoundException | CannotCompileException ex )
123 { 169 {
124 throw new Error( ex ); 170 throw new Error( ex );
125 } 171 }
126 } 172 }
173
174 private void assertClassName( CtClass c, ClassEntry obfClassEntry )
175 {
176 String name1 = Descriptor.toJvmName( c.getName() );
177 assert( name1.equals( obfClassEntry.getName() ) )
178 : String.format( "Looking for %s, instead found %s", obfClassEntry.getName(), name1 );
179
180 String name2 = Descriptor.toJvmName( c.getClassFile().getName() );
181 assert( name2.equals( obfClassEntry.getName() ) )
182 : String.format( "Looking for %s, instead found %s", obfClassEntry.getName(), name2 );
183 }
127} 184}