diff --git a/README.md b/README.md
index e2977f65..18c955b0 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,10 @@ A deobfuscator for java
> **This is dev(v2) version of diobfuscator version and it's not completed yet, if you are willing to help there is a list of things that needs to be completed. The old diobfuscator was moved to [v1 branch](https://github.com/narumii/Deobfuscator/tree/v1).**
>
> - Porting old transformers to new code base
-> - Testing InstructionMatcher
+> - Testing `Match` API
> - Implementing/Improving transformers
> - Writing tests
-> - Feedback on how the new api presents itself (mainly InstructionMatcher)
+> - Feedback on how the new api presents itself (mainly `Match` API)
>
>
> 1. *You can also provide samples of obfuscation to help with development of the transformers.*
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/Match.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/Match.java
index 9114195c..51529b8c 100644
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/Match.java
+++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/Match.java
@@ -1,7 +1,11 @@
package uwu.narumi.deobfuscator.api.asm.matcher;
+import org.objectweb.asm.tree.AbstractInsnNode;
import uwu.narumi.deobfuscator.api.asm.InstructionContext;
+import uwu.narumi.deobfuscator.api.asm.MethodContext;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Predicate;
/**
@@ -40,6 +44,26 @@ public boolean matchAndMerge(InstructionContext insnContext, MatchContext curren
return result != null;
}
+ /**
+ * Finds all matches in the method
+ *
+ * @param methodContext Method context
+ * @return List of all matches
+ */
+ public List findAllMatches(MethodContext methodContext) {
+ List allMatches = new ArrayList<>();
+
+ for (AbstractInsnNode insn : methodContext.methodNode().instructions) {
+ InstructionContext insnContext = methodContext.newInsnContext(insn);
+ MatchContext match = this.matchResult(insnContext);
+ if (match != null) {
+ allMatches.add(match);
+ }
+ }
+
+ return allMatches;
+ }
+
/**
* @return {@link MatchContext} if matches or {@code null} if it does not match
*/
@@ -72,15 +96,15 @@ public MatchContext matchResult(InstructionContext insnContext) {
protected abstract boolean test(MatchContext context);
public Match and(Match match) {
- return Match.predicate(context -> this.matchAndMerge(context.insnContext(), context) && match.matchAndMerge(context.insnContext(), context));
+ return Match.of(context -> this.matchAndMerge(context.insnContext(), context) && match.matchAndMerge(context.insnContext(), context));
}
public Match or(Match match) {
- return Match.predicate(context -> this.matchAndMerge(context.insnContext(), context) || match.matchAndMerge(context.insnContext(), context));
+ return Match.of(context -> this.matchAndMerge(context.insnContext(), context) || match.matchAndMerge(context.insnContext(), context));
}
public Match not() {
- return Match.predicate(context -> !matchAndMerge(context.insnContext(), context));
+ return Match.of(context -> !matchAndMerge(context.insnContext(), context));
}
public Match defineTransformation(Transformation transformation) {
@@ -108,7 +132,7 @@ public Transformation transformation() {
* @param predicate Your lambda predicate
* @return A new {@link Match}
*/
- public static Match predicate(Predicate predicate) {
+ public static Match of(Predicate predicate) {
return new Match() {
@Override
protected boolean test(MatchContext context) {
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/group/SequenceMatch.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/group/SequenceMatch.java
index fc43f706..9e02b994 100644
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/group/SequenceMatch.java
+++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/group/SequenceMatch.java
@@ -17,9 +17,9 @@
// TODO: backwards match?
public class SequenceMatch extends Match {
- private static final Match FRAME_MATCH = Match.predicate(context -> context.insn() instanceof FrameNode);
- private static final Match LABEL_MATCH = Match.predicate(context -> context.insn() instanceof LabelNode);
- private static final Match LINE_MATCH = Match.predicate(context -> context.insn() instanceof LineNumberNode);
+ private static final Match FRAME_MATCH = Match.of(context -> context.insn() instanceof FrameNode);
+ private static final Match LABEL_MATCH = Match.of(context -> context.insn() instanceof LabelNode);
+ private static final Match LINE_MATCH = Match.of(context -> context.insn() instanceof LineNumberNode);
private final Match[] matches;
private final List skipMatches = new ArrayList<>(List.of(FRAME_MATCH, LABEL_MATCH, LINE_MATCH));
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/InsnMatch.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/InsnMatch.java
index 0bdbe35e..3104a379 100644
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/InsnMatch.java
+++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/InsnMatch.java
@@ -4,6 +4,9 @@
import uwu.narumi.deobfuscator.api.asm.matcher.Match;
import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext;
+/**
+ * Matches an instruction by its instance.
+ */
public class InsnMatch extends Match {
private final AbstractInsnNode node;
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/IntInsnMatch.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/IntInsnMatch.java
deleted file mode 100644
index b5a0c7ce..00000000
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/IntInsnMatch.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package uwu.narumi.deobfuscator.api.asm.matcher.impl;
-
-import org.objectweb.asm.tree.IntInsnNode;
-import uwu.narumi.deobfuscator.api.asm.matcher.Match;
-import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext;
-
-public class IntInsnMatch extends Match {
-
- private final int opcode;
- private final int operand;
-
- private IntInsnMatch(int opcode, int operand) {
- this.opcode = opcode;
- this.operand = operand;
- }
-
- public static IntInsnMatch of(int opcode, int operand) {
- return new IntInsnMatch(opcode, operand);
- }
-
- @Override
- protected boolean test(MatchContext context) {
- return context.insn() instanceof IntInsnNode intInsn
- && intInsn.getOpcode() == opcode
- && intInsn.operand == operand;
- }
-}
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/NumberMatch.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/NumberMatch.java
index 0fdf406b..5d2501e2 100644
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/NumberMatch.java
+++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/NumberMatch.java
@@ -15,23 +15,23 @@ public static NumberMatch of(Number number) {
}
public static Match of() {
- return predicate(ctx -> ctx.insn().isNumber());
+ return Match.of(ctx -> ctx.insn().isNumber());
}
public static Match numDouble() {
- return predicate(ctx -> ctx.insn().isDouble());
+ return Match.of(ctx -> ctx.insn().isDouble());
}
public static Match numFloat() {
- return predicate(ctx -> ctx.insn().isFloat());
+ return Match.of(ctx -> ctx.insn().isFloat());
}
public static Match numInteger() {
- return predicate(ctx -> ctx.insn().isInteger());
+ return Match.of(ctx -> ctx.insn().isInteger());
}
public static Match numLong() {
- return predicate(ctx -> ctx.insn().isLong());
+ return Match.of(ctx -> ctx.insn().isLong());
}
@Override
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/StringMatch.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/StringMatch.java
index af8821a5..827317bc 100644
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/StringMatch.java
+++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/StringMatch.java
@@ -16,7 +16,7 @@ public static StringMatch of(String string) {
}
public static Match of() {
- return Match.predicate(context -> context.insn().isString());
+ return Match.of(context -> context.insn().isString());
}
@Override
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/TypeMatch.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/TypeMatch.java
deleted file mode 100644
index 906ea3a3..00000000
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/TypeMatch.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package uwu.narumi.deobfuscator.api.asm.matcher.impl;
-
-import org.objectweb.asm.tree.TypeInsnNode;
-import uwu.narumi.deobfuscator.api.asm.matcher.Match;
-import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext;
-
-public class TypeMatch extends Match {
-
- private final int opcode;
- private final String desc;
-
- private TypeMatch(int opcode, String desc) {
- this.opcode = opcode;
- this.desc = desc;
- }
-
- public static TypeMatch of(int opcode, String desc) {
- return new TypeMatch(opcode, desc);
- }
-
- public static TypeMatch of(String desc) {
- return of(-1, desc);
- }
-
- public static TypeMatch of(int opcode) {
- return of(opcode, null);
- }
-
- public static TypeMatch of() {
- return of(-1, null);
- }
-
- @Override
- protected boolean test(MatchContext context) {
- return context.insn() instanceof TypeInsnNode typeInsn
- && (opcode == -1 || typeInsn.getOpcode() == opcode)
- && (desc == null || typeInsn.desc.equals(desc));
- }
-}
diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmHelper.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmHelper.java
index 66c88a27..65e3b00d 100644
--- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmHelper.java
+++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmHelper.java
@@ -219,13 +219,13 @@ public static void removeField(FieldInsnNode fieldInsnNode, Context context) {
&& fieldNode.desc.equals(fieldInsnNode.desc));
}
- public static Optional findMethod(
- ClassNode classNode, MethodInsnNode methodInsnNode) {
- return classNode == null || classNode.methods == null
- ? Optional.empty()
- : classNode.methods.stream()
- .filter(methodNode -> methodNode.name.equals(methodInsnNode.name))
- .filter(methodNode -> methodNode.desc.equals(methodInsnNode.desc))
+ public static Optional findMethod(ClassNode classNode, MethodRef methodRef) {
+ if (classNode == null || classNode.methods == null) {
+ return Optional.empty();
+ }
+ return classNode.methods.stream()
+ .filter(methodNode -> methodNode.name.equals(methodRef.name()))
+ .filter(methodNode -> methodNode.desc.equals(methodRef.desc()))
.findFirst();
}
diff --git a/deobfuscator-impl/src/test/resources/junit-platform.properties b/deobfuscator-impl/src/test/resources/junit-platform.properties
new file mode 100644
index 00000000..317c292e
--- /dev/null
+++ b/deobfuscator-impl/src/test/resources/junit-platform.properties
@@ -0,0 +1,2 @@
+junit.jupiter.execution.parallel.enabled = true
+junit.jupiter.execution.parallel.mode.default = concurrent
diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888StringTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888StringTransformer.java
index b2626cc0..b7e98d25 100644
--- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888StringTransformer.java
+++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888StringTransformer.java
@@ -3,67 +3,82 @@
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import uwu.narumi.deobfuscator.api.asm.ClassWrapper;
+import uwu.narumi.deobfuscator.api.asm.MethodContext;
+import uwu.narumi.deobfuscator.api.asm.MethodRef;
+import uwu.narumi.deobfuscator.api.asm.matcher.Match;
+import uwu.narumi.deobfuscator.api.asm.matcher.group.SequenceMatch;
+import uwu.narumi.deobfuscator.api.asm.matcher.impl.MethodMatch;
+import uwu.narumi.deobfuscator.api.asm.matcher.impl.OpcodeMatch;
+import uwu.narumi.deobfuscator.api.asm.matcher.impl.StringMatch;
import uwu.narumi.deobfuscator.api.context.Context;
import uwu.narumi.deobfuscator.api.transformer.Transformer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
-import java.util.Optional;
/**
* Strings are encrypted using a constant pool size of a provided class.
*/
public class HP888StringTransformer extends Transformer {
+ private static final Match ENCRYPTED_STRING = SequenceMatch.of(
+ StringMatch.of().capture("key"),
+ MethodMatch.invokeStatic().desc("(Ljava/lang/String;)Ljava/lang/String;").capture("decrypt-method")
+ );
+
+ private static final Match CLASS_FOR_CONSTANT_POOL = SequenceMatch.of(
+ MethodMatch.invokeStatic(),
+ OpcodeMatch.of(LDC).and(Match.of(ctx -> ((LdcInsnNode) ctx.insn()).cst instanceof Type)).capture("class"),
+ MethodMatch.invokeInterface()
+ );
+
@Override
protected void transform(ClassWrapper scope, Context context) throws Exception {
- context.classes().stream().map(ClassWrapper::classNode).forEach(classNode -> {
+ context.classes().forEach(classWrapper -> {
List toRemove = new ArrayList<>();
// Find all encrypted strings
- classNode.methods.forEach(methodNode -> Arrays.stream(methodNode.instructions.toArray())
- .filter(node -> node.getOpcode() == INVOKESTATIC)
- .filter(node -> node.getPrevious() != null && node.getPrevious().isString())
- .map(MethodInsnNode.class::cast)
- .filter(node -> node.owner.equals(classNode.name))
- .filter(node -> node.desc.equals("(Ljava/lang/String;)Ljava/lang/String;"))
- .forEach(node -> findMethod(classNode, method -> method.name.equals(node.name) && method.desc.equals(node.desc)).ifPresent(method -> {
- String string = node.getPrevious().asString();
+ classWrapper.methods().forEach(methodNode -> {
+ MethodContext methodContext = MethodContext.framed(classWrapper, methodNode);
+
+ // Find encrypted strings
+ ENCRYPTED_STRING.findAllMatches(methodContext).forEach(matchContext -> {
+ AbstractInsnNode keyInsn = matchContext.captures().get("key").insn();
+ MethodInsnNode decryptMethodInsn = (MethodInsnNode) matchContext.captures().get("decrypt-method").insn();
+ MethodRef methodRef = MethodRef.of(decryptMethodInsn);
+
+ // Get decrypt method
+ findMethod(classWrapper.classNode(), methodRef).ifPresent(decryptMethod -> {
+ String key = keyInsn.asString();
+
+ MethodContext decryptMethodContext = MethodContext.framed(classWrapper, decryptMethod);
+
+ // Find class for constant pool
+ LdcInsnNode constantPoolClassLdc = (LdcInsnNode) CLASS_FOR_CONSTANT_POOL.findAllMatches(decryptMethodContext)
+ .get(0).captures().get("class").insn();
+ Type classForConstantPoolType = (Type) constantPoolClassLdc.cst;
// Prepare data for decryption
- ClassWrapper classForConstantPool = context.getClasses().get(getClassForConstantPool(method).orElseThrow().getInternalName());
+ ClassWrapper classForConstantPool = context.getClasses().get(classForConstantPoolType.getInternalName());
int constantPoolSize = classForConstantPool.getConstantPool().getSize();
- String class0 = classNode.name;
- String class1 = classNode.name;
+ String class0 = classWrapper.name();
+ String class1 = classWrapper.name();
// Decrypt!
- String decryptedString = decrypt(string, constantPoolSize, class0.hashCode(), class1.hashCode());
+ String decryptedString = decrypt(key, constantPoolSize, class0.hashCode(), class1.hashCode());
- methodNode.instructions.remove(node.getPrevious());
- methodNode.instructions.set(node, new LdcInsnNode(decryptedString));
+ methodNode.instructions.remove(keyInsn);
+ methodNode.instructions.set(decryptMethodInsn, new LdcInsnNode(decryptedString));
markChange();
- toRemove.add(method);
- })));
- classNode.methods.removeAll(toRemove);
- toRemove.clear();
+ toRemove.add(decryptMethod);
+ });
+ });
+ });
+ classWrapper.methods().removeAll(toRemove);
});
}
- private Optional getClassForConstantPool(MethodNode methodNode) {
- return Arrays.stream(methodNode.instructions.toArray())
- .filter(node -> node.getOpcode() == INVOKESTATIC)
- .map(AbstractInsnNode::next)
- .filter(next -> next.getOpcode() == LDC)
- .filter(next -> next.next().getOpcode() == INVOKEINTERFACE)
- .filter(abstractInsnNode -> abstractInsnNode instanceof LdcInsnNode)
- .map(LdcInsnNode.class::cast)
- .map(ldcInsnNode -> ldcInsnNode.cst)
- .map(Type.class::cast)
- .findFirst();
- }
-
private String decrypt(String string, int constantPoolSize, int className0HashCode, int className1HashCode) {
char[] charArray = string.toCharArray();
int i = 0;
diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionMPCTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionMPCTransformer.java
index c2d71ea5..d4995089 100644
--- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionMPCTransformer.java
+++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixLongEncryptionMPCTransformer.java
@@ -64,7 +64,7 @@ public class ZelixLongEncryptionMPCTransformer extends Transformer {
.and(StackMatch.of(0, MethodMatch.invokeInterface().desc("(J)J")
.and(StackMatch.of(0, NumberMatch.numLong().capture("decrypt-key"))) // Decrypt key
// Create decrypter
- .and(StackMatch.of(1, MethodMatch.invokeStatic().and(Match.predicate(context ->
+ .and(StackMatch.of(1, MethodMatch.invokeStatic().and(Match.of(context ->
((MethodInsnNode) context.insn()).desc.startsWith("(JJLjava/lang/Object;)"))).capture("create-decrypter-method")
.and(StackMatch.of(0, MethodMatch.invokeVirtual().and(StackMatch.of(0, MethodMatch.invokeStatic())))) // Class lookup
diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixParametersTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixParametersTransformer.java
index c1bd5699..2de625a2 100644
--- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixParametersTransformer.java
+++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/zkm/ZelixParametersTransformer.java
@@ -56,7 +56,7 @@
public class ZelixParametersTransformer extends Transformer {
private static final Match OBJECT_ARRAY_ALOAD = OpcodeMatch.of(ALOAD).and(
- Match.predicate(context -> {
+ Match.of(context -> {
// The object array is always the first argument to method
return ((VarInsnNode) context.insn()).var == MethodHelper.getFirstParameterIdx(context.insnContext().methodNode());
}));
@@ -71,7 +71,7 @@ public class ZelixParametersTransformer extends Transformer {
))
);
- private static final Match OBJECT_ARRAY_VAR_USAGE = Match.predicate(ctx -> ctx.insn().isVarStore()).capture("var-store")
+ private static final Match OBJECT_ARRAY_VAR_USAGE = Match.of(ctx -> ctx.insn().isVarStore()).capture("var-store")
.and(
StackMatch.of(0, MethodMatch.invokeVirtual().capture("to-primitive") // Converting to a primitive type
.and(OBJECT_ARRAY_ACCESS)