From fb7b5e4969d55dd49a5c9ae93d490cbad3af7345 Mon Sep 17 00:00:00 2001 From: EpicPlayerA10 Date: Sat, 31 Aug 2024 18:21:42 +0200 Subject: [PATCH 1/7] Fix NPE on Optional.of --- .../objectweb/asm/tree/AbstractInsnNode.java | 3 ++ .../analysis/OriginalSourceInterpreter.java | 14 ++++----- .../tree/analysis/OriginalSourceValue.java | 30 ++++++++++++++----- .../deobfuscator/api/helper/AsmHelper.java | 19 ++++++------ .../api/helper/AsmMathHelper.java | 18 +++++------ 5 files changed, 52 insertions(+), 32 deletions(-) diff --git a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/AbstractInsnNode.java b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/AbstractInsnNode.java index cb6866c4..117df14d 100644 --- a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/AbstractInsnNode.java +++ b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/AbstractInsnNode.java @@ -34,6 +34,8 @@ import java.util.Map; import java.util.function.Consumer; import java.util.function.Predicate; + +import org.jetbrains.annotations.Nullable; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import uwu.narumi.deobfuscator.api.asm.matcher.rule.Match; @@ -344,6 +346,7 @@ public Type asType() { return (Type) ((LdcInsnNode) this).cst; } + @Nullable public Object asConstant() { if (isNumber()) { return asNumber(); diff --git a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceInterpreter.java b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceInterpreter.java index f997093a..412b5b0f 100644 --- a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceInterpreter.java +++ b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceInterpreter.java @@ -135,11 +135,11 @@ public OriginalSourceValue unaryOperation(final AbstractInsnNode insn, final Ori // Narumii start - Predict constant if (AsmMathHelper.isMathUnaryOperation(insn.getOpcode())) { - Optional constant = value.getConstantValue(); + OriginalSourceValue.ConstantValue constant = value.getConstantValue(); - if (constant.isPresent() && constant.get() instanceof Number constNum) { + if (constant != null && constant.get() instanceof Number constNum) { Number result = AsmMathHelper.mathUnaryOperation(constNum, insn.getOpcode()); - return new OriginalSourceValue(size, insn, Optional.of(result)); + return new OriginalSourceValue(size, insn, OriginalSourceValue.ConstantValue.of(result)); } } // Narumii end @@ -179,12 +179,12 @@ public OriginalSourceValue binaryOperation( // Narumii start - Predict constant if (AsmMathHelper.isMathBinaryOperation(insn.getOpcode())) { - Optional constant1 = value1.getConstantValue(); - Optional constant2 = value2.getConstantValue(); + OriginalSourceValue.ConstantValue constant1 = value1.getConstantValue(); + OriginalSourceValue.ConstantValue constant2 = value2.getConstantValue(); - if (constant1.isPresent() && constant2.isPresent() && constant1.get() instanceof Number constNum1 && constant2.get() instanceof Number constNum2) { + if (constant1 != null && constant2 != null && constant1.get() instanceof Number constNum1 && constant2.get() instanceof Number constNum2) { Number result = AsmMathHelper.mathBinaryOperation(constNum1, constNum2, insn.getOpcode()); - return new OriginalSourceValue(size, insn, Optional.of(result)); + return new OriginalSourceValue(size, insn, OriginalSourceValue.ConstantValue.of(result)); } } // Narumii end diff --git a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java index 05d086e4..3741b39a 100644 --- a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java +++ b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java @@ -72,7 +72,8 @@ public class OriginalSourceValue extends SourceValue { * It is so convenient because for example if you want to get value of * a IMUL instruction, then this field already contains the calculated value! No need to calculate it manually from stack values. */ - private Optional constantValue = Optional.empty(); + @Nullable + private ConstantValue constantValue = null; public OriginalSourceValue(int size) { this(size, Set.of()); @@ -91,10 +92,10 @@ public OriginalSourceValue(OriginalSourceValue copiedFrom, AbstractInsnNode insn } public OriginalSourceValue(int size, Set insnSet, @Nullable OriginalSourceValue copiedFrom) { - this(size, insnSet, copiedFrom, Optional.empty()); + this(size, insnSet, copiedFrom, null); } - public OriginalSourceValue(int size, AbstractInsnNode insnNode, Optional constantValue) { + public OriginalSourceValue(int size, AbstractInsnNode insnNode, @Nullable ConstantValue constantValue) { this(size, Set.of(insnNode), null, constantValue); } @@ -107,11 +108,11 @@ public OriginalSourceValue(int size, AbstractInsnNode insnNode, Optional * @param copiedFrom The value from which this value was copied or null if it was not copied * @param constantValue Predicted constant value if exists */ - public OriginalSourceValue(int size, Set insnSet, @Nullable OriginalSourceValue copiedFrom, Optional constantValue) { + public OriginalSourceValue(int size, Set insnSet, @Nullable OriginalSourceValue copiedFrom, @Nullable ConstantValue constantValue) { super(size, insnSet); this.copiedFrom = copiedFrom; this.originalSource = copiedFrom == null ? this : copiedFrom.originalSource; - if (constantValue.isPresent()) { + if (constantValue != null) { // If the constant value is present, then use it this.constantValue = constantValue; } else if (copiedFrom != null) { @@ -121,7 +122,7 @@ public OriginalSourceValue(int size, Set insnSet, @Nullable Or // Try to infer constant value from producer AbstractInsnNode insn = insnSet.iterator().next(); if (insn.isConstant()) { - this.constantValue = Optional.of(insn.asConstant()); + this.constantValue = ConstantValue.of(insn.asConstant()); } } } @@ -150,7 +151,8 @@ public AbstractInsnNode getProducer() { /** * See {@link #constantValue}. */ - public Optional getConstantValue() { + @Nullable + public ConstantValue getConstantValue() { return constantValue; } @@ -183,4 +185,18 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(super.hashCode(), copiedFrom); } + + /** + * We need to create our own {@link Optional}-like class because {@link Optional} can't + * store nullable values which we need to store. + */ + public record ConstantValue(Object value) { + public static ConstantValue of(Object value) { + return new ConstantValue(value); + } + + public Object get() { + return value; + } + } } 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 e589ff96..982b212c 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 @@ -5,7 +5,7 @@ import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.*; -import org.objectweb.asm.tree.analysis.Analyzer; +import org.objectweb.asm.tree.analysis.AnalyzerException; import org.objectweb.asm.tree.analysis.Frame; import org.objectweb.asm.tree.analysis.JumpPredictingAnalyzer; import org.objectweb.asm.tree.analysis.OriginalSourceInterpreter; @@ -142,17 +142,18 @@ public static List getInstructionsBetween( public static Map> analyzeSource( ClassNode classNode, MethodNode methodNode ) { + Map> frames = new HashMap<>(); + Frame[] framesArray; try { - Map> frames = new HashMap<>(); - Frame[] framesArray = - new JumpPredictingAnalyzer(new OriginalSourceInterpreter()).analyze(classNode.name, methodNode); - for (int i = 0; i < framesArray.length; i++) { - frames.put(methodNode.instructions.get(i), framesArray[i]); - } - return frames; - } catch (Exception e) { + framesArray = new JumpPredictingAnalyzer(new OriginalSourceInterpreter()).analyze(classNode.name, methodNode); + } catch (AnalyzerException e) { + // Return null on invalid method return null; } + for (int i = 0; i < framesArray.length; i++) { + frames.put(methodNode.instructions.get(i), framesArray[i]); + } + return frames; } /** diff --git a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmMathHelper.java b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmMathHelper.java index b39483cc..75065a18 100644 --- a/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmMathHelper.java +++ b/deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/AsmMathHelper.java @@ -420,8 +420,8 @@ public static Optional predictIf(JumpInsnNode jumpInsn, Frame constantValue = sourceValue.getConstantValue(); - if (constantValue.isEmpty()) return Optional.empty(); + OriginalSourceValue.ConstantValue constantValue = sourceValue.getConstantValue(); + if (constantValue == null) return Optional.empty(); // Process if statement if (constantValue.get() instanceof Integer value) { @@ -438,9 +438,9 @@ public static Optional predictIf(JumpInsnNode jumpInsn, Frame constValue1 = sourceValue1.getConstantValue(); - Optional constValue2 = sourceValue2.getConstantValue(); - if (constValue1.isEmpty() || constValue2.isEmpty()) return Optional.empty(); + OriginalSourceValue.ConstantValue constValue1 = sourceValue1.getConstantValue(); + OriginalSourceValue.ConstantValue constValue2 = sourceValue2.getConstantValue(); + if (constValue1 == null || constValue2 == null) return Optional.empty(); // Process if statement if (constValue1.get() instanceof Integer value1 && constValue2.get() instanceof Integer value2) { @@ -462,8 +462,8 @@ public static Optional predictIf(JumpInsnNode jumpInsn, Frame predictLookupSwitch(LookupSwitchInsnNode lookupSwitchInsn, Frame frame) { OriginalSourceValue sourceValue = frame.getStack(frame.getStackSize() - 1); - Optional constantValue = sourceValue.getConstantValue(); - if (constantValue.isEmpty()) return Optional.empty(); + OriginalSourceValue.ConstantValue constantValue = sourceValue.getConstantValue(); + if (constantValue == null) return Optional.empty(); if (constantValue.get() instanceof Integer value) { int index = lookupSwitchInsn.keys.indexOf(value); @@ -486,8 +486,8 @@ public static Optional predictLookupSwitch(LookupSwitchInsnNode looku */ public static Optional predictTableSwitch(TableSwitchInsnNode tableSwitchInsn, Frame frame) { OriginalSourceValue sourceValue = frame.getStack(frame.getStackSize() - 1); - Optional constantValue = sourceValue.getConstantValue(); - if (constantValue.isEmpty()) return Optional.empty(); + OriginalSourceValue.ConstantValue constantValue = sourceValue.getConstantValue(); + if (constantValue == null) return Optional.empty(); if (constantValue.get() instanceof Integer value) { int index = value - tableSwitchInsn.min; From 52108b60f2f34ff1910f737846e6b74b47fb3af3 Mon Sep 17 00:00:00 2001 From: EpicPlayerA10 Date: Sat, 31 Aug 2024 18:40:03 +0200 Subject: [PATCH 2/7] fix example --- .../tree/analysis/OriginalSourceValue.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java index 3741b39a..c6fa10c1 100644 --- a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java +++ b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java @@ -60,15 +60,20 @@ public class OriginalSourceValue extends SourceValue { *
    * 1: A:
    * 2:   ldc 12L
-   * 3:   ldiv
-   * 4:   l2i
-   * 5:   lookupswitch {
-   * 6:     ...
-   * 7:   }
+   * 3:   ldc 2L
+   * 4:   ldiv
+   * 5:   l2i
+   * 6:   lookupswitch {
+   * 7:     ...
+   * 8:   }
    * 
* - * In line 2, the constant value is 12L. In line 3, the constant value is 12L / 2L = 6L. In line 4, - * the constant value is 6 (but cast to integer). + * In line 2, the constant value is 12L.
+ * In line 3, the constant value is 2L.
+ * In line 4, the constant value is 12L / 2L = 6L.
+ * In line 5, the constant value is 6 (but cast to integer). + * + *

* It is so convenient because for example if you want to get value of * a IMUL instruction, then this field already contains the calculated value! No need to calculate it manually from stack values. */ From 002b250b01df2408d369929688d2429432d53b74 Mon Sep 17 00:00:00 2001 From: EpicPlayerA10 Date: Sat, 31 Aug 2024 18:42:17 +0200 Subject: [PATCH 3/7] remove space --- .../org/objectweb/asm/tree/analysis/OriginalSourceValue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java index c6fa10c1..1abdaada 100644 --- a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java +++ b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java @@ -70,7 +70,7 @@ public class OriginalSourceValue extends SourceValue { * * In line 2, the constant value is 12L.
* In line 3, the constant value is 2L.
- * In line 4, the constant value is 12L / 2L = 6L.
+ * In line 4, the constant value is 12L / 2L = 6L.
* In line 5, the constant value is 6 (but cast to integer). * *

From 681dfda021e6c8ba4a57dd3a4cb39788ec462dd4 Mon Sep 17 00:00:00 2001 From: EpicPlayerA10 Date: Sat, 31 Aug 2024 18:44:21 +0200 Subject: [PATCH 4/7] line break --- .../org/objectweb/asm/tree/analysis/OriginalSourceValue.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java index 1abdaada..b70695b8 100644 --- a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java +++ b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java @@ -74,8 +74,8 @@ public class OriginalSourceValue extends SourceValue { * In line 5, the constant value is 6 (but cast to integer). * *

- * It is so convenient because for example if you want to get value of - * a IMUL instruction, then this field already contains the calculated value! No need to calculate it manually from stack values. + * It is so convenient because for example if you want to get value of a IMUL instruction, + * then this field already contains the calculated value! No need to calculate it manually from stack values. */ @Nullable private ConstantValue constantValue = null; From 9de3d97d2fdd477efb851b8fc653636fb748dad6 Mon Sep 17 00:00:00 2001 From: EpicPlayerA10 Date: Sat, 31 Aug 2024 18:57:43 +0200 Subject: [PATCH 5/7] better javadoc --- .../objectweb/asm/tree/analysis/OriginalSourceValue.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java index b70695b8..1d38792d 100644 --- a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java +++ b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java @@ -44,6 +44,7 @@ public class OriginalSourceValue extends SourceValue { * 8: C: * 9: ... * + * * When the current class is source value of instruction at line 6, then it will follow all * instructions (DUP, ILOAD) and jumps to get the original source value of this instruction at line 6. * In this example, it will return source value of instruction at line 2. @@ -51,9 +52,9 @@ public class OriginalSourceValue extends SourceValue { public final OriginalSourceValue originalSource; /** - * Predicted constant value. It holds a constant value such as {@link Integer}, {@link Double}, - * {@link Float}, {@link String}, {@link Type} and {@code null}. It also follows all math operations - * and jumps to get the constant value. + * Predicted constant value that holds an object to constant value such as {@link Integer}, {@link Double}, + * {@link Float}, {@link String}, {@link Type} and {@code null}. Additionally, to {@link #originalSource} it is + * also doing all math operations on math operation instructions. * *

* Consider this example: From 267c453da9eafb58e3d3ac90c6c93646feed8e60 Mon Sep 17 00:00:00 2001 From: EpicPlayerA10 Date: Sat, 31 Aug 2024 18:58:55 +0200 Subject: [PATCH 6/7] more more javadoc --- .../org/objectweb/asm/tree/analysis/OriginalSourceValue.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java index 1d38792d..40700eb8 100644 --- a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java +++ b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceValue.java @@ -195,6 +195,9 @@ public int hashCode() { /** * We need to create our own {@link Optional}-like class because {@link Optional} can't * store nullable values which we need to store. + * + * @param value A constant value. It can be {@link Integer}, {@link Double}, + * {@link Float}, {@link String}, {@link Type} or {@code null} */ public record ConstantValue(Object value) { public static ConstantValue of(Object value) { From b340ff993b5bdbd23eee9112ac953e063738ad7a Mon Sep 17 00:00:00 2001 From: EpicPlayerA10 Date: Sat, 31 Aug 2024 19:01:17 +0200 Subject: [PATCH 7/7] better diff comments --- .../asm/tree/analysis/OriginalSourceInterpreter.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceInterpreter.java b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceInterpreter.java index 412b5b0f..db5f7625 100644 --- a/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceInterpreter.java +++ b/deobfuscator-api/src/main/java/org/objectweb/asm/tree/analysis/OriginalSourceInterpreter.java @@ -228,9 +228,7 @@ public OriginalSourceValue merge(final OriginalSourceValue value1, final Origina Set setUnion = ((SmallSet) value1.insns) .union((SmallSet) value2.insns); - // Narumii start - if (setUnion == value1.insns && value1.size == value2.size && Objects.equals(value1.copiedFrom, value2.copiedFrom)) { - // Narumii end + if (setUnion == value1.insns && value1.size == value2.size && Objects.equals(value1.copiedFrom, value2.copiedFrom)) { // Narumii return value1; } else { // Narumii start @@ -242,9 +240,7 @@ public OriginalSourceValue merge(final OriginalSourceValue value1, final Origina // Narumii end } } - // Narumii start - if (value1.size != value2.size || !containsAll(value1.insns, value2.insns) || !Objects.equals(value1.copiedFrom, value2.copiedFrom)) { - // Narumii end + if (value1.size != value2.size || !containsAll(value1.insns, value2.insns) || !Objects.equals(value1.copiedFrom, value2.copiedFrom)) { // Narumii HashSet setUnion = new HashSet<>(); setUnion.addAll(value1.insns); setUnion.addAll(value2.insns);