summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar jeff2014-08-23 23:43:31 -0400
committerGravatar jeff2014-08-23 23:43:31 -0400
commit8fa1741b621644ef84a9395a4c395d4ff3a89207 (patch)
tree9cc054e2636dd13a32950ad68dba212275d33026 /src
parentadded export command with progress bar (diff)
downloadenigma-fork-8fa1741b621644ef84a9395a4c395d4ff3a89207.tar.gz
enigma-fork-8fa1741b621644ef84a9395a4c395d4ff3a89207.tar.xz
enigma-fork-8fa1741b621644ef84a9395a4c395d4ff3a89207.zip
moved all classes from the default package into a package called "default" so they can be properly imported by other classes
Diffstat (limited to 'src')
-rw-r--r--src/cuchaz/enigma/Deobfuscator.java49
-rw-r--r--src/cuchaz/enigma/TranslatingTypeLoader.java19
-rw-r--r--src/cuchaz/enigma/analysis/JarClassIterator.java45
-rw-r--r--src/cuchaz/enigma/analysis/JarIndex.java40
-rw-r--r--src/cuchaz/enigma/bytecode/ClassRenamer.java115
-rw-r--r--src/cuchaz/enigma/bytecode/ClassTranslator.java64
-rw-r--r--src/cuchaz/enigma/bytecode/InnerClassWriter.java6
-rw-r--r--src/cuchaz/enigma/gui/Gui.java3
-rw-r--r--src/cuchaz/enigma/gui/GuiController.java9
-rw-r--r--src/cuchaz/enigma/gui/GuiTricks.java18
-rw-r--r--src/cuchaz/enigma/mapping/ClassEntry.java25
-rw-r--r--src/cuchaz/enigma/mapping/IllegalNameException.java22
-rw-r--r--src/cuchaz/enigma/mapping/NameValidator.java8
13 files changed, 298 insertions, 125 deletions
diff --git a/src/cuchaz/enigma/Deobfuscator.java b/src/cuchaz/enigma/Deobfuscator.java
index 5d87ad0..ee414fa 100644
--- a/src/cuchaz/enigma/Deobfuscator.java
+++ b/src/cuchaz/enigma/Deobfuscator.java
@@ -17,6 +17,8 @@ import java.io.StringWriter;
17import java.util.List; 17import java.util.List;
18import java.util.jar.JarFile; 18import java.util.jar.JarFile;
19 19
20import javassist.bytecode.Descriptor;
21
20import com.strobel.assembler.metadata.MetadataSystem; 22import com.strobel.assembler.metadata.MetadataSystem;
21import com.strobel.assembler.metadata.TypeDefinition; 23import com.strobel.assembler.metadata.TypeDefinition;
22import com.strobel.decompiler.DecompilerContext; 24import com.strobel.decompiler.DecompilerContext;
@@ -118,28 +120,30 @@ public class Deobfuscator
118 120
119 public void getSeparatedClasses( List<String> obfClasses, List<String> deobfClasses ) 121 public void getSeparatedClasses( List<String> obfClasses, List<String> deobfClasses )
120 { 122 {
121 for( String obfClassName : m_jarIndex.getObfClassNames() ) 123 for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() )
122 { 124 {
123 // skip inner classes 125 // skip inner classes
124 if( m_jarIndex.getOuterClass( obfClassName ) != null ) 126 if( m_jarIndex.getOuterClass( obfClassEntry.getName() ) != null )
125 { 127 {
126 continue; 128 continue;
127 } 129 }
128 130
129 // separate the classes 131 // separate the classes
130 ClassMapping classMapping = m_mappings.getClassByObf( obfClassName ); 132 ClassEntry deobfClassEntry = deobfuscateEntry( obfClassEntry );
131 if( classMapping != null && !classMapping.getObfName().equals( classMapping.getDeobfName() ) ) 133 if( !deobfClassEntry.equals( obfClassEntry ) )
132 { 134 {
133 deobfClasses.add( classMapping.getDeobfName() ); 135 // if the class has a mapping, clearly it's deobfuscated
136 deobfClasses.add( deobfClassEntry.getName() );
134 } 137 }
135 else if( obfClassName.indexOf( '/' ) >= 0 ) 138 else if( !obfClassEntry.getPackageName().equals( "default" ) )
136 { 139 {
137 // this class is in a package and therefore is not obfuscated 140 // also call it deobufscated if it's not in the "default" package
138 deobfClasses.add( obfClassName ); 141 deobfClasses.add( obfClassEntry.getName() );
139 } 142 }
140 else 143 else
141 { 144 {
142 obfClasses.add( obfClassName ); 145 // otherwise, assume it's still obfuscated
146 obfClasses.add( obfClassEntry.getName() );
143 } 147 }
144 } 148 }
145 } 149 }
@@ -198,7 +202,7 @@ public class Deobfuscator
198 public void writeSources( File dirOut, ProgressListener progress ) 202 public void writeSources( File dirOut, ProgressListener progress )
199 throws IOException 203 throws IOException
200 { 204 {
201 int numClasses = m_jarIndex.getObfClassNames().size(); 205 int numClasses = m_jarIndex.getObfClassEntries().size();
202 if( progress != null ) 206 if( progress != null )
203 { 207 {
204 progress.init( numClasses ); 208 progress.init( numClasses );
@@ -206,22 +210,22 @@ public class Deobfuscator
206 int i = 0; 210 int i = 0;
207 211
208 // DEOBFUSCATE ALL THE THINGS!! @_@ 212 // DEOBFUSCATE ALL THE THINGS!! @_@
209 for( String obfClassName : m_jarIndex.getObfClassNames() ) 213 for( ClassEntry obfClassEntry : m_jarIndex.getObfClassEntries() )
210 { 214 {
211 // skip inner classes 215 // skip inner classes
212 if( m_jarIndex.getOuterClass( obfClassName ) != null ) 216 if( obfClassEntry.isInnerClass() )
213 { 217 {
214 continue; 218 continue;
215 } 219 }
216 220
217 ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassName ) ); 221 ClassEntry deobfClassEntry = deobfuscateEntry( new ClassEntry( obfClassEntry ) );
218 if( progress != null ) 222 if( progress != null )
219 { 223 {
220 progress.onProgress( i++, deobfClassEntry.toString() ); 224 progress.onProgress( i++, deobfClassEntry.toString() );
221 } 225 }
222 226
223 // get the source 227 // get the source
224 String source = getSource( getSourceTree( obfClassName ) ); 228 String source = getSource( getSourceTree( obfClassEntry.getName() ) );
225 229
226 // write the file 230 // write the file
227 File file = new File( dirOut, deobfClassEntry.getName().replace( '.', '/' ) + ".java" ); 231 File file = new File( dirOut, deobfClassEntry.getName().replace( '.', '/' ) + ".java" );
@@ -284,7 +288,7 @@ public class Deobfuscator
284 { 288 {
285 if( obfEntry instanceof ClassEntry ) 289 if( obfEntry instanceof ClassEntry )
286 { 290 {
287 m_renamer.setClassName( (ClassEntry)obfEntry, newName ); 291 m_renamer.setClassName( (ClassEntry)obfEntry, Descriptor.toJvmName( newName ) );
288 } 292 }
289 else if( obfEntry instanceof FieldEntry ) 293 else if( obfEntry instanceof FieldEntry )
290 { 294 {
@@ -348,21 +352,18 @@ public class Deobfuscator
348 { 352 {
349 if( obfEntry instanceof ClassEntry ) 353 if( obfEntry instanceof ClassEntry )
350 { 354 {
351 if( obfEntry.getName().indexOf( '$' ) >= 0 ) 355 ClassEntry obfClassEntry = (ClassEntry)obfEntry;
356 if( obfClassEntry.isInnerClass() )
352 { 357 {
353 String[] parts = obfEntry.getName().split( "\\$" );
354 assert( parts.length == 2 ); // not supporting recursively-nested classes
355 String outerClassName = parts[0];
356 String innerClassName = parts[1];
357
358 // both classes must be in the list 358 // both classes must be in the list
359 return m_jarIndex.getObfClassNames().contains( outerClassName ) 359 return m_jarIndex.getObfClassEntries().contains( obfClassEntry.getOuterClassEntry() )
360 && m_jarIndex.getObfClassNames().contains( innerClassName ); 360 && m_jarIndex.getObfClassEntries().contains( obfClassEntry.getInnerClassName() );
361 // TODO: make sure this works for the inner class!!
361 } 362 }
362 else 363 else
363 { 364 {
364 // class must be in the list 365 // class must be in the list
365 return m_jarIndex.getObfClassNames().contains( obfEntry.getName() ); 366 return m_jarIndex.getObfClassEntries().contains( obfEntry );
366 } 367 }
367 } 368 }
368 else 369 else
diff --git a/src/cuchaz/enigma/TranslatingTypeLoader.java b/src/cuchaz/enigma/TranslatingTypeLoader.java
index cc86364..e7e941a 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.Arrays;
16import java.util.Map; 17import java.util.Map;
17import java.util.jar.JarEntry; 18import java.util.jar.JarEntry;
18import java.util.jar.JarFile; 19import java.util.jar.JarFile;
@@ -30,6 +31,7 @@ import com.strobel.assembler.metadata.ITypeLoader;
30 31
31import cuchaz.enigma.analysis.BridgeFixer; 32import cuchaz.enigma.analysis.BridgeFixer;
32import cuchaz.enigma.analysis.JarIndex; 33import cuchaz.enigma.analysis.JarIndex;
34import cuchaz.enigma.bytecode.ClassRenamer;
33import cuchaz.enigma.bytecode.ClassTranslator; 35import cuchaz.enigma.bytecode.ClassTranslator;
34import cuchaz.enigma.bytecode.InnerClassWriter; 36import cuchaz.enigma.bytecode.InnerClassWriter;
35import cuchaz.enigma.bytecode.MethodParameterWriter; 37import cuchaz.enigma.bytecode.MethodParameterWriter;
@@ -99,22 +101,29 @@ public class TranslatingTypeLoader implements ITypeLoader
99 return null; 101 return null;
100 } 102 }
101 103
102 /* DEBUG 104 // DEBUG
103 if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) ) 105 if( !Arrays.asList( "java", "org", "io" ).contains( deobfClassName.split( "/" )[0] ) )
104 { 106 {
105 System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) ); 107 System.out.println( String.format( "Looking for %s (%s)", deobfClassEntry.getName(), obfClassEntry.getName() ) );
106 } 108 }
107 */ 109 //
108 110
109 // get the jar entry 111 // get the jar entry
110 String classFileName; 112 String classFileName;
111 if( obfClassEntry.isInnerClass() ) 113 if( obfClassEntry.isInnerClass() )
112 { 114 {
115 // use just the inner class simple name for inner classes
113 classFileName = obfClassEntry.getInnerClassName(); 116 classFileName = obfClassEntry.getInnerClassName();
114 } 117 }
118 else if( obfClassEntry.getPackageName().equals( "default" ) )
119 {
120 // use the outer class simple name for classes in the "default" package
121 classFileName = obfClassEntry.getSimpleName();
122 }
115 else 123 else
116 { 124 {
117 classFileName = obfClassEntry.getOuterClassName(); 125 // otherwise, just use the class name (ie for classes in packages)
126 classFileName = obfClassEntry.getName();
118 } 127 }
119 JarEntry entry = m_jar.getJarEntry( classFileName + ".class" ); 128 JarEntry entry = m_jar.getJarEntry( classFileName + ".class" );
120 if( entry == null ) 129 if( entry == null )
@@ -147,6 +156,10 @@ public class TranslatingTypeLoader implements ITypeLoader
147 classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) ); 156 classPool.insertClassPath( new ByteArrayClassPath( javaClassFileName, buf ) );
148 CtClass c = classPool.get( javaClassFileName ); 157 CtClass c = classPool.get( javaClassFileName );
149 158
159 // we moved a lot of classes out of the default package into the "default" package
160 // make sure all the class references are consistent
161 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, "default" );
162
150 // reconstruct inner classes 163 // reconstruct inner classes
151 new InnerClassWriter( m_jarIndex ).write( c ); 164 new InnerClassWriter( m_jarIndex ).write( c );
152 165
diff --git a/src/cuchaz/enigma/analysis/JarClassIterator.java b/src/cuchaz/enigma/analysis/JarClassIterator.java
index cf6df80..6c9f124 100644
--- a/src/cuchaz/enigma/analysis/JarClassIterator.java
+++ b/src/cuchaz/enigma/analysis/JarClassIterator.java
@@ -28,6 +28,7 @@ import javassist.bytecode.Descriptor;
28import com.beust.jcommander.internal.Lists; 28import com.beust.jcommander.internal.Lists;
29 29
30import cuchaz.enigma.Constants; 30import cuchaz.enigma.Constants;
31import cuchaz.enigma.mapping.ClassEntry;
31 32
32public class JarClassIterator implements Iterator<CtClass> 33public class JarClassIterator implements Iterator<CtClass>
33{ 34{
@@ -36,13 +37,22 @@ public class JarClassIterator implements Iterator<CtClass>
36 37
37 public JarClassIterator( JarFile jar ) 38 public JarClassIterator( JarFile jar )
38 { 39 {
39 this( jar, getClassEntries( jar ) );
40 }
41
42 public JarClassIterator( JarFile jar, List<JarEntry> entries )
43 {
44 m_jar = jar; 40 m_jar = jar;
45 m_iter = entries.iterator(); 41
42 // get the jar entries that correspond to classes
43 List<JarEntry> classEntries = Lists.newArrayList();
44 Enumeration<JarEntry> entries = m_jar.entries();
45 while( entries.hasMoreElements() )
46 {
47 JarEntry entry = entries.nextElement();
48
49 // is this a class file?
50 if( entry.getName().endsWith( ".class" ) )
51 {
52 classEntries.add( entry );
53 }
54 }
55 m_iter = classEntries.iterator();
46 } 56 }
47 57
48 @Override 58 @Override
@@ -79,19 +89,13 @@ public class JarClassIterator implements Iterator<CtClass>
79 } 89 }
80 } 90 }
81 91
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 92 // get a javassist handle for the class
93 String className = Descriptor.toJavaName( getClassEntry( entry ).getName() );
86 ClassPool classPool = new ClassPool(); 94 ClassPool classPool = new ClassPool();
87 classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) ); 95 classPool.insertClassPath( new ByteArrayClassPath( className, bos.toByteArray() ) );
88 return classPool.get( className ); 96 return classPool.get( className );
89 } 97 }
90 catch( IOException ex ) 98 catch( IOException | NotFoundException ex )
91 {
92 throw new Error( "Unable to read class: " + entry.getName() );
93 }
94 catch( NotFoundException ex )
95 { 99 {
96 throw new Error( "Unable to load class: " + entry.getName() ); 100 throw new Error( "Unable to load class: " + entry.getName() );
97 } 101 }
@@ -103,9 +107,9 @@ public class JarClassIterator implements Iterator<CtClass>
103 throw new UnsupportedOperationException(); 107 throw new UnsupportedOperationException();
104 } 108 }
105 109
106 public static List<JarEntry> getClassEntries( JarFile jar ) 110 public static List<ClassEntry> getClassEntries( JarFile jar )
107 { 111 {
108 List<JarEntry> classes = Lists.newArrayList(); 112 List<ClassEntry> classEntries = Lists.newArrayList();
109 Enumeration<JarEntry> entries = jar.entries(); 113 Enumeration<JarEntry> entries = jar.entries();
110 while( entries.hasMoreElements() ) 114 while( entries.hasMoreElements() )
111 { 115 {
@@ -114,10 +118,10 @@ public class JarClassIterator implements Iterator<CtClass>
114 // is this a class file? 118 // is this a class file?
115 if( !entry.isDirectory() && entry.getName().endsWith( ".class" ) ) 119 if( !entry.isDirectory() && entry.getName().endsWith( ".class" ) )
116 { 120 {
117 classes.add( entry ); 121 classEntries.add( getClassEntry( entry ) );
118 } 122 }
119 } 123 }
120 return classes; 124 return classEntries;
121 } 125 }
122 126
123 public static Iterable<CtClass> classes( final JarFile jar ) 127 public static Iterable<CtClass> classes( final JarFile jar )
@@ -131,4 +135,9 @@ public class JarClassIterator implements Iterator<CtClass>
131 } 135 }
132 }; 136 };
133 } 137 }
138
139 private static ClassEntry getClassEntry( JarEntry entry )
140 {
141 return new ClassEntry( entry.getName().substring( 0, entry.getName().length() - ".class".length() ) );
142 }
134} 143}
diff --git a/src/cuchaz/enigma/analysis/JarIndex.java b/src/cuchaz/enigma/analysis/JarIndex.java
index 315a286..cdaca37 100644
--- a/src/cuchaz/enigma/analysis/JarIndex.java
+++ b/src/cuchaz/enigma/analysis/JarIndex.java
@@ -17,7 +17,6 @@ import java.util.Iterator;
17import java.util.List; 17import java.util.List;
18import java.util.Map; 18import java.util.Map;
19import java.util.Set; 19import java.util.Set;
20import java.util.jar.JarEntry;
21import java.util.jar.JarFile; 20import java.util.jar.JarFile;
22 21
23import javassist.CannotCompileException; 22import javassist.CannotCompileException;
@@ -35,13 +34,12 @@ import javassist.expr.MethodCall;
35import javassist.expr.NewExpr; 34import javassist.expr.NewExpr;
36 35
37import com.google.common.collect.HashMultimap; 36import com.google.common.collect.HashMultimap;
38import com.google.common.collect.HashMultiset;
39import com.google.common.collect.Lists; 37import com.google.common.collect.Lists;
40import com.google.common.collect.Maps; 38import com.google.common.collect.Maps;
41import com.google.common.collect.Multimap; 39import com.google.common.collect.Multimap;
42import com.google.common.collect.Multiset;
43import com.google.common.collect.Sets; 40import com.google.common.collect.Sets;
44 41
42import cuchaz.enigma.bytecode.ClassRenamer;
45import cuchaz.enigma.mapping.ArgumentEntry; 43import cuchaz.enigma.mapping.ArgumentEntry;
46import cuchaz.enigma.mapping.BehaviorEntry; 44import cuchaz.enigma.mapping.BehaviorEntry;
47import cuchaz.enigma.mapping.ClassEntry; 45import cuchaz.enigma.mapping.ClassEntry;
@@ -53,7 +51,7 @@ import cuchaz.enigma.mapping.Translator;
53 51
54public class JarIndex 52public class JarIndex
55{ 53{
56 private Set<String> m_obfClassNames; 54 private Set<ClassEntry> m_obfClassEntries;
57 private Ancestries m_ancestries; 55 private Ancestries m_ancestries;
58 private Multimap<String,MethodEntry> m_methodImplementations; 56 private Multimap<String,MethodEntry> m_methodImplementations;
59 private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences; 57 private Multimap<BehaviorEntry,EntryReference<BehaviorEntry,BehaviorEntry>> m_behaviorReferences;
@@ -64,7 +62,7 @@ public class JarIndex
64 62
65 public JarIndex( ) 63 public JarIndex( )
66 { 64 {
67 m_obfClassNames = Sets.newHashSet(); 65 m_obfClassEntries = Sets.newHashSet();
68 m_ancestries = new Ancestries(); 66 m_ancestries = new Ancestries();
69 m_methodImplementations = HashMultimap.create(); 67 m_methodImplementations = HashMultimap.create();
70 m_behaviorReferences = HashMultimap.create(); 68 m_behaviorReferences = HashMultimap.create();
@@ -77,15 +75,20 @@ public class JarIndex
77 public void indexJar( JarFile jar ) 75 public void indexJar( JarFile jar )
78 { 76 {
79 // pass 1: read the class names 77 // pass 1: read the class names
80 for( JarEntry entry : JarClassIterator.getClassEntries( jar ) ) 78 for( ClassEntry classEntry : JarClassIterator.getClassEntries( jar ) )
81 { 79 {
82 String className = entry.getName().substring( 0, entry.getName().length() - 6 ); 80 if( classEntry.isInDefaultPackage() )
83 m_obfClassNames.add( Descriptor.toJvmName( className ) ); 81 {
82 // move out of default package
83 classEntry = new ClassEntry( "default/" + classEntry.getName() );
84 }
85 m_obfClassEntries.add( classEntry );
84 } 86 }
85 87
86 // pass 2: index the types, methods 88 // pass 2: index the types, methods
87 for( CtClass c : JarClassIterator.classes( jar ) ) 89 for( CtClass c : JarClassIterator.classes( jar ) )
88 { 90 {
91 fixClass( c );
89 m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() ); 92 m_ancestries.addSuperclass( c.getName(), c.getClassFile().getSuperclass() );
90 for( CtBehavior behavior : c.getDeclaredBehaviors() ) 93 for( CtBehavior behavior : c.getDeclaredBehaviors() )
91 { 94 {
@@ -96,8 +99,10 @@ public class JarIndex
96 // pass 2: index inner classes and anonymous classes 99 // pass 2: index inner classes and anonymous classes
97 for( CtClass c : JarClassIterator.classes( jar ) ) 100 for( CtClass c : JarClassIterator.classes( jar ) )
98 { 101 {
102 fixClass( c );
103
99 String outerClassName = findOuterClass( c ); 104 String outerClassName = findOuterClass( c );
100 if( outerClassName != null )// /* TEMP */ && false ) 105 if( outerClassName != null )
101 { 106 {
102 String innerClassName = Descriptor.toJvmName( c.getName() ); 107 String innerClassName = Descriptor.toJvmName( c.getName() );
103 m_innerClasses.put( outerClassName, innerClassName ); 108 m_innerClasses.put( outerClassName, innerClassName );
@@ -127,6 +132,17 @@ public class JarIndex
127 renameClasses( renames ); 132 renameClasses( renames );
128 } 133 }
129 134
135 private void fixClass( CtClass c )
136 {
137 ClassEntry classEntry = new ClassEntry( Descriptor.toJvmName( c.getName() ) );
138 if( classEntry.isInDefaultPackage() )
139 {
140 // move class out of default package
141 classEntry = new ClassEntry( "default/" + classEntry.getName() );
142 ClassRenamer.moveAllClassesOutOfDefaultPackage( c, "default" );
143 }
144 }
145
130 private void indexBehavior( CtBehavior behavior ) 146 private void indexBehavior( CtBehavior behavior )
131 { 147 {
132 // get the method entry 148 // get the method entry
@@ -270,7 +286,7 @@ public class JarIndex
270 else if( callerClasses.size() > 1 ) 286 else if( callerClasses.size() > 1 )
271 { 287 {
272 // TEMP 288 // TEMP
273 System.out.println( "WARNING: Illegal class called by more than one class!" + callerClasses ); 289 System.out.println( "WARNING: Illegal constructor called by more than one class!" + callerClasses );
274 } 290 }
275 } 291 }
276 292
@@ -423,9 +439,9 @@ public class JarIndex
423 return true; 439 return true;
424 } 440 }
425 441
426 public Set<String> getObfClassNames( ) 442 public Set<ClassEntry> getObfClassEntries( )
427 { 443 {
428 return m_obfClassNames; 444 return m_obfClassEntries;
429 } 445 }
430 446
431 public Ancestries getAncestries( ) 447 public Ancestries getAncestries( )
diff --git a/src/cuchaz/enigma/bytecode/ClassRenamer.java b/src/cuchaz/enigma/bytecode/ClassRenamer.java
new file mode 100644
index 0000000..cba5861
--- /dev/null
+++ b/src/cuchaz/enigma/bytecode/ClassRenamer.java
@@ -0,0 +1,115 @@
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.Map;
14import java.util.Set;
15
16import javassist.ClassMap;
17import javassist.CtClass;
18import javassist.bytecode.ConstPool;
19import javassist.bytecode.Descriptor;
20import javassist.bytecode.InnerClassesAttribute;
21
22import com.beust.jcommander.internal.Sets;
23import com.google.common.collect.Maps;
24
25import cuchaz.enigma.mapping.ClassEntry;
26
27public class ClassRenamer
28{
29 public static void renameClasses( CtClass c, Map<ClassEntry,ClassEntry> map )
30 {
31 // build the map used by javassist
32 ClassMap nameMap = new ClassMap();
33 for( Map.Entry<ClassEntry,ClassEntry> entry : map.entrySet() )
34 {
35 nameMap.put( entry.getKey().getName(), entry.getValue().getName() );
36 }
37 c.replaceClassName( nameMap );
38
39 // translate the names in the InnerClasses attribute
40 ConstPool constants = c.getClassFile().getConstPool();
41 InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag );
42 if( attr != null )
43 {
44 for( int i=0; i<attr.tableLength(); i++ )
45 {
46 ClassEntry inClassEntry = new ClassEntry( Descriptor.toJvmName( attr.innerClass( i ) ) );
47 ClassEntry outClassEntry = map.get( inClassEntry );
48 attr.setInnerClassIndex( i, constants.addClassInfo( outClassEntry.getName() ) );
49 if( attr.outerClassIndex( i ) != 0 )
50 {
51 attr.setOuterClassIndex( i, constants.addClassInfo( outClassEntry.getOuterClassName() ) );
52 }
53 if( attr.innerNameIndex( i ) != 0 )
54 {
55 attr.setInnerNameIndex( i, constants.addUtf8Info( outClassEntry.getInnerClassName() ) );
56 }
57
58 /* DEBUG
59 System.out.println( String.format( "\tOBF: %s DEOBF: %s-> ATTR: %s,%s,%s",
60 obfClassEntry, deobfClassEntry,
61 attr.outerClass( i ),
62 attr.innerClass( i ),
63 attr.innerName( i )
64 ) );
65 */
66 }
67 }
68 }
69
70 public static Set<ClassEntry> getAllClassEntries( CtClass c )
71 {
72 // get the classes that javassist knows about
73 final Set<ClassEntry> entries = Sets.newHashSet();
74 ClassMap map = new ClassMap( )
75 {
76 @Override
77 public Object get( Object obj )
78 {
79 if( obj instanceof String )
80 {
81 entries.add( new ClassEntry( (String)obj ) );
82 }
83 return null;
84 }
85 private static final long serialVersionUID = -202160293602070641L;
86 };
87 c.replaceClassName( map );
88
89 // also check InnerClassesAttribute
90 InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag );
91 if( attr != null )
92 {
93 for( int i=0; i<attr.tableLength(); i++ )
94 {
95 entries.add( new ClassEntry( Descriptor.toJvmName( attr.innerClass( i ) ) ) );
96 }
97 }
98
99 return entries;
100 }
101
102 public static void moveAllClassesOutOfDefaultPackage( CtClass c, String newPackageName )
103 {
104 // rename all classes
105 Map<ClassEntry,ClassEntry> map = Maps.newHashMap();
106 for( ClassEntry classEntry : ClassRenamer.getAllClassEntries( c ) )
107 {
108 if( classEntry.isInDefaultPackage() )
109 {
110 map.put( classEntry, new ClassEntry( newPackageName + "/" + classEntry.getName() ) );
111 }
112 }
113 ClassRenamer.renameClasses( c, map );
114 }
115}
diff --git a/src/cuchaz/enigma/bytecode/ClassTranslator.java b/src/cuchaz/enigma/bytecode/ClassTranslator.java
index 9ce06a5..885b45f 100644
--- a/src/cuchaz/enigma/bytecode/ClassTranslator.java
+++ b/src/cuchaz/enigma/bytecode/ClassTranslator.java
@@ -10,18 +10,16 @@
10 ******************************************************************************/ 10 ******************************************************************************/
11package cuchaz.enigma.bytecode; 11package cuchaz.enigma.bytecode;
12 12
13import java.util.Set; 13import java.util.Map;
14 14
15import javassist.ClassMap;
16import javassist.CtBehavior; 15import javassist.CtBehavior;
17import javassist.CtClass; 16import javassist.CtClass;
18import javassist.CtField; 17import javassist.CtField;
19import javassist.CtMethod; 18import javassist.CtMethod;
20import javassist.bytecode.ConstPool; 19import javassist.bytecode.ConstPool;
21import javassist.bytecode.Descriptor; 20import javassist.bytecode.Descriptor;
22import javassist.bytecode.InnerClassesAttribute;
23 21
24import com.beust.jcommander.internal.Sets; 22import com.beust.jcommander.internal.Maps;
25 23
26import cuchaz.enigma.mapping.ClassEntry; 24import cuchaz.enigma.mapping.ClassEntry;
27import cuchaz.enigma.mapping.FieldEntry; 25import cuchaz.enigma.mapping.FieldEntry;
@@ -136,61 +134,11 @@ public class ClassTranslator
136 134
137 // translate all the class names referenced in the code 135 // translate all the class names referenced in the code
138 // the above code only changed method/field/reference names and types, but not the class names themselves 136 // the above code only changed method/field/reference names and types, but not the class names themselves
139 Set<ClassEntry> classEntries = getAllClassEntries( c ); 137 Map<ClassEntry,ClassEntry> map = Maps.newHashMap();
140 ClassMap map = new ClassMap(); 138 for( ClassEntry obfClassEntry : ClassRenamer.getAllClassEntries( c ) )
141 for( ClassEntry obfClassEntry : classEntries )
142 { 139 {
143 map.put( obfClassEntry.getName(), m_translator.translateEntry( obfClassEntry ).getName() ); 140 map.put( obfClassEntry, m_translator.translateEntry( obfClassEntry ) );
144 } 141 }
145 c.replaceClassName( map ); 142 ClassRenamer.renameClasses( c, map );
146
147 // translate the names in the InnerClasses attribute
148 InnerClassesAttribute attr = (InnerClassesAttribute)c.getClassFile().getAttribute( InnerClassesAttribute.tag );
149 if( attr != null )
150 {
151 for( int i=0; i<attr.tableLength(); i++ )
152 {
153 ClassEntry obfClassEntry = new ClassEntry( Descriptor.toJvmName( attr.innerClass( i ) ) );
154 ClassEntry deobfClassEntry = m_translator.translateEntry( obfClassEntry );
155 attr.setInnerClassIndex( i, constants.addClassInfo( deobfClassEntry.getName() ) );
156 if( attr.outerClassIndex( i ) != 0 )
157 {
158 attr.setOuterClassIndex( i, constants.addClassInfo( deobfClassEntry.getOuterClassName() ) );
159 }
160 if( attr.innerNameIndex( i ) != 0 )
161 {
162 attr.setInnerNameIndex( i, constants.addUtf8Info( deobfClassEntry.getInnerClassName() ) );
163 }
164
165 /* DEBUG
166 System.out.println( String.format( "\tOBF: %s DEOBF: %s-> ATTR: %s,%s,%s",
167 obfClassEntry, deobfClassEntry,
168 attr.outerClass( i ),
169 attr.innerClass( i ),
170 attr.innerName( i )
171 ) );
172 */
173 }
174 }
175 }
176
177 private Set<ClassEntry> getAllClassEntries( CtClass c )
178 {
179 final Set<ClassEntry> entries = Sets.newHashSet();
180 ClassMap map = new ClassMap( )
181 {
182 @Override
183 public Object get( Object obj )
184 {
185 if( obj instanceof String )
186 {
187 entries.add( new ClassEntry( (String)obj ) );
188 }
189 return null;
190 }
191 private static final long serialVersionUID = -202160293602070641L;
192 };
193 c.replaceClassName( map );
194 return entries;
195 } 143 }
196} 144}
diff --git a/src/cuchaz/enigma/bytecode/InnerClassWriter.java b/src/cuchaz/enigma/bytecode/InnerClassWriter.java
index c412b1a..2fb5fe0 100644
--- a/src/cuchaz/enigma/bytecode/InnerClassWriter.java
+++ b/src/cuchaz/enigma/bytecode/InnerClassWriter.java
@@ -42,7 +42,7 @@ public class InnerClassWriter
42 else 42 else
43 { 43 {
44 // this is an inner class, rename it to outer$inner 44 // this is an inner class, rename it to outer$inner
45 ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfClassName ); 45 ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfClassName ).getSimpleName() );
46 c.setName( obfClassEntry.getName() ); 46 c.setName( obfClassEntry.getName() );
47 } 47 }
48 48
@@ -60,8 +60,8 @@ public class InnerClassWriter
60 c.getClassFile().addAttribute( attr ); 60 c.getClassFile().addAttribute( attr );
61 for( String obfInnerClassName : obfInnerClassNames ) 61 for( String obfInnerClassName : obfInnerClassNames )
62 { 62 {
63 // deobfuscate the class names 63 // get the new inner class name
64 ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + obfInnerClassName ); 64 ClassEntry obfClassEntry = new ClassEntry( obfOuterClassName + "$" + new ClassEntry( obfInnerClassName ).getSimpleName() );
65 65
66 // here's what the JVM spec says about the InnerClasses attribute 66 // here's what the JVM spec says about the InnerClasses attribute
67 // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags ); 67 // append( inner, outer of inner if inner is member of outer 0 ow, name after $ if inner not anonymous 0 ow, flags );
diff --git a/src/cuchaz/enigma/gui/Gui.java b/src/cuchaz/enigma/gui/Gui.java
index febdfd4..4e63606 100644
--- a/src/cuchaz/enigma/gui/Gui.java
+++ b/src/cuchaz/enigma/gui/Gui.java
@@ -1073,8 +1073,9 @@ public class Gui
1073 } 1073 }
1074 catch( IllegalNameException ex ) 1074 catch( IllegalNameException ex )
1075 { 1075 {
1076 ex.printStackTrace( System.err );
1077 text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) ); 1076 text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) );
1077 text.setToolTipText( ex.getReason() );
1078 GuiTricks.showToolTipNow( text );
1078 } 1079 }
1079 return; 1080 return;
1080 } 1081 }
diff --git a/src/cuchaz/enigma/gui/GuiController.java b/src/cuchaz/enigma/gui/GuiController.java
index cf4f002..90bce52 100644
--- a/src/cuchaz/enigma/gui/GuiController.java
+++ b/src/cuchaz/enigma/gui/GuiController.java
@@ -114,16 +114,19 @@ public class GuiController
114 @Override 114 @Override
115 public void run( ) 115 public void run( )
116 { 116 {
117 ProgressDialog progress = new ProgressDialog( m_gui.getFrame() );
117 try 118 try
118 { 119 {
119 ProgressDialog progress = new ProgressDialog( m_gui.getFrame() );
120 m_deobfuscator.writeSources( dirOut, progress ); 120 m_deobfuscator.writeSources( dirOut, progress );
121 progress.close();
122 } 121 }
123 catch( IOException ex ) 122 catch( Exception ex )
124 { 123 {
125 throw new Error( ex ); 124 throw new Error( ex );
126 } 125 }
126 finally
127 {
128 progress.close();
129 }
127 } 130 }
128 }.start(); 131 }.start();
129 } 132 }
diff --git a/src/cuchaz/enigma/gui/GuiTricks.java b/src/cuchaz/enigma/gui/GuiTricks.java
index c79f432..9b889ef 100644
--- a/src/cuchaz/enigma/gui/GuiTricks.java
+++ b/src/cuchaz/enigma/gui/GuiTricks.java
@@ -11,8 +11,11 @@
11package cuchaz.enigma.gui; 11package cuchaz.enigma.gui;
12 12
13import java.awt.Font; 13import java.awt.Font;
14import java.awt.event.MouseEvent;
14 15
16import javax.swing.JComponent;
15import javax.swing.JLabel; 17import javax.swing.JLabel;
18import javax.swing.ToolTipManager;
16 19
17public class GuiTricks 20public class GuiTricks
18{ 21{
@@ -22,4 +25,19 @@ public class GuiTricks
22 label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) ); 25 label.setFont( font.deriveFont( font.getStyle() & ~Font.BOLD ) );
23 return label; 26 return label;
24 } 27 }
28
29 public static void showToolTipNow( JComponent component )
30 {
31 // HACKHACK: trick the tooltip manager into showing the tooltip right now
32 ToolTipManager manager = ToolTipManager.sharedInstance();
33 int oldDelay = manager.getInitialDelay();
34 manager.setInitialDelay( 0 );
35 manager.mouseMoved( new MouseEvent(
36 component,
37 MouseEvent.MOUSE_MOVED,
38 System.currentTimeMillis(),
39 0, 0, 0, 0, false
40 ) );
41 manager.setInitialDelay( oldDelay );
42 }
25} 43}
diff --git a/src/cuchaz/enigma/mapping/ClassEntry.java b/src/cuchaz/enigma/mapping/ClassEntry.java
index dad6da9..fdb7c2c 100644
--- a/src/cuchaz/enigma/mapping/ClassEntry.java
+++ b/src/cuchaz/enigma/mapping/ClassEntry.java
@@ -110,4 +110,29 @@ public class ClassEntry implements Entry, Serializable
110 { 110 {
111 return new ClassEntry( getOuterClassName() ); 111 return new ClassEntry( getOuterClassName() );
112 } 112 }
113
114 public boolean isInDefaultPackage( )
115 {
116 return m_name.indexOf( '/' ) < 0;
117 }
118
119 public String getPackageName( )
120 {
121 int pos = m_name.lastIndexOf( '/' );
122 if( pos > 0 )
123 {
124 return m_name.substring( 0, pos );
125 }
126 return null;
127 }
128
129 public String getSimpleName( )
130 {
131 int pos = m_name.lastIndexOf( '/' );
132 if( pos > 0 )
133 {
134 return m_name.substring( pos + 1 );
135 }
136 return m_name;
137 }
113} 138}
diff --git a/src/cuchaz/enigma/mapping/IllegalNameException.java b/src/cuchaz/enigma/mapping/IllegalNameException.java
index 560e5d9..830f05c 100644
--- a/src/cuchaz/enigma/mapping/IllegalNameException.java
+++ b/src/cuchaz/enigma/mapping/IllegalNameException.java
@@ -15,15 +15,35 @@ public class IllegalNameException extends RuntimeException
15 private static final long serialVersionUID = -2279910052561114323L; 15 private static final long serialVersionUID = -2279910052561114323L;
16 16
17 private String m_name; 17 private String m_name;
18 private String m_reason;
18 19
19 public IllegalNameException( String name ) 20 public IllegalNameException( String name )
20 { 21 {
22 this( name, null );
23 }
24
25 public IllegalNameException( String name, String reason )
26 {
21 m_name = name; 27 m_name = name;
28 m_reason = reason;
29 }
30
31 public String getReason( )
32 {
33 return m_reason;
22 } 34 }
23 35
24 @Override 36 @Override
25 public String getMessage( ) 37 public String getMessage( )
26 { 38 {
27 return "Illegal name: " + m_name; 39 StringBuilder buf = new StringBuilder();
40 buf.append( "Illegal name: " );
41 buf.append( m_name );
42 if( m_reason != null )
43 {
44 buf.append( " because " );
45 buf.append( m_reason );
46 }
47 return buf.toString();
28 } 48 }
29} 49}
diff --git a/src/cuchaz/enigma/mapping/NameValidator.java b/src/cuchaz/enigma/mapping/NameValidator.java
index a8421fa..6df893f 100644
--- a/src/cuchaz/enigma/mapping/NameValidator.java
+++ b/src/cuchaz/enigma/mapping/NameValidator.java
@@ -59,7 +59,11 @@ public class NameValidator
59 { 59 {
60 if( name == null || !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) 60 if( name == null || !ClassPattern.matcher( name ).matches() || ReservedWords.contains( name ) )
61 { 61 {
62 throw new IllegalNameException( name ); 62 throw new IllegalNameException( name, "This doesn't look like a legal class name" );
63 }
64 if( new ClassEntry( name ).getPackageName() == null )
65 {
66 throw new IllegalNameException( name, "Classes must be in a package" );
63 } 67 }
64 return Descriptor.toJvmName( name ); 68 return Descriptor.toJvmName( name );
65 } 69 }
@@ -68,7 +72,7 @@ public class NameValidator
68 { 72 {
69 if( name == null || !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) ) 73 if( name == null || !IdentifierPattern.matcher( name ).matches() || ReservedWords.contains( name ) )
70 { 74 {
71 throw new IllegalNameException( name ); 75 throw new IllegalNameException( name, "This doesn't look like a legal identifier" );
72 } 76 }
73 return name; 77 return name;
74 } 78 }