From 34c1e8e64ec4575527a19fb4cb0640c57da784db Mon Sep 17 00:00:00 2001 From: jeff Date: Mon, 18 Aug 2014 00:55:30 -0400 Subject: crap-ton of bug fixes for inner classes --- src/cuchaz/enigma/Deobfuscator.java | 2 + src/cuchaz/enigma/Main.java | 4 + src/cuchaz/enigma/TranslatingTypeLoader.java | 103 +++++++++--- src/cuchaz/enigma/Util.java | 20 +++ src/cuchaz/enigma/analysis/BridgeFixer.java | 2 + src/cuchaz/enigma/analysis/JarIndex.java | 195 ++++++++++++++++++----- src/cuchaz/enigma/bytecode/BytecodeTools.java | 57 +++++++ src/cuchaz/enigma/bytecode/ClassTranslator.java | 53 ++++-- src/cuchaz/enigma/bytecode/InnerClassWriter.java | 42 +++-- src/cuchaz/enigma/mapping/Translator.java | 16 +- 10 files changed, 392 insertions(+), 102 deletions(-) diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java index 9a0ec13..323aa2e 100644 --- a/src/cuchaz/enigma/Deobfuscator.java +++ b/src/cuchaz/enigma/Deobfuscator.java @@ -62,6 +62,8 @@ public class Deobfuscator // config the decompiler m_settings = DecompilerSettings.javaDefaults(); + // DEBUG + //m_settings.setShowSyntheticMembers( true ); // init mappings setMappings( new Mappings() ); diff --git a/src/cuchaz/enigma/Main.java b/src/cuchaz/enigma/Main.java index 6a300ed..20d73c2 100644 --- a/src/cuchaz/enigma/Main.java +++ b/src/cuchaz/enigma/Main.java @@ -13,6 +13,7 @@ package cuchaz.enigma; import java.io.File; import cuchaz.enigma.gui.Gui; +import cuchaz.enigma.mapping.ClassEntry; public class Main { @@ -30,6 +31,9 @@ public class Main { gui.getController().openMappings( getFile( args[1] ) ); } + + // DEBUG + //gui.getController().openEntry( new ClassEntry( "bah$bag" ) ); // bah,bag } private static File getFile( String path ) diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java index c1d96ae..ae27f37 100644 --- a/src/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/cuchaz/enigma/TranslatingTypeLoader.java @@ -13,6 +13,7 @@ package cuchaz.enigma; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -23,6 +24,7 @@ import javassist.CtClass; import javassist.NotFoundException; import javassist.bytecode.Descriptor; +import com.beust.jcommander.internal.Maps; import com.strobel.assembler.metadata.Buffer; import com.strobel.assembler.metadata.ITypeLoader; @@ -31,6 +33,7 @@ import cuchaz.enigma.analysis.JarIndex; import cuchaz.enigma.bytecode.ClassTranslator; import cuchaz.enigma.bytecode.InnerClassWriter; import cuchaz.enigma.bytecode.MethodParameterWriter; +import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.Translator; public class TranslatingTypeLoader implements ITypeLoader @@ -39,6 +42,7 @@ public class TranslatingTypeLoader implements ITypeLoader private JarIndex m_jarIndex; private Translator m_obfuscatingTranslator; private Translator m_deobfuscatingTranslator; + private Map m_cache; public TranslatingTypeLoader( JarFile jar, JarIndex jarIndex, Translator obfuscatingTranslator, Translator deobfuscatingTranslator ) { @@ -46,32 +50,71 @@ public class TranslatingTypeLoader implements ITypeLoader m_jarIndex = jarIndex; m_obfuscatingTranslator = obfuscatingTranslator; m_deobfuscatingTranslator = deobfuscatingTranslator; + m_cache = Maps.newHashMap(); } @Override public boolean tryLoadType( String deobfClassName, Buffer out ) + { + // check the cache + byte[] data; + if( m_cache.containsKey( deobfClassName ) ) + { + data = m_cache.get( deobfClassName ); + } + else + { + data = loadType( deobfClassName ); + m_cache.put( deobfClassName, data ); + } + + if( data == null ) + { + return false; + } + + // send the class to the decompiler + out.reset( data.length ); + System.arraycopy( data, 0, out.array(), out.position(), data.length ); + out.position( 0 ); + return true; + } + + private byte[] loadType( String deobfClassName ) { // what class file should we actually load? - String obfClassName = m_obfuscatingTranslator.translateClass( deobfClassName ); - if( obfClassName == null ) + ClassEntry deobfClassEntry = new ClassEntry( deobfClassName ); + ClassEntry obfClassEntry = m_obfuscatingTranslator.translateEntry( deobfClassEntry ); + + // is this an inner class referenced directly? + if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null ) { - obfClassName = deobfClassName; + // this class doesn't really exist. Reference it by outer$inner instead + System.err.println( String.format( "WARNING: class %s referenced by bare inner name", deobfClassName ) ); + return null; } - String classFileName = obfClassName; - // is this an inner class? - if( obfClassName.indexOf( '$' ) >= 0 ) + /* DEBUG + if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) { - // the file name is the bare inner class name - String[] parts = obfClassName.split( "\\$" ); - classFileName = parts[parts.length - 1]; + System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); } + */ // get the jar entry + String classFileName; + if( obfClassEntry.isInnerClass() ) + { + classFileName = obfClassEntry.getInnerClassName(); + } + else + { + classFileName = obfClassEntry.getOuterClassName(); + } JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); if( entry == null ) { - return false; + return null; } try @@ -93,35 +136,49 @@ public class TranslatingTypeLoader implements ITypeLoader in.close(); buf = data.toByteArray(); - // load the javassist handle to the class + // load the javassist handle to the raw class String javaClassFileName = Descriptor.toJavaName( classFileName ); ClassPool classPool = new ClassPool(); classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); CtClass c = classPool.get( javaClassFileName ); + // reconstruct inner classes + new InnerClassWriter( m_jarIndex ).write( c ); + + // re-get the javassist handle since we changed class names + String javaClassReconstructedName = Descriptor.toJavaName( obfClassEntry.getName() ); + classPool = new ClassPool(); + classPool.insertClassPath( new ByteArrayClassPath( javaClassReconstructedName, c.toBytecode() ) ); + c = classPool.get( javaClassReconstructedName ); + + // check that the file is correct after inner class reconstruction (ie cause Javassist to fail fast if something is wrong) + assertClassName( c, obfClassEntry ); + // do all kinds of deobfuscating transformations on the class - new InnerClassWriter( m_deobfuscatingTranslator, m_jarIndex ).write( c ); new BridgeFixer().fixBridges( c ); new MethodParameterWriter( m_deobfuscatingTranslator ).writeMethodArguments( c ); new ClassTranslator( m_deobfuscatingTranslator ).translate( c ); // sanity checking - assert( Descriptor.toJvmName( c.getName() ).equals( deobfClassName ) ) - : String.format( "%s is not %s", Descriptor.toJvmName( c.getName() ), deobfClassName ); - assert( Descriptor.toJvmName( c.getClassFile().getName() ).equals( deobfClassName ) ) - : String.format( "%s is not %s", Descriptor.toJvmName( c.getClassFile().getName() ), deobfClassName ); - - // pass the transformed class along to the decompiler - buf = c.toBytecode(); - out.reset( buf.length ); - System.arraycopy( buf, 0, out.array(), out.position(), buf.length ); - out.position( 0 ); + assertClassName( c, deobfClassEntry ); - return true; + // we have a transformed class! + return c.toBytecode(); } catch( IOException | NotFoundException | CannotCompileException ex ) { throw new Error( ex ); } } + + private void assertClassName( CtClass c, ClassEntry obfClassEntry ) + { + String name1 = Descriptor.toJvmName( c.getName() ); + assert( name1.equals( obfClassEntry.getName() ) ) + : String.format( "Looking for %s, instead found %s", obfClassEntry.getName(), name1 ); + + String name2 = Descriptor.toJvmName( c.getClassFile().getName() ); + assert( name2.equals( obfClassEntry.getName() ) ) + : String.format( "Looking for %s, instead found %s", obfClassEntry.getName(), name2 ); + } } diff --git a/src/cuchaz/enigma/Util.java b/src/cuchaz/enigma/Util.java index 84927fd..3686ef0 100644 --- a/src/cuchaz/enigma/Util.java +++ b/src/cuchaz/enigma/Util.java @@ -12,6 +12,8 @@ package cuchaz.enigma; import java.awt.Desktop; import java.io.Closeable; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -19,6 +21,10 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.jar.JarFile; +import javassist.CannotCompileException; +import javassist.CtClass; +import javassist.bytecode.Descriptor; + import com.google.common.io.CharStreams; @@ -106,4 +112,18 @@ public class Util } } } + + public static void writeClass( CtClass c ) + { + String name = Descriptor.toJavaName( c.getName() ); + File file = new File( name + ".class" ); + try( FileOutputStream out = new FileOutputStream( file ) ) + { + out.write( c.toBytecode() ); + } + catch( IOException | CannotCompileException ex ) + { + throw new Error( ex ); + } + } } diff --git a/src/cuchaz/enigma/analysis/BridgeFixer.java b/src/cuchaz/enigma/analysis/BridgeFixer.java index db441d2..ee90f51 100644 --- a/src/cuchaz/enigma/analysis/BridgeFixer.java +++ b/src/cuchaz/enigma/analysis/BridgeFixer.java @@ -41,6 +41,8 @@ public class BridgeFixer { bridgedMethod.setName( method.getName() ); method.setModifiers( method.getModifiers() | AccessFlag.BRIDGE ); + + // TODO: rename all references to this method? } } } diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java index 34e8986..7d68c35 100644 --- a/src/cuchaz/enigma/analysis/JarIndex.java +++ b/src/cuchaz/enigma/analysis/JarIndex.java @@ -10,6 +10,7 @@ ******************************************************************************/ package cuchaz.enigma.analysis; +import java.lang.reflect.Modifier; import java.util.AbstractMap; import java.util.Collection; import java.util.Iterator; @@ -92,8 +93,8 @@ public class JarIndex // pass 2: index inner classes and anonymous classes for( CtClass c : JarClassIterator.classes( jar ) ) { - String outerClassName = isInnerClass( c ); - if( outerClassName != null ) + String outerClassName = findOuterClass( c ); + if( outerClassName != null )// /* TEMP */ && false ) { String innerClassName = Descriptor.toJvmName( c.getName() ); m_innerClasses.put( outerClassName, innerClassName ); @@ -102,6 +103,14 @@ public class JarIndex if( isAnonymousClass( c, outerClassName ) ) { m_anonymousClasses.add( innerClassName ); + + // DEBUG + System.out.println( "ANONYMOUS: " + outerClassName + "$" + innerClassName ); + } + else + { + // DEBUG + System.out.println( "INNER: " + outerClassName + "$" + innerClassName ); } } } @@ -175,6 +184,10 @@ public class JarIndex @Override public void edit( ConstructorCall call ) { + boolean isSuper = call.getMethodName().equals( "super" ); + // TODO: make method reference class, update method calls tree to use Invocation instances + // this might end up being a big refactor... =( + String className = Descriptor.toJvmName( call.getClassName() ); ConstructorEntry calledConstructorEntry = new ConstructorEntry( new ClassEntry( className ), @@ -201,75 +214,168 @@ public class JarIndex } } - @SuppressWarnings( "unchecked" ) - private String isInnerClass( CtClass c ) + private String findOuterClass( CtClass c ) { // inner classes: - // the outer class is always a synthetic field - // there's at least one constructor with the type of the synthetic field as an argument - - for( FieldInfo field : (List)c.getClassFile().getFields() ) + // have constructors that can (illegally) set synthetic fields + // the outer class is the only class that calls constructors + + // use the synthetic fields to find the synthetic constructors + for( CtConstructor constructor : c.getDeclaredConstructors() ) { - boolean isSynthetic = (field.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; - if( !isSynthetic ) + if( !isIllegalConstructor( constructor ) ) { continue; } - // skip non-class types - if( !field.getDescriptor().startsWith( "L" ) ) + // who calls this constructor? + Set callerClasses = Sets.newHashSet(); + ConstructorEntry constructorEntry = new ConstructorEntry( + new ClassEntry( Descriptor.toJvmName( c.getName() ) ), + constructor.getMethodInfo().getDescriptor() + ); + for( Entry callerEntry : getMethodCallers( constructorEntry ) ) { - continue; + callerClasses.add( callerEntry.getClassEntry() ); } - // get the outer class from the field type - String outerClassName = Descriptor.toJvmName( Descriptor.toClassName( field.getDescriptor() ) ); - - // look for a constructor where this type is the first parameter - CtConstructor targetConstructor = null; - for( CtConstructor constructor : c.getDeclaredConstructors() ) + // is this called by exactly one class? + if( callerClasses.size() == 1 ) + { + return callerClasses.iterator().next().getName(); + } + else if( callerClasses.size() > 1 ) + { + // TEMP + System.out.println( "WARNING: Illegal class called by more than one class!" + callerClasses ); + } + } + + return null; + } + + @SuppressWarnings( "unchecked" ) + private boolean isIllegalConstructor( CtConstructor constructor ) + { + // illegal constructors only set synthetic member fields, then call super() + String className = constructor.getDeclaringClass().getName(); + + // collect all the field accesses, constructor calls, and method calls + final List illegalFieldWrites = Lists.newArrayList(); + final List constructorCalls = Lists.newArrayList(); + final List methodCalls = Lists.newArrayList(); + try + { + constructor.instrument( new ExprEditor( ) { - String signature = Descriptor.getParamDescriptor( constructor.getMethodInfo().getDescriptor() ); - if( Descriptor.numOfParameters( signature ) < 1 ) + @Override + public void edit( FieldAccess fieldAccess ) { - continue; + if( fieldAccess.isWriter() && constructorCalls.isEmpty() ) + { + illegalFieldWrites.add( fieldAccess ); + } } - // match the first parameter to the outer class - Descriptor.Iterator iter = new Descriptor.Iterator( signature ); - int pos = iter.next(); - if( iter.isParameter() && signature.charAt( pos ) == 'L' ) + @Override + public void edit( ConstructorCall constructorCall ) { - String argumentDesc = signature.substring( pos, signature.indexOf(';', pos) + 1 ); - String argumentClassName = Descriptor.toJvmName( Descriptor.toClassName( argumentDesc ) ); - if( argumentClassName.equals( outerClassName ) ) - { - targetConstructor = constructor; - break; - } + constructorCalls.add( constructorCall ); + } + + @Override + public void edit( MethodCall methodCall ) + { + methodCalls.add( methodCall ); } + } ); + } + catch( CannotCompileException ex ) + { + // we're not compiling anything... this is stupid + throw new Error( ex ); + } + + // method calls are not allowed + if( !methodCalls.isEmpty() ) + { + return false; + } + + // is there only one constructor call? + if( constructorCalls.size() != 1 ) + { + return false; + } + + // is the call to super? + ConstructorCall constructorCall = constructorCalls.get( 0 ); + if( !constructorCall.getMethodName().equals( "super" ) ) + { + return false; + } + + // are there any illegal field writes? + if( illegalFieldWrites.isEmpty() ) + { + return false; + } + + // are all the writes to synthetic fields? + for( FieldAccess fieldWrite : illegalFieldWrites ) + { + // all illegal writes have to be to the local class + if( !fieldWrite.getClassName().equals( className ) ) + { + System.err.println( String.format( "WARNING: illegal write to non-member field %s.%s", fieldWrite.getClassName(), fieldWrite.getFieldName() ) ); + return false; } - if( targetConstructor == null ) + + // find the field + FieldInfo fieldInfo = null; + for( FieldInfo info : (List)constructor.getDeclaringClass().getClassFile().getFields() ) { - continue; + if( info.getName().equals( fieldWrite.getFieldName() ) ) + { + fieldInfo = info; + break; + } + } + if( fieldInfo == null ) + { + // field is in a superclass or something, can't be a local synthetic member + return false; } - // yeah, this is an inner class - return outerClassName; + // is this field synthetic? + boolean isSynthetic = (fieldInfo.getAccessFlags() & AccessFlag.SYNTHETIC) != 0; + if( !isSynthetic ) + { + System.err.println( String.format( "WARNING: illegal write to non synthetic field %s.%s", className, fieldInfo.getName() ) ); + return false; + } } - return null; + // we passed all the tests! + return true; } - + private boolean isAnonymousClass( CtClass c, String outerClassName ) { String innerClassName = Descriptor.toJvmName( c.getName() ); // anonymous classes: + // can't be abstract // have only one constructor // it's called exactly once by the outer class // type of inner class not referenced anywhere in outer class + // is absract? + if( Modifier.isAbstract( c.getModifiers() ) ) + { + return false; + } + // is there exactly one constructor? if( c.getDeclaredConstructors().length != 1 ) { @@ -282,7 +388,16 @@ public class JarIndex new ClassEntry( innerClassName ), constructor.getMethodInfo().getDescriptor() ); - return getMethodCallers( constructorEntry ).size() == 1; + if( getMethodCallers( constructorEntry ).size() != 1 ) + { + return false; + } + + // TODO: check outer class doesn't reference type + // except this is hard because we can't just load the outer class now + // we'd have to pre-index those references in the JarIndex + + return true; } public Set getObfClassNames( ) diff --git a/src/cuchaz/enigma/bytecode/BytecodeTools.java b/src/cuchaz/enigma/bytecode/BytecodeTools.java index 664350e..0de9bd6 100644 --- a/src/cuchaz/enigma/bytecode/BytecodeTools.java +++ b/src/cuchaz/enigma/bytecode/BytecodeTools.java @@ -15,6 +15,7 @@ import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.List; import java.util.Map; import java.util.Set; @@ -25,6 +26,7 @@ import javassist.bytecode.CodeAttribute; import javassist.bytecode.ConstPool; import javassist.bytecode.ExceptionTable; +import com.beust.jcommander.internal.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -266,4 +268,59 @@ public class BytecodeTools ); } } + + public static List getParameterTypes( String signature ) + { + List types = Lists.newArrayList(); + for( int i=0; i 0 ) + { + type = "[" + type; + } + types.add( type ); + } + return types; + } } diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java index 3b5beeb..9ce06a5 100644 --- a/src/cuchaz/enigma/bytecode/ClassTranslator.java +++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java @@ -10,7 +10,6 @@ ******************************************************************************/ package cuchaz.enigma.bytecode; -import java.util.HashSet; import java.util.Set; import javassist.ClassMap; @@ -20,6 +19,10 @@ import javassist.CtField; import javassist.CtMethod; import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; +import javassist.bytecode.InnerClassesAttribute; + +import com.beust.jcommander.internal.Sets; + import cuchaz.enigma.mapping.ClassEntry; import cuchaz.enigma.mapping.FieldEntry; import cuchaz.enigma.mapping.MethodEntry; @@ -133,25 +136,47 @@ public class ClassTranslator // translate all the class names referenced in the code // the above code only changed method/field/reference names and types, but not the class names themselves - Set classNames = getAllClassNames( c ); + Set classEntries = getAllClassEntries( c ); ClassMap map = new ClassMap(); - for( String className : classNames ) + for( ClassEntry obfClassEntry : classEntries ) { - String translatedName = m_translator.translateClass( className ); - if( translatedName != null ) - { - map.put( className, translatedName ); - } + map.put( obfClassEntry.getName(), m_translator.translateEntry( obfClassEntry ).getName() ); } - if( !map.isEmpty() ) + c.replaceClassName( map ); + + // translate the names in the InnerClasses attribute + InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag ); + if( attr != null ) { - c.replaceClassName( map ); + for( int i=0; i ATTR: %s,%s,%s", + obfClassEntry, deobfClassEntry, + attr.outerClass( i ), + attr.innerClass( i ), + attr.innerName( i ) + ) ); + */ + } } } - private Set getAllClassNames( CtClass c ) + private Set getAllClassEntries( CtClass c ) { - final Set names = new HashSet(); + final Set entries = Sets.newHashSet(); ClassMap map = new ClassMap( ) { @Override @@ -159,13 +184,13 @@ public class ClassTranslator { if( obj instanceof String ) { - names.add( (String)obj ); + entries.add( new ClassEntry( (String)obj ) ); } return null; } private static final long serialVersionUID = -202160293602070641L; }; c.replaceClassName( map ); - return names; + return entries; } } diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java index b0e33ac..c412b1a 100644 --- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java +++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java @@ -18,16 +18,14 @@ import javassist.bytecode.ConstPool; import javassist.bytecode.Descriptor; import javassist.bytecode.InnerClassesAttribute; import cuchaz.enigma.analysis.JarIndex; -import cuchaz.enigma.mapping.Translator; +import cuchaz.enigma.mapping.ClassEntry; public class InnerClassWriter { - private Translator m_deobfuscatingTranslator; private JarIndex m_jarIndex; - public InnerClassWriter( Translator deobfuscatingTranslator, JarIndex jarIndex ) + public InnerClassWriter( JarIndex jarIndex ) { - m_deobfuscatingTranslator = deobfuscatingTranslator; m_jarIndex = jarIndex; } @@ -44,7 +42,8 @@ public class InnerClassWriter else { // this is an inner class, rename it to outer$inner - c.setName( obfOuterClassName + "$" + obfClassName ); + ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfClassName ); + c.setName( obfClassEntry.getName() ); } // write the inner classes if needed @@ -62,31 +61,20 @@ public class InnerClassWriter for( String obfInnerClassName : obfInnerClassNames ) { // deobfuscate the class names - String deobfOuterClassName = m_deobfuscatingTranslator.translateClass( obfOuterClassName ); - if( deobfOuterClassName == null ) - { - deobfOuterClassName = obfOuterClassName; - } - String obfOuterInnerClassName = obfOuterClassName + "$" + obfInnerClassName; - String deobfOuterInnerClassName = m_deobfuscatingTranslator.translateClass( obfOuterInnerClassName ); - if( deobfOuterInnerClassName == null ) - { - deobfOuterInnerClassName = obfOuterInnerClassName; - } - String deobfInnerClassName = deobfOuterInnerClassName.substring( deobfOuterInnerClassName.lastIndexOf( '$' ) + 1 ); - + ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfInnerClassName ); + // here's what the JVM spec says about the InnerClasses attribute // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); // update the attribute with this inner class ConstPool constPool = c.getClassFile().getConstPool(); - int innerClassIndex = constPool.addClassInfo( deobfOuterInnerClassName ); + int innerClassIndex = constPool.addClassInfo( obfClassEntry.getName() ); int outerClassIndex = 0; int innerClassSimpleNameIndex = 0; if( !m_jarIndex.isAnonymousClass( obfInnerClassName ) ) { - outerClassIndex = constPool.addClassInfo( deobfOuterClassName ); - innerClassSimpleNameIndex = constPool.addUtf8Info( deobfInnerClassName ); + outerClassIndex = constPool.addClassInfo( obfClassEntry.getOuterClassName() ); + innerClassSimpleNameIndex = constPool.addUtf8Info( obfClassEntry.getInnerClassName() ); } attr.append( @@ -96,8 +84,18 @@ public class InnerClassWriter c.getClassFile().getAccessFlags() & ~AccessFlag.SUPER ); + /* DEBUG + System.out.println( String.format( "\tOBF: %s -> ATTR: %s,%s,%s (replace %s with %s)", + obfClassEntry, + attr.outerClass( attr.tableLength() - 1 ), + attr.innerClass( attr.tableLength() - 1 ), + attr.innerName( attr.tableLength() - 1 ), + obfInnerClassName, obfClassEntry.getName() + ) ); + */ + // make sure the outer class references only the new inner class names - c.replaceClassName( obfInnerClassName, deobfOuterInnerClassName ); + c.replaceClassName( obfInnerClassName, obfClassEntry.getName() ); } } } diff --git a/src/cuchaz/enigma/mapping/Translator.java b/src/cuchaz/enigma/mapping/Translator.java index 76f45cd..fc41f94 100644 --- a/src/cuchaz/enigma/mapping/Translator.java +++ b/src/cuchaz/enigma/mapping/Translator.java @@ -72,11 +72,21 @@ public class Translator public ClassEntry translateEntry( ClassEntry in ) { String name = translate( in ); - if( name == null ) + if( name != null ) + { + return new ClassEntry( name ); + } + + if( in.isInnerClass() ) { - return in; + // just translate the outer class name + String outerClassName = translate( in.getOuterClassEntry() ); + if( outerClassName != null ) + { + return new ClassEntry( outerClassName + "$" + in.getInnerClassName() ); + } } - return new ClassEntry( name ); + return in; } public String translate( FieldEntry in ) -- cgit v1.2.3