diff options
Diffstat (limited to 'src/cuchaz/enigma/TranslatingTypeLoader.java')
| -rw-r--r-- | src/cuchaz/enigma/TranslatingTypeLoader.java | 103 |
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; | |||
| 13 | import java.io.ByteArrayOutputStream; | 13 | import java.io.ByteArrayOutputStream; |
| 14 | import java.io.IOException; | 14 | import java.io.IOException; |
| 15 | import java.io.InputStream; | 15 | import java.io.InputStream; |
| 16 | import java.util.Map; | ||
| 16 | import java.util.jar.JarEntry; | 17 | import java.util.jar.JarEntry; |
| 17 | import java.util.jar.JarFile; | 18 | import java.util.jar.JarFile; |
| 18 | 19 | ||
| @@ -23,6 +24,7 @@ import javassist.CtClass; | |||
| 23 | import javassist.NotFoundException; | 24 | import javassist.NotFoundException; |
| 24 | import javassist.bytecode.Descriptor; | 25 | import javassist.bytecode.Descriptor; |
| 25 | 26 | ||
| 27 | import com.beust.jcommander.internal.Maps; | ||
| 26 | import com.strobel.assembler.metadata.Buffer; | 28 | import com.strobel.assembler.metadata.Buffer; |
| 27 | import com.strobel.assembler.metadata.ITypeLoader; | 29 | import com.strobel.assembler.metadata.ITypeLoader; |
| 28 | 30 | ||
| @@ -31,6 +33,7 @@ import cuchaz.enigma.analysis.JarIndex; | |||
| 31 | import cuchaz.enigma.bytecode.ClassTranslator; | 33 | import cuchaz.enigma.bytecode.ClassTranslator; |
| 32 | import cuchaz.enigma.bytecode.InnerClassWriter; | 34 | import cuchaz.enigma.bytecode.InnerClassWriter; |
| 33 | import cuchaz.enigma.bytecode.MethodParameterWriter; | 35 | import cuchaz.enigma.bytecode.MethodParameterWriter; |
| 36 | import cuchaz.enigma.mapping.ClassEntry; | ||
| 34 | import cuchaz.enigma.mapping.Translator; | 37 | import cuchaz.enigma.mapping.Translator; |
| 35 | 38 | ||
| 36 | public class TranslatingTypeLoader implements ITypeLoader | 39 | public 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 | } |