From 268e8bd3a292162c215723638665e32415207c28 Mon Sep 17 00:00:00 2001 From: Thiakil Date: Thu, 12 Jul 2018 07:35:04 +0800 Subject: remove raw casts to Object --- src/main/java/cuchaz/enigma/Deobfuscator.java | 4 ++- .../oml/ast/transformers/RemoveObjectCasts.java | 39 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/main/java/oml/ast/transformers/RemoveObjectCasts.java diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java index 6ea1c40b..5b210110 100644 --- a/src/main/java/cuchaz/enigma/Deobfuscator.java +++ b/src/main/java/cuchaz/enigma/Deobfuscator.java @@ -34,6 +34,7 @@ import cuchaz.enigma.mapping.entry.*; import cuchaz.enigma.throwables.IllegalNameException; import cuchaz.enigma.utils.Utils; import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform; +import oml.ast.transformers.RemoveObjectCasts; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; @@ -661,7 +662,8 @@ public class Deobfuscator { public static void runCustomTransforms(AstBuilder builder, DecompilerContext context){ List transformers = Arrays.asList( - new ObfuscatedEnumSwitchRewriterTransform(context) + new ObfuscatedEnumSwitchRewriterTransform(context), + new RemoveObjectCasts(context) ); for (IAstTransform transform : transformers){ transform.run(builder.getCompilationUnit()); diff --git a/src/main/java/oml/ast/transformers/RemoveObjectCasts.java b/src/main/java/oml/ast/transformers/RemoveObjectCasts.java new file mode 100644 index 00000000..d7c3c4a6 --- /dev/null +++ b/src/main/java/oml/ast/transformers/RemoveObjectCasts.java @@ -0,0 +1,39 @@ +package oml.ast.transformers; + +import com.strobel.assembler.metadata.BuiltinTypes; +import com.strobel.decompiler.DecompilerContext; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor; +import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; + +/** + * Created by Thiakil on 11/07/2018. + */ +public class RemoveObjectCasts implements IAstTransform { + private final DecompilerContext _context; + + public RemoveObjectCasts(DecompilerContext context) { + _context = context; + } + + @Override + public void run(AstNode compilationUnit) { + compilationUnit.acceptVisitor(new Visitor(_context), null); + } + + private final static class Visitor extends ContextTrackingVisitor{ + + protected Visitor(DecompilerContext context) { + super(context); + } + + @Override + public Void visitCastExpression(CastExpression node, Void data) { + if (node.getType().toTypeReference().equals(BuiltinTypes.Object)){ + node.replaceWith(node.getExpression()); + } + return super.visitCastExpression(node, data); + } + } +} -- cgit v1.2.3 From 4a8ee4303ca1ab82da9499181122bfd7e3214a05 Mon Sep 17 00:00:00 2001 From: Thiakil Date: Fri, 20 Jul 2018 12:14:38 +0800 Subject: recursively check ClassMapping dirty state --- src/main/java/cuchaz/enigma/mapping/ClassMapping.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java index 8f3f2b2b..369ba8c6 100644 --- a/src/main/java/cuchaz/enigma/mapping/ClassMapping.java +++ b/src/main/java/cuchaz/enigma/mapping/ClassMapping.java @@ -526,7 +526,16 @@ public class ClassMapping implements Comparable { } public boolean isDirty() { - return isDirty; + return isDirty || areInnersDirty(); + } + + private boolean areInnersDirty(){ + for (ClassMapping c : this.innerClasses()){ + if (c.isDirty()){ + return true; + } + } + return false; } public void resetDirty() { -- cgit v1.2.3 From c8e894dfa1485214ed3d4636e3a54689d722d439 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sun, 22 Jul 2018 23:29:42 +0100 Subject: Fixed setBridged resetting the viability --- src/main/java/cuchaz/enigma/bytecode/AccessFlags.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java index 21b24897..0bfc59bd 100644 --- a/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java +++ b/src/main/java/cuchaz/enigma/bytecode/AccessFlags.java @@ -51,7 +51,7 @@ public class AccessFlags { } public AccessFlags setBridged() { - this.setVisibility(Opcodes.ACC_BRIDGE); + flags |= Opcodes.ACC_BRIDGE; return this; } -- cgit v1.2.3 From 714c0297c2e4e389ac52c6378b590b77c7058594 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sun, 22 Jul 2018 23:38:02 +0100 Subject: Make rebuildMethodNames use parallelStream's should improve the speed a lot let me know if it breaks anything --- src/main/java/cuchaz/enigma/Deobfuscator.java | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java index 5b210110..599387e5 100644 --- a/src/main/java/cuchaz/enigma/Deobfuscator.java +++ b/src/main/java/cuchaz/enigma/Deobfuscator.java @@ -340,28 +340,28 @@ public class Deobfuscator { } public void rebuildMethodNames(ProgressListener progress) { - int i = 0; + final AtomicInteger i = new AtomicInteger(); Map> renameClassMap = new HashMap<>(); progress.init(getMappings().classes().size() * 3, "Rebuilding method names"); - for (ClassMapping classMapping : Lists.newArrayList(getMappings().classes())) { - progress.onProgress(i++, classMapping.getDeobfName()); + Lists.newArrayList(getMappings().classes()).parallelStream().forEach(classMapping -> { + progress.onProgress(i.getAndIncrement(), classMapping.getDeobfName()); rebuildMethodNames(classMapping, renameClassMap); - } + }); - for (Map.Entry> renameClassMapEntry : renameClassMap.entrySet()) { - progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); + renameClassMap.entrySet().parallelStream().forEach(renameClassMapEntry -> { + progress.onProgress(i.getAndIncrement(), renameClassMapEntry.getKey().getDeobfName()); for (Map.Entry entry : renameClassMapEntry.getValue().entrySet()) { Entry obfEntry = entry.getKey(); removeMapping(obfEntry); } - } + }); - for (Map.Entry> renameClassMapEntry : renameClassMap.entrySet()) { - progress.onProgress(i++, renameClassMapEntry.getKey().getDeobfName()); + renameClassMap.entrySet().parallelStream().forEach(renameClassMapEntry -> { + progress.onProgress(i.getAndIncrement(), renameClassMapEntry.getKey().getDeobfName()); for (Map.Entry entry : renameClassMapEntry.getValue().entrySet()) { Entry obfEntry = entry.getKey(); @@ -373,7 +373,7 @@ public class Deobfuscator { System.out.println("WARNING: " + exception.getMessage()); } } - } + }); } private void rebuildMethodNames(ClassMapping classMapping, Map> renameClassMap) { -- cgit v1.2.3 From 49e696255e9b623535679f228384ea45d4f89cfd Mon Sep 17 00:00:00 2001 From: Thiakil Date: Mon, 23 Jul 2018 12:41:19 +0800 Subject: fix source index tokens for inner classes that are not mapped --- src/main/java/cuchaz/enigma/analysis/SourceIndex.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java index 14b2e768..78195cb7 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndex.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndex.java @@ -17,15 +17,19 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.strobel.decompiler.languages.Region; import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.ConstructorDeclaration; import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.TypeDeclaration; import cuchaz.enigma.mapping.entry.Entry; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.regex.Pattern; public class SourceIndex { + private static Pattern ANONYMOUS_INNER = Pattern.compile("\\$\\d+$"); private String source; private TreeMap> tokenToReference; @@ -81,6 +85,14 @@ public class SourceIndex { return null; } + if (node instanceof Identifier && name.indexOf('$') >=0 && node.getParent() instanceof ConstructorDeclaration && name.lastIndexOf('$') >= 0 && !ANONYMOUS_INNER.matcher(name).matches()){ + TypeDeclaration type = node.getParent().getParent() instanceof TypeDeclaration ? (TypeDeclaration) node.getParent().getParent() : null; + if (type != null){ + name = type.getName(); + token.end = token.start + name.length(); + } + } + // DEBUG // System.out.println( String.format( "%s \"%s\" region: %s", node.getNodeType(), name, region ) ); -- cgit v1.2.3 From 8c5d13ed87f92e982a3de489543f77da8a06d7ff Mon Sep 17 00:00:00 2001 From: Thiakil Date: Mon, 23 Jul 2018 13:07:16 +0800 Subject: remove injected DUP + getClass() + POP instructions --- .../java/cuchaz/enigma/TranslatingTypeLoader.java | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java index eb780ee9..8300f21c 100644 --- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java @@ -22,7 +22,10 @@ import cuchaz.enigma.mapping.entry.ReferencedEntryPool; import cuchaz.enigma.mapping.Translator; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; import java.util.List; @@ -89,6 +92,31 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla return null; } + + // remove .getClass() calls that are seemingly injected + // DUP + // INVOKEVIRTUAL java/lang/Object.getClass ()Ljava/lang/Class; + // POP + for (MethodNode methodNode : node.methods){ + AbstractInsnNode insnNode = methodNode.instructions.getFirst(); + while (insnNode != null){ + if (insnNode instanceof MethodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL){ + MethodInsnNode methodInsnNode = (MethodInsnNode)insnNode; + if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class")){ + AbstractInsnNode previous = methodInsnNode.getPrevious(); + AbstractInsnNode next = methodInsnNode.getNext(); + if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP){ + insnNode = previous.getPrevious();//reset the iterator so it gets the new next instruction + methodNode.instructions.remove(previous); + methodNode.instructions.remove(methodInsnNode); + methodNode.instructions.remove(next); + } + } + } + insnNode = insnNode.getNext(); + } + } + ClassWriter writer = new ClassWriter(0); transformInto(node, writer); -- cgit v1.2.3 From e98dae3df050f180b1dfef8576d32df4c3c64663 Mon Sep 17 00:00:00 2001 From: Thiakil Date: Mon, 23 Jul 2018 13:20:47 +0800 Subject: add classloader for adding deps --- src/main/java/oml/ExtraClasspathTypeLoader.java | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/main/java/oml/ExtraClasspathTypeLoader.java diff --git a/src/main/java/oml/ExtraClasspathTypeLoader.java b/src/main/java/oml/ExtraClasspathTypeLoader.java new file mode 100644 index 00000000..f8ec2e0c --- /dev/null +++ b/src/main/java/oml/ExtraClasspathTypeLoader.java @@ -0,0 +1,59 @@ +package oml; + +import com.strobel.assembler.metadata.Buffer; +import com.strobel.assembler.metadata.ITypeLoader; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; + +/** + * Copy of ClasspathTypeLoader supporting a classpath constructor. + */ +public class ExtraClasspathTypeLoader implements ITypeLoader { + private final ClassLoader _loader; + + public ExtraClasspathTypeLoader(String extraClasspath){ + _loader = new URLClassLoader(Arrays.stream(extraClasspath.split(File.pathSeparator)).map(path-> { + try { + return new File(path).toURI().toURL(); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + }).toArray(URL[]::new)); + } + + @Override + public boolean tryLoadType(final String internalName, final Buffer buffer) { + + final String path = internalName.concat(".class"); + final URL resource = _loader.getResource(path); + + if (resource == null) { + return false; + } + + try (final InputStream stream = _loader.getResourceAsStream(path)) { + final byte[] temp = new byte[4096]; + + int bytesRead; + + while ((bytesRead = stream.read(temp, 0, temp.length)) > 0) { + //buffer.ensureWriteableBytes(bytesRead); + buffer.putByteArray(temp, 0, bytesRead); + } + + buffer.flip(); + + + return true; + } + catch (final IOException ignored) { + return false; + } + } +} -- cgit v1.2.3 From 7f914aa5d50f151ceff74f0764c9eafb2bebfd9a Mon Sep 17 00:00:00 2001 From: Thiakil Date: Mon, 23 Jul 2018 13:22:33 +0800 Subject: add support for the extra loader --- src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java index 58682e23..fe13321c 100644 --- a/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java +++ b/src/main/java/cuchaz/enigma/CachingClasspathTypeLoader.java @@ -8,6 +8,12 @@ import com.strobel.assembler.metadata.ITypeLoader; * Caching version of {@link ClasspathTypeLoader} */ public class CachingClasspathTypeLoader extends CachingTypeLoader { + private static ITypeLoader extraClassPathLoader = null; + + public static void setExtraClassPathLoader(ITypeLoader loader){ + extraClassPathLoader = loader; + } + private final ITypeLoader classpathLoader = new ClasspathTypeLoader(); protected byte[] doLoad(String className) { @@ -15,6 +21,12 @@ public class CachingClasspathTypeLoader extends CachingTypeLoader { if (classpathLoader.tryLoadType(className, parentBuf)) { return parentBuf.array(); } + if (extraClassPathLoader != null){ + parentBuf.reset(); + if (extraClassPathLoader.tryLoadType(className, parentBuf)){ + return parentBuf.array(); + } + } return EMPTY_ARRAY;//need to return *something* as null means no store } } -- cgit v1.2.3 From 9085c79abc2eb96f232757a803ab1b5f8359eda8 Mon Sep 17 00:00:00 2001 From: Thiakil Date: Mon, 23 Jul 2018 15:14:19 +0800 Subject: missed a semicolon --- src/main/java/cuchaz/enigma/TranslatingTypeLoader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java index 8300f21c..42ceec4d 100644 --- a/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java +++ b/src/main/java/cuchaz/enigma/TranslatingTypeLoader.java @@ -102,7 +102,7 @@ public class TranslatingTypeLoader extends CachingTypeLoader implements ITransla while (insnNode != null){ if (insnNode instanceof MethodInsnNode && insnNode.getOpcode() == Opcodes.INVOKEVIRTUAL){ MethodInsnNode methodInsnNode = (MethodInsnNode)insnNode; - if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class")){ + if (methodInsnNode.name.equals("getClass") && methodInsnNode.owner.equals("java/lang/Object") && methodInsnNode.desc.equals("()Ljava/lang/Class;")){ AbstractInsnNode previous = methodInsnNode.getPrevious(); AbstractInsnNode next = methodInsnNode.getNext(); if (previous.getOpcode() == Opcodes.DUP && next.getOpcode() == Opcodes.POP){ -- cgit v1.2.3 From 8537f73abcf45e4380cf86d29404f61f0c9d88fe Mon Sep 17 00:00:00 2001 From: Thiakil Date: Tue, 24 Jul 2018 22:36:08 +0800 Subject: more custom source transformers --- src/main/java/cuchaz/enigma/Deobfuscator.java | 8 +- .../oml/ast/transformers/InvalidIdentifierFix.java | 29 +++ .../java/oml/ast/transformers/Java8Generics.java | 107 +++++++++++ .../java/oml/ast/transformers/VaragsFixer.java | 197 +++++++++++++++++++++ 4 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 src/main/java/oml/ast/transformers/InvalidIdentifierFix.java create mode 100644 src/main/java/oml/ast/transformers/Java8Generics.java create mode 100644 src/main/java/oml/ast/transformers/VaragsFixer.java diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java index 599387e5..2642424e 100644 --- a/src/main/java/cuchaz/enigma/Deobfuscator.java +++ b/src/main/java/cuchaz/enigma/Deobfuscator.java @@ -33,8 +33,11 @@ import cuchaz.enigma.mapping.*; import cuchaz.enigma.mapping.entry.*; import cuchaz.enigma.throwables.IllegalNameException; import cuchaz.enigma.utils.Utils; +import oml.ast.transformers.InvalidIdentifierFix; +import oml.ast.transformers.Java8Generics; import oml.ast.transformers.ObfuscatedEnumSwitchRewriterTransform; import oml.ast.transformers.RemoveObjectCasts; +import oml.ast.transformers.VaragsFixer; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.ClassNode; @@ -663,7 +666,10 @@ public class Deobfuscator { public static void runCustomTransforms(AstBuilder builder, DecompilerContext context){ List transformers = Arrays.asList( new ObfuscatedEnumSwitchRewriterTransform(context), - new RemoveObjectCasts(context) + new VaragsFixer(context), + new RemoveObjectCasts(context), + new Java8Generics(), + new InvalidIdentifierFix() ); for (IAstTransform transform : transformers){ transform.run(builder.getCompilationUnit()); diff --git a/src/main/java/oml/ast/transformers/InvalidIdentifierFix.java b/src/main/java/oml/ast/transformers/InvalidIdentifierFix.java new file mode 100644 index 00000000..3e052ded --- /dev/null +++ b/src/main/java/oml/ast/transformers/InvalidIdentifierFix.java @@ -0,0 +1,29 @@ +package oml.ast.transformers; + +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; + +/** + * Created by Thiakil on 13/07/2018. + */ +public class InvalidIdentifierFix implements IAstTransform { + @Override + public void run(AstNode compilationUnit) { + compilationUnit.acceptVisitor(new Visitor(), null); + } + + class Visitor extends DepthFirstAstVisitor{ + @Override + public Void visitIdentifier(Identifier node, Void data) { + super.visitIdentifier(node, data); + if (node.getName().equals("do") || node.getName().equals("if")){ + Identifier newIdentifier = Identifier.create(node.getName() + "_", node.getStartLocation()); + newIdentifier.copyUserDataFrom(node); + node.replaceWith(newIdentifier); + } + return null; + } + } +} diff --git a/src/main/java/oml/ast/transformers/Java8Generics.java b/src/main/java/oml/ast/transformers/Java8Generics.java new file mode 100644 index 00000000..0f8a84c1 --- /dev/null +++ b/src/main/java/oml/ast/transformers/Java8Generics.java @@ -0,0 +1,107 @@ +package oml.ast.transformers; + +import com.strobel.assembler.metadata.BuiltinTypes; +import com.strobel.assembler.metadata.CommonTypeReferences; +import com.strobel.assembler.metadata.Flags; +import com.strobel.assembler.metadata.IGenericInstance; +import com.strobel.assembler.metadata.IMemberDefinition; +import com.strobel.assembler.metadata.JvmType; +import com.strobel.assembler.metadata.MemberReference; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.TypeDefinition; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.AstNodeCollection; +import com.strobel.decompiler.languages.java.ast.AstType; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.ComposedType; +import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; +import com.strobel.decompiler.languages.java.ast.Expression; +import com.strobel.decompiler.languages.java.ast.Identifier; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.Roles; +import com.strobel.decompiler.languages.java.ast.SimpleType; +import com.strobel.decompiler.languages.java.ast.WildcardType; +import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; + +/** + * Created by Thiakil on 12/07/2018. + */ +public class Java8Generics implements IAstTransform { + + @Override + public void run(AstNode compilationUnit) { + compilationUnit.acceptVisitor(new Visitor(), null); + } + + static class Visitor extends DepthFirstAstVisitor{ + + @Override + public Void visitInvocationExpression(InvocationExpression node, Void data) { + super.visitInvocationExpression(node, data); + if (node.getTarget() instanceof MemberReferenceExpression){ + MemberReferenceExpression referenceExpression = (MemberReferenceExpression) node.getTarget(); + if (referenceExpression.getTypeArguments().stream().map(t->{ + TypeReference tr = t.toTypeReference(); + if (tr.getDeclaringType() != null){//ensure that inner types are resolved so we can get the TypeDefinition below + TypeReference resolved = tr.resolve(); + if (resolved != null) + return resolved; + } + return tr; + }).anyMatch(t -> t.isWildcardType() || (t instanceof TypeDefinition && ((TypeDefinition) t).isAnonymous()))) { + //these are invalid for invocations, let the compiler work it out + referenceExpression.getTypeArguments().clear(); + } else if (referenceExpression.getTypeArguments().stream().allMatch(t->t.toTypeReference().equals(CommonTypeReferences.Object))){ + //all are , thereby redundant and/or bad + referenceExpression.getTypeArguments().clear(); + } + } + return null; + } + + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) { + super.visitObjectCreationExpression(node, data); + AstType type = node.getType(); + if (type instanceof SimpleType && !((SimpleType) type).getTypeArguments().isEmpty()){ + SimpleType simpleType = (SimpleType) type; + AstNodeCollection typeArguments = simpleType.getTypeArguments(); + if (typeArguments.size() == 1 && typeArguments.firstOrNullObject().toTypeReference().equals(CommonTypeReferences.Object)){ + //all are , thereby redundant and/or bad + typeArguments.firstOrNullObject().getChildByRole(Roles.IDENTIFIER).replaceWith(Identifier.create("")); + } + } + return null; + } + + @Override + public Void visitCastExpression(CastExpression node, Void data) { + boolean doReplace = false; + TypeReference typeReference = node.getType().toTypeReference(); + if (typeReference.isArray() && typeReference.getElementType().isGenericType()){ + doReplace = true; + } else if (typeReference.isGenericType()) { + Expression target = node.getExpression(); + if (typeReference instanceof IGenericInstance && ((IGenericInstance)typeReference).getTypeArguments().stream().anyMatch(t->t.isWildcardType())){ + doReplace = true; + } else if (target instanceof InvocationExpression) { + InvocationExpression invocationExpression = (InvocationExpression)target; + if (invocationExpression.getTarget() instanceof MemberReferenceExpression && !((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().isEmpty()) { + ((MemberReferenceExpression) invocationExpression.getTarget()).getTypeArguments().clear(); + doReplace = true; + } + } + } + super.visitCastExpression(node, data); + if (doReplace){ + node.replaceWith(node.getExpression()); + } + return null; + } + } +} diff --git a/src/main/java/oml/ast/transformers/VaragsFixer.java b/src/main/java/oml/ast/transformers/VaragsFixer.java new file mode 100644 index 00000000..cd711ae0 --- /dev/null +++ b/src/main/java/oml/ast/transformers/VaragsFixer.java @@ -0,0 +1,197 @@ +package oml.ast.transformers; + +import com.strobel.assembler.metadata.MemberReference; +import com.strobel.assembler.metadata.MetadataFilters; +import com.strobel.assembler.metadata.MetadataHelper; +import com.strobel.assembler.metadata.MethodBinder; +import com.strobel.assembler.metadata.MethodDefinition; +import com.strobel.assembler.metadata.MethodReference; +import com.strobel.assembler.metadata.TypeReference; +import com.strobel.core.StringUtilities; +import com.strobel.core.VerifyArgument; +import com.strobel.decompiler.DecompilerContext; +import com.strobel.decompiler.languages.java.ast.ArrayCreationExpression; +import com.strobel.decompiler.languages.java.ast.ArrayInitializerExpression; +import com.strobel.decompiler.languages.java.ast.AstNode; +import com.strobel.decompiler.languages.java.ast.AstNodeCollection; +import com.strobel.decompiler.languages.java.ast.CastExpression; +import com.strobel.decompiler.languages.java.ast.ContextTrackingVisitor; +import com.strobel.decompiler.languages.java.ast.DepthFirstAstVisitor; +import com.strobel.decompiler.languages.java.ast.Expression; +import com.strobel.decompiler.languages.java.ast.InvocationExpression; +import com.strobel.decompiler.languages.java.ast.JavaResolver; +import com.strobel.decompiler.languages.java.ast.Keys; +import com.strobel.decompiler.languages.java.ast.MemberReferenceExpression; +import com.strobel.decompiler.languages.java.ast.ObjectCreationExpression; +import com.strobel.decompiler.languages.java.ast.transforms.IAstTransform; +import com.strobel.decompiler.semantics.ResolveResult; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Thiakil on 12/07/2018. + */ +public class VaragsFixer implements IAstTransform { + private final DecompilerContext _context; + + public VaragsFixer(final DecompilerContext context) { + _context = VerifyArgument.notNull(context, "context"); + } + + @Override + public void run(AstNode compilationUnit) { + compilationUnit.acceptVisitor(new Visitor(_context), null); + } + + class Visitor extends ContextTrackingVisitor { + private final JavaResolver _resolver; + protected Visitor(DecompilerContext context) { + super(context); + _resolver = new JavaResolver(context); + } + + //remove `new Object[0]` on varagrs as the normal tranformer doesnt do them + @Override + public Void visitInvocationExpression(InvocationExpression node, Void data) { + super.visitInvocationExpression(node, data); + MemberReference definition = node.getUserData(Keys.MEMBER_REFERENCE); + if (definition instanceof MethodDefinition && ((MethodDefinition) definition).isVarArgs()){ + AstNodeCollection arguments = node.getArguments(); + Expression lastParam = arguments.lastOrNullObject(); + if (!lastParam.isNull() && lastParam instanceof ArrayCreationExpression){ + ArrayCreationExpression varargArray = (ArrayCreationExpression)lastParam; + if (varargArray.getInitializer().isNull() || varargArray.getInitializer().getElements().isEmpty()){ + lastParam.remove(); + } else { + for (Expression e : varargArray.getInitializer().getElements()){ + arguments.insertBefore(varargArray, e.clone()); + } + varargArray.remove(); + } + } + } + return null; + } + + //applies the vararg transform to object creation + @Override + public Void visitObjectCreationExpression(ObjectCreationExpression node, Void data) { + super.visitObjectCreationExpression(node, data); + final AstNodeCollection arguments = node.getArguments(); + final Expression lastArgument = arguments.lastOrNullObject(); + + Expression arrayArg = lastArgument; + + if (arrayArg instanceof CastExpression) + arrayArg = ((CastExpression) arrayArg).getExpression(); + + if (arrayArg == null || + arrayArg.isNull() || + !(arrayArg instanceof ArrayCreationExpression && + node.getTarget() instanceof MemberReferenceExpression)) { + + return null; + } + + final ArrayCreationExpression newArray = (ArrayCreationExpression) arrayArg; + final MemberReferenceExpression target = (MemberReferenceExpression) node.getTarget(); + + if (!newArray.getAdditionalArraySpecifiers().hasSingleElement()) { + return null; + } + + final MethodReference method = (MethodReference) node.getUserData(Keys.MEMBER_REFERENCE); + + if (method == null) { + return null; + } + + final MethodDefinition resolved = method.resolve(); + + if (resolved == null || !resolved.isVarArgs()) { + return null; + } + + final List candidates; + final Expression invocationTarget = target.getTarget(); + + if (invocationTarget == null || invocationTarget.isNull()) { + candidates = MetadataHelper.findMethods( + context.getCurrentType(), + MetadataFilters.matchName(resolved.getName()) + ); + } + else { + final ResolveResult targetResult = _resolver.apply(invocationTarget); + + if (targetResult == null || targetResult.getType() == null) { + return null; + } + + candidates = MetadataHelper.findMethods( + targetResult.getType(), + MetadataFilters.matchName(resolved.getName()) + ); + } + + final List argTypes = new ArrayList<>(); + + for (final Expression argument : arguments) { + final ResolveResult argResult = _resolver.apply(argument); + + if (argResult == null || argResult.getType() == null) { + return null; + } + + argTypes.add(argResult.getType()); + } + + final MethodBinder.BindResult c1 = MethodBinder.selectMethod(candidates, argTypes); + + if (c1.isFailure() || c1.isAmbiguous()) { + return null; + } + + argTypes.remove(argTypes.size() - 1); + + final ArrayInitializerExpression initializer = newArray.getInitializer(); + final boolean hasElements = !initializer.isNull() && !initializer.getElements().isEmpty(); + + if (hasElements) { + for (final Expression argument : initializer.getElements()) { + final ResolveResult argResult = _resolver.apply(argument); + + if (argResult == null || argResult.getType() == null) { + return null; + } + + argTypes.add(argResult.getType()); + } + } + + final MethodBinder.BindResult c2 = MethodBinder.selectMethod(candidates, argTypes); + + if (c2.isFailure() || + c2.isAmbiguous() || + !StringUtilities.equals(c2.getMethod().getErasedSignature(), c1.getMethod().getErasedSignature())) { + + return null; + } + + lastArgument.remove(); + + if (!hasElements) { + lastArgument.remove(); + return null; + } + + for (final Expression newArg : initializer.getElements()) { + newArg.remove(); + arguments.add(newArg); + } + + return null; + } + } +} -- cgit v1.2.3 From 34df974decb5f934ce87ad801f5dadea8c905f43 Mon Sep 17 00:00:00 2001 From: Thiakil Date: Tue, 24 Jul 2018 22:36:35 +0800 Subject: match the keybind to what the UI says --- src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java index fd30e389..ffceae12 100644 --- a/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java +++ b/src/main/java/cuchaz/enigma/gui/panels/PanelEditor.java @@ -56,7 +56,7 @@ public class PanelEditor extends JEditorPane { gui.popupMenu.showCallsMenu.doClick(); break; - case KeyEvent.VK_T: + case KeyEvent.VK_O: gui.popupMenu.toggleMappingMenu.doClick(); break; case KeyEvent.VK_F5: -- cgit v1.2.3 From 0993c18bc44b35fad25af05f50c7efde3e85fec4 Mon Sep 17 00:00:00 2001 From: Thiakil Date: Tue, 24 Jul 2018 22:40:03 +0800 Subject: use a common metadata (no-retry) instance to decompile all the files, similar to the procyon standalone decompiler --- src/main/java/cuchaz/enigma/Deobfuscator.java | 39 +++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/main/java/cuchaz/enigma/Deobfuscator.java b/src/main/java/cuchaz/enigma/Deobfuscator.java index 2642424e..f42b59f2 100644 --- a/src/main/java/cuchaz/enigma/Deobfuscator.java +++ b/src/main/java/cuchaz/enigma/Deobfuscator.java @@ -15,6 +15,7 @@ import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import com.strobel.assembler.metadata.ITypeLoader; import com.strobel.assembler.metadata.MetadataSystem; import com.strobel.assembler.metadata.TypeDefinition; import com.strobel.assembler.metadata.TypeReference; @@ -44,6 +45,7 @@ import org.objectweb.asm.tree.ClassNode; import java.io.*; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -173,6 +175,10 @@ public class Deobfuscator { } public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader) { + return getSourceTree(className, loader, new NoRetryMetadataSystem(loader)); + } + + public CompilationUnit getSourceTree(String className, ITranslatingTypeLoader loader, MetadataSystem metadataSystem) { // we don't know if this class name is obfuscated or deobfuscated // we need to tell the decompiler the deobfuscated name so it doesn't get freaked out @@ -191,7 +197,7 @@ public class Deobfuscator { this.settings.setTypeLoader(loader); // see if procyon can find the desc - TypeReference type = new MetadataSystem(loader).lookupType(deobfClassName); + TypeReference type = metadataSystem.lookupType(deobfClassName); if (type == null) { throw new Error(String.format("Unable to find desc: %s (deobf: %s)\nTried class names: %s", className, deobfClassName, loader.getClassNamesToTry(deobfClassName) @@ -281,6 +287,9 @@ public class Deobfuscator { //synchronized to make sure the parallelStream doesn't CME with the cache ITranslatingTypeLoader typeLoader = new SynchronizedTypeLoader(createTypeLoader()); + MetadataSystem metadataSystem = new NoRetryMetadataSystem(typeLoader); + metadataSystem.setEagerMethodLoadingEnabled(true);//ensures methods are loaded on classload and prevents race conditions + // DEOBFUSCATE ALL THE THINGS!! @_@ Stopwatch stopwatch = Stopwatch.createStarted(); AtomicInteger count = new AtomicInteger(); @@ -292,7 +301,7 @@ public class Deobfuscator { try { // get the source - CompilationUnit sourceTree = getSourceTree(obfClassEntry.getName(), typeLoader); + CompilationUnit sourceTree = getSourceTree(obfClassEntry.getName(), typeLoader, metadataSystem); // write the file File file = new File(dirOut, deobfClassEntry.getName().replace('.', '/') + ".java"); @@ -686,4 +695,30 @@ public class Deobfuscator { String transform(ClassNode node, ClassWriter writer); } + public static class NoRetryMetadataSystem extends MetadataSystem { + private final Set _failedTypes = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + public NoRetryMetadataSystem(final ITypeLoader typeLoader) { + super(typeLoader); + } + + @Override + protected synchronized TypeDefinition resolveType(final String descriptor, final boolean mightBePrimitive) { + if (_failedTypes.contains(descriptor)) { + return null; + } + + final TypeDefinition result = super.resolveType(descriptor, mightBePrimitive); + + if (result == null) { + _failedTypes.add(descriptor); + } + + return result; + } + + public synchronized TypeDefinition resolve(final TypeReference type) { + return super.resolve(type); + } + } } -- cgit v1.2.3 From 6fd948d323e3819d4fe7f15fbf64113d55de24b4 Mon Sep 17 00:00:00 2001 From: Thiakil Date: Tue, 24 Jul 2018 22:40:51 +0800 Subject: "make sure this is actually a field" properly --- src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java index 83fe296c..83e5e04f 100644 --- a/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java +++ b/src/main/java/cuchaz/enigma/analysis/SourceIndexMethodVisitor.java @@ -76,7 +76,7 @@ public class SourceIndexMethodVisitor extends SourceIndexVisitor { @Override public Void visitMemberReferenceExpression(MemberReferenceExpression node, SourceIndex index) { MemberReference ref = node.getUserData(Keys.MEMBER_REFERENCE); - if (ref != null) { + if (ref instanceof FieldReference) { // make sure this is actually a field String erasedSignature = ref.getErasedSignature(); if (erasedSignature.indexOf('(') >= 0) { -- cgit v1.2.3