diff options
8 files changed, 158 insertions, 35 deletions
| @@ -150,6 +150,7 @@ flycheck_*.el | |||
| 150 | 150 | ||
| 151 | # IntelliJ | 151 | # IntelliJ |
| 152 | /out/ | 152 | /out/ |
| 153 | .idea | ||
| 153 | 154 | ||
| 154 | # mpeltonen/sbt-idea plugin | 155 | # mpeltonen/sbt-idea plugin |
| 155 | .idea_modules/ | 156 | .idea_modules/ |
diff --git a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java new file mode 100644 index 00000000..fca2a47e --- /dev/null +++ b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.Buffer; | ||
| 4 | import com.strobel.assembler.metadata.ClasspathTypeLoader; | ||
| 5 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 6 | |||
| 7 | /** | ||
| 8 | * Caching version of {@link ClasspathTypeLoader} | ||
| 9 | */ | ||
| 10 | class CachingClasspathTypeLoader extends CachingTypeLoader { | ||
| 11 | private final ITypeLoader classpathLoader = new ClasspathTypeLoader(); | ||
| 12 | |||
| 13 | protected byte[] doLoad(String className) { | ||
| 14 | Buffer parentBuf = new Buffer(); | ||
| 15 | if (classpathLoader.tryLoadType(className, parentBuf)) { | ||
| 16 | return parentBuf.array(); | ||
| 17 | } | ||
| 18 | return EMPTY_ARRAY;//need to return *something* as null means no store | ||
| 19 | } | ||
| 20 | } | ||
diff --git a/src/main/java/cuchaz/enigma/CachingTypeLoader.java b/src/main/java/cuchaz/enigma/CachingTypeLoader.java new file mode 100644 index 00000000..22c31c63 --- /dev/null +++ b/src/main/java/cuchaz/enigma/CachingTypeLoader.java | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | import com.google.common.collect.Maps; | ||
| 4 | import com.strobel.assembler.metadata.Buffer; | ||
| 5 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 6 | |||
| 7 | import java.util.Map; | ||
| 8 | |||
| 9 | /** | ||
| 10 | * Common cache functions | ||
| 11 | */ | ||
| 12 | public abstract class CachingTypeLoader implements ITypeLoader { | ||
| 13 | protected static final byte[] EMPTY_ARRAY = {}; | ||
| 14 | |||
| 15 | private final Map<String, byte[]> cache = Maps.newHashMap(); | ||
| 16 | |||
| 17 | protected abstract byte[] doLoad(String className); | ||
| 18 | |||
| 19 | @Override | ||
| 20 | public boolean tryLoadType(String className, Buffer out) { | ||
| 21 | |||
| 22 | // check the cache | ||
| 23 | byte[] data = this.cache.computeIfAbsent(className, this::doLoad); | ||
| 24 | |||
| 25 | if (data == EMPTY_ARRAY) { | ||
| 26 | return false; | ||
| 27 | } | ||
| 28 | |||
| 29 | out.reset(data.length); | ||
| 30 | System.arraycopy(data, 0, out.array(), out.position(), data.length); | ||
| 31 | out.position(0); | ||
| 32 | return true; | ||
| 33 | } | ||
| 34 | |||
| 35 | public void clearCache() { | ||
| 36 | this.cache.clear(); | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java index 0e037536..b2cecfe2 100644 --- a/src/main/java/cuchaz/enigma/Deobfuscator.java +++ b/src/main/java/cuchaz/enigma/Deobfuscator.java | |||
| @@ -163,6 +163,10 @@ public class Deobfuscator { | |||
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | public CompilationUnit getSourceTree(String className) { | 165 | public CompilationUnit getSourceTree(String className) { |
| 166 | return getSourceTree(className, createTypeLoader()); | ||
| 167 | } | ||
| 168 | |||
| 169 | public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader) { | ||
| 166 | 170 | ||
| 167 | // we don't know if this class name is obfuscated or deobfuscated | 171 | // we don't know if this class name is obfuscated or deobfuscated |
| 168 | // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out | 172 | // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out |
| @@ -178,7 +182,6 @@ public class Deobfuscator { | |||
| 178 | } | 182 | } |
| 179 | 183 | ||
| 180 | // set the desc loader | 184 | // set the desc loader |
| 181 | TranslatingTypeLoader loader = createTypeLoader(); | ||
| 182 | this.settings.setTypeLoader(loader); | 185 | this.settings.setTypeLoader(loader); |
| 183 | 186 | ||
| 184 | // see if procyon can find the desc | 187 | // see if procyon can find the desc |
| @@ -267,6 +270,10 @@ public class Deobfuscator { | |||
| 267 | progress.init(classEntries.size(), "Decompiling classes..."); | 270 | progress.init(classEntries.size(), "Decompiling classes..."); |
| 268 | } | 271 | } |
| 269 | 272 | ||
| 273 | //create a common instance outside the loop as mappings shouldn't be changing while this is happening | ||
| 274 | //synchronized to make sure the parallelStream doesn't CME with the cache | ||
| 275 | ITranslatingTypeLoader typeLoader = new SynchronizedTypeLoader(createTypeLoader()); | ||
| 276 | |||
| 270 | // DEOBFUSCATE ALL THE THINGS!! @_@ | 277 | // DEOBFUSCATE ALL THE THINGS!! @_@ |
| 271 | Stopwatch stopwatch = Stopwatch.createStarted(); | 278 | Stopwatch stopwatch = Stopwatch.createStarted(); |
| 272 | AtomicInteger count = new AtomicInteger(); | 279 | AtomicInteger count = new AtomicInteger(); |
| @@ -278,7 +285,7 @@ public class Deobfuscator { | |||
| 278 | 285 | ||
| 279 | try { | 286 | try { |
| 280 | // get the source | 287 | // get the source |
| 281 | CompilationUnit sourceTree = getSourceTree(obfClassEntry.getName()); | 288 | CompilationUnit sourceTree = getSourceTree(obfClassEntry.getName(), typeLoader); |
| 282 | 289 | ||
| 283 | // write the file | 290 | // write the file |
| 284 | File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); | 291 | File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); |
| @@ -295,7 +302,7 @@ public class Deobfuscator { | |||
| 295 | } | 302 | } |
| 296 | }); | 303 | }); |
| 297 | stopwatch.stop(); | 304 | stopwatch.stop(); |
| 298 | System.out.println("Done in : " + stopwatch.toString()); | 305 | System.out.println("writeSources Done in : " + stopwatch.toString()); |
| 299 | if (progress != null) { | 306 | if (progress != null) { |
| 300 | progress.onProgress(count.get(), "Done:"); | 307 | progress.onProgress(count.get(), "Done:"); |
| 301 | } | 308 | } |
| @@ -658,4 +665,5 @@ public class Deobfuscator { | |||
| 658 | public interface ClassTransformer { | 665 | public interface ClassTransformer { |
| 659 | String transform(ClassNode node, ClassWriter writer); | 666 | String transform(ClassNode node, ClassWriter writer); |
| 660 | } | 667 | } |
| 668 | |||
| 661 | } | 669 | } |
diff --git a/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java new file mode 100644 index 00000000..547ed0b2 --- /dev/null +++ b/src/main/java/cuchaz/enigma/ITranslatingTypeLoader.java | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.ITypeLoader; | ||
| 4 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 5 | import org.objectweb.asm.ClassWriter; | ||
| 6 | import org.objectweb.asm.tree.ClassNode; | ||
| 7 | |||
| 8 | import java.util.List; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * For delegation of TranslatingTypeLoader without needing the subclass the whole thing | ||
| 12 | */ | ||
| 13 | public interface ITranslatingTypeLoader extends ITypeLoader { | ||
| 14 | List<String> getClassNamesToTry(String className); | ||
| 15 | |||
| 16 | List<String> getClassNamesToTry(ClassEntry obfClassEntry); | ||
| 17 | |||
| 18 | String transformInto(ClassNode node, ClassWriter writer); | ||
| 19 | } | ||
diff --git a/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java b/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java new file mode 100644 index 00000000..9aa3c5e4 --- /dev/null +++ b/src/main/java/cuchaz/enigma/SynchronizedTypeLoader.java | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | package cuchaz.enigma; | ||
| 2 | |||
| 3 | import com.strobel.assembler.metadata.Buffer; | ||
| 4 | import cuchaz.enigma.mapping.entry.ClassEntry; | ||
| 5 | import org.objectweb.asm.ClassWriter; | ||
| 6 | import org.objectweb.asm.tree.ClassNode; | ||
| 7 | |||
| 8 | import java.util.List; | ||
| 9 | |||
| 10 | /** | ||
| 11 | * Typeloader with synchronized tryLoadType method | ||
| 12 | */ | ||
| 13 | public class SynchronizedTypeLoader implements ITranslatingTypeLoader { | ||
| 14 | private final TranslatingTypeLoader delegate; | ||
| 15 | |||
| 16 | SynchronizedTypeLoader(TranslatingTypeLoader delegate) { | ||
| 17 | this.delegate = delegate; | ||
| 18 | } | ||
| 19 | |||
| 20 | @Override | ||
| 21 | public List<String> getClassNamesToTry(String className) { | ||
| 22 | return delegate.getClassNamesToTry(className); | ||
| 23 | } | ||
| 24 | |||
| 25 | @Override | ||
| 26 | public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { | ||
| 27 | return delegate.getClassNamesToTry(obfClassEntry); | ||
| 28 | } | ||
| 29 | |||
| 30 | @Override | ||
| 31 | public String transformInto(ClassNode node, ClassWriter writer) { | ||
| 32 | return delegate.transformInto(node, writer); | ||
| 33 | } | ||
| 34 | |||
| 35 | @Override | ||
| 36 | public synchronized boolean tryLoadType(String internalName, Buffer buffer) { | ||
| 37 | return delegate.tryLoadType(internalName, buffer); | ||
| 38 | } | ||
| 39 | } | ||
diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java index 34901d52..eb780ee9 100644 --- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java | |||
| @@ -12,9 +12,7 @@ | |||
| 12 | package cuchaz.enigma; | 12 | package cuchaz.enigma; |
| 13 | 13 | ||
| 14 | import com.google.common.collect.Lists; | 14 | import com.google.common.collect.Lists; |
| 15 | import com.google.common.collect.Maps; | ||
| 16 | import com.strobel.assembler.metadata.Buffer; | 15 | import com.strobel.assembler.metadata.Buffer; |
| 17 | import com.strobel.assembler.metadata.ClasspathTypeLoader; | ||
| 18 | import com.strobel.assembler.metadata.ITypeLoader; | 16 | import com.strobel.assembler.metadata.ITypeLoader; |
| 19 | import cuchaz.enigma.analysis.JarIndex; | 17 | import cuchaz.enigma.analysis.JarIndex; |
| 20 | import cuchaz.enigma.analysis.ParsedJar; | 18 | import cuchaz.enigma.analysis.ParsedJar; |
| @@ -27,17 +25,16 @@ import org.objectweb.asm.Opcodes; | |||
| 27 | import org.objectweb.asm.tree.ClassNode; | 25 | import org.objectweb.asm.tree.ClassNode; |
| 28 | 26 | ||
| 29 | import java.util.List; | 27 | import java.util.List; |
| 30 | import java.util.Map; | ||
| 31 | 28 | ||
| 32 | public class TranslatingTypeLoader implements ITypeLoader { | 29 | public class TranslatingTypeLoader extends CachingTypeLoader implements ITranslatingTypeLoader { |
| 30 | //Store one instance as the classpath shouldnt change during load | ||
| 31 | private static final ITypeLoader defaultTypeLoader = new CachingClasspathTypeLoader(); | ||
| 33 | 32 | ||
| 34 | private final ParsedJar jar; | 33 | private final ParsedJar jar; |
| 35 | private final JarIndex jarIndex; | 34 | private final JarIndex jarIndex; |
| 36 | private final ReferencedEntryPool entryPool; | 35 | private final ReferencedEntryPool entryPool; |
| 37 | private final Translator obfuscatingTranslator; | 36 | private final Translator obfuscatingTranslator; |
| 38 | private final Translator deobfuscatingTranslator; | 37 | private final Translator deobfuscatingTranslator; |
| 39 | private final Map<String, byte[]> cache; | ||
| 40 | private final ClasspathTypeLoader defaultTypeLoader; | ||
| 41 | 38 | ||
| 42 | public TranslatingTypeLoader(ParsedJar jar, JarIndex jarIndex, ReferencedEntryPool entryPool, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { | 39 | public TranslatingTypeLoader(ParsedJar jar, JarIndex jarIndex, ReferencedEntryPool entryPool, Translator obfuscatingTranslator, Translator deobfuscatingTranslator) { |
| 43 | this.jar = jar; | 40 | this.jar = jar; |
| @@ -45,37 +42,19 @@ public class TranslatingTypeLoader implements ITypeLoader { | |||
| 45 | this.entryPool = entryPool; | 42 | this.entryPool = entryPool; |
| 46 | this.obfuscatingTranslator = obfuscatingTranslator; | 43 | this.obfuscatingTranslator = obfuscatingTranslator; |
| 47 | this.deobfuscatingTranslator = deobfuscatingTranslator; | 44 | this.deobfuscatingTranslator = deobfuscatingTranslator; |
| 48 | this.cache = Maps.newHashMap(); | ||
| 49 | this.defaultTypeLoader = new ClasspathTypeLoader(); | ||
| 50 | |||
| 51 | } | ||
| 52 | |||
| 53 | public void clearCache() { | ||
| 54 | this.cache.clear(); | ||
| 55 | } | 45 | } |
| 56 | 46 | ||
| 57 | @Override | 47 | protected byte[] doLoad(String className){ |
| 58 | public boolean tryLoadType(String className, Buffer out) { | 48 | byte[] data = loadType(className); |
| 59 | |||
| 60 | // check the cache | ||
| 61 | byte[] data; | ||
| 62 | if (this.cache.containsKey(className)) { | ||
| 63 | data = this.cache.get(className); | ||
| 64 | } else { | ||
| 65 | data = loadType(className); | ||
| 66 | this.cache.put(className, data); | ||
| 67 | } | ||
| 68 | |||
| 69 | if (data == null) { | 49 | if (data == null) { |
| 70 | // chain to default desc loader | 50 | // chain to default desc loader |
| 71 | return this.defaultTypeLoader.tryLoadType(className, out); | 51 | Buffer parentBuf = new Buffer(); |
| 52 | if (defaultTypeLoader.tryLoadType(className, parentBuf)){ | ||
| 53 | return parentBuf.array(); | ||
| 54 | } | ||
| 55 | return EMPTY_ARRAY;//need to return *something* as null means no store | ||
| 72 | } | 56 | } |
| 73 | 57 | return data; | |
| 74 | // send the class to the decompiler | ||
| 75 | out.reset(data.length); | ||
| 76 | System.arraycopy(data, 0, out.array(), out.position(), data.length); | ||
| 77 | out.position(0); | ||
| 78 | return true; | ||
| 79 | } | 58 | } |
| 80 | 59 | ||
| 81 | private byte[] loadType(String className) { | 60 | private byte[] loadType(String className) { |
| @@ -131,10 +110,12 @@ public class TranslatingTypeLoader implements ITypeLoader { | |||
| 131 | return null; | 110 | return null; |
| 132 | } | 111 | } |
| 133 | 112 | ||
| 113 | @Override | ||
| 134 | public List<String> getClassNamesToTry(String className) { | 114 | public List<String> getClassNamesToTry(String className) { |
| 135 | return getClassNamesToTry(this.obfuscatingTranslator.getTranslatedClass(new ClassEntry(className))); | 115 | return getClassNamesToTry(this.obfuscatingTranslator.getTranslatedClass(new ClassEntry(className))); |
| 136 | } | 116 | } |
| 137 | 117 | ||
| 118 | @Override | ||
| 138 | public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { | 119 | public List<String> getClassNamesToTry(ClassEntry obfClassEntry) { |
| 139 | List<String> classNamesToTry = Lists.newArrayList(); | 120 | List<String> classNamesToTry = Lists.newArrayList(); |
| 140 | classNamesToTry.add(obfClassEntry.getName()); | 121 | classNamesToTry.add(obfClassEntry.getName()); |
| @@ -145,8 +126,10 @@ public class TranslatingTypeLoader implements ITypeLoader { | |||
| 145 | return classNamesToTry; | 126 | return classNamesToTry; |
| 146 | } | 127 | } |
| 147 | 128 | ||
| 129 | @Override | ||
| 148 | public String transformInto(ClassNode node, ClassWriter writer) { | 130 | public String transformInto(ClassNode node, ClassWriter writer) { |
| 149 | node.accept(new TranslationClassVisitor(deobfuscatingTranslator, jarIndex, entryPool, Opcodes.ASM5, writer)); | 131 | node.accept(new TranslationClassVisitor(deobfuscatingTranslator, jarIndex, entryPool, Opcodes.ASM5, writer)); |
| 150 | return deobfuscatingTranslator.getTranslatedClass(new ClassEntry(node.name)).getName(); | 132 | return deobfuscatingTranslator.getTranslatedClass(new ClassEntry(node.name)).getName(); |
| 151 | } | 133 | } |
| 134 | |||
| 152 | } | 135 | } |
diff --git a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java index 928c6d19..e66b085f 100644 --- a/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java +++ b/src/main/java/cuchaz/enigma/bytecode/translators/TranslationSignatureVisitor.java | |||
| @@ -3,12 +3,14 @@ package cuchaz.enigma.bytecode.translators; | |||
| 3 | import org.objectweb.asm.Opcodes; | 3 | import org.objectweb.asm.Opcodes; |
| 4 | import org.objectweb.asm.signature.SignatureVisitor; | 4 | import org.objectweb.asm.signature.SignatureVisitor; |
| 5 | 5 | ||
| 6 | import java.util.Stack; | ||
| 6 | import java.util.function.Function; | 7 | import java.util.function.Function; |
| 7 | 8 | ||
| 8 | public class TranslationSignatureVisitor extends SignatureVisitor { | 9 | public class TranslationSignatureVisitor extends SignatureVisitor { |
| 9 | private final Function<String, String> remapper; | 10 | private final Function<String, String> remapper; |
| 10 | 11 | ||
| 11 | private final SignatureVisitor sv; | 12 | private final SignatureVisitor sv; |
| 13 | private final Stack<String> classStack = new Stack<>(); | ||
| 12 | 14 | ||
| 13 | public TranslationSignatureVisitor(Function<String, String> remapper, SignatureVisitor sv) { | 15 | public TranslationSignatureVisitor(Function<String, String> remapper, SignatureVisitor sv) { |
| 14 | super(Opcodes.ASM5); | 16 | super(Opcodes.ASM5); |
| @@ -18,13 +20,24 @@ public class TranslationSignatureVisitor extends SignatureVisitor { | |||
| 18 | 20 | ||
| 19 | @Override | 21 | @Override |
| 20 | public void visitClassType(String name) { | 22 | public void visitClassType(String name) { |
| 23 | classStack.push(name); | ||
| 21 | String translatedEntry = this.remapper.apply(name); | 24 | String translatedEntry = this.remapper.apply(name); |
| 22 | this.sv.visitClassType(translatedEntry); | 25 | this.sv.visitClassType(translatedEntry); |
| 23 | } | 26 | } |
| 24 | 27 | ||
| 25 | @Override | 28 | @Override |
| 26 | public void visitInnerClassType(String name) { | 29 | public void visitInnerClassType(String name) { |
| 30 | String lastClass = classStack.pop(); | ||
| 31 | if (!name.startsWith(lastClass+"$")){//todo see if there's a way to base this on whether there were type params or not | ||
| 32 | name = lastClass+"$"+name; | ||
| 33 | } | ||
| 27 | String translatedEntry = this.remapper.apply(name); | 34 | String translatedEntry = this.remapper.apply(name); |
| 35 | if (translatedEntry.contains("/")){ | ||
| 36 | translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("/")+1); | ||
| 37 | } | ||
| 38 | if (translatedEntry.contains("$")){ | ||
| 39 | translatedEntry = translatedEntry.substring(translatedEntry.lastIndexOf("$")+1); | ||
| 40 | } | ||
| 28 | this.sv.visitInnerClassType(translatedEntry); | 41 | this.sv.visitInnerClassType(translatedEntry); |
| 29 | } | 42 | } |
| 30 | 43 | ||
| @@ -105,6 +118,8 @@ public class TranslationSignatureVisitor extends SignatureVisitor { | |||
| 105 | @Override | 118 | @Override |
| 106 | public void visitEnd() { | 119 | public void visitEnd() { |
| 107 | this.sv.visitEnd(); | 120 | this.sv.visitEnd(); |
| 121 | if (!classStack.empty()) | ||
| 122 | classStack.pop(); | ||
| 108 | } | 123 | } |
| 109 | 124 | ||
| 110 | @Override | 125 | @Override |