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