Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix NPE on Optional.of #85

Merged
merged 7 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -344,6 +346,7 @@ public Type asType() {
return (Type) ((LdcInsnNode) this).cst;
}

@Nullable
public Object asConstant() {
if (isNumber()) {
return asNumber();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ public OriginalSourceValue unaryOperation(final AbstractInsnNode insn, final Ori

// Narumii start - Predict constant
if (AsmMathHelper.isMathUnaryOperation(insn.getOpcode())) {
Optional<Object> 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
Expand Down Expand Up @@ -179,12 +179,12 @@ public OriginalSourceValue binaryOperation(

// Narumii start - Predict constant
if (AsmMathHelper.isMathBinaryOperation(insn.getOpcode())) {
Optional<Object> constant1 = value1.getConstantValue();
Optional<Object> 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
Expand Down Expand Up @@ -228,9 +228,7 @@ public OriginalSourceValue merge(final OriginalSourceValue value1, final Origina
Set<AbstractInsnNode> setUnion =
((SmallSet<AbstractInsnNode>) value1.insns)
.union((SmallSet<AbstractInsnNode>) 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
Expand All @@ -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<AbstractInsnNode> setUnion = new HashSet<>();
setUnion.addAll(value1.insns);
setUnion.addAll(value2.insns);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,35 +44,42 @@ public class OriginalSourceValue extends SourceValue {
* 8: C:
* 9: ...
* </pre>
*
* 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.
*/
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.
*
* <p>
* Consider this example:
* <pre>
* 1: A:
* 2: ldc 12L
* 3: ldiv
* 4: l2i
* 5: lookupswitch {
* 6: ...
* 7: }
* 3: ldc 2L
* 4: ldiv
* 5: l2i
* 6: lookupswitch {
* 7: ...
* 8: }
* </pre>
*
* 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).
* 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.
* In line 2, the constant value is 12L.<br>
* In line 3, the constant value is 2L.<br>
* In line 4, the constant value is 12L / 2L = 6L.<br>
* In line 5, the constant value is 6 (but cast to integer).
*
* <p>
* 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<Object> constantValue = Optional.empty();
@Nullable
private ConstantValue constantValue = null;

public OriginalSourceValue(int size) {
this(size, Set.of());
Expand All @@ -91,10 +98,10 @@ public OriginalSourceValue(OriginalSourceValue copiedFrom, AbstractInsnNode insn
}

public OriginalSourceValue(int size, Set<AbstractInsnNode> insnSet, @Nullable OriginalSourceValue copiedFrom) {
this(size, insnSet, copiedFrom, Optional.empty());
this(size, insnSet, copiedFrom, null);
}

public OriginalSourceValue(int size, AbstractInsnNode insnNode, Optional<Object> constantValue) {
public OriginalSourceValue(int size, AbstractInsnNode insnNode, @Nullable ConstantValue constantValue) {
this(size, Set.of(insnNode), null, constantValue);
}

Expand All @@ -107,11 +114,11 @@ public OriginalSourceValue(int size, AbstractInsnNode insnNode, Optional<Object>
* @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<AbstractInsnNode> insnSet, @Nullable OriginalSourceValue copiedFrom, Optional<Object> constantValue) {
public OriginalSourceValue(int size, Set<AbstractInsnNode> 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) {
Expand All @@ -121,7 +128,7 @@ public OriginalSourceValue(int size, Set<AbstractInsnNode> 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());
}
}
}
Expand Down Expand Up @@ -150,7 +157,8 @@ public AbstractInsnNode getProducer() {
/**
* See {@link #constantValue}.
*/
public Optional<Object> getConstantValue() {
@Nullable
public ConstantValue getConstantValue() {
return constantValue;
}

Expand Down Expand Up @@ -183,4 +191,21 @@ 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.
*
* @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) {
return new ConstantValue(value);
}

public Object get() {
return value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -142,17 +142,18 @@ public static List<AbstractInsnNode> getInstructionsBetween(
public static Map<AbstractInsnNode, Frame<OriginalSourceValue>> analyzeSource(
ClassNode classNode, MethodNode methodNode
) {
Map<AbstractInsnNode, Frame<OriginalSourceValue>> frames = new HashMap<>();
Frame<OriginalSourceValue>[] framesArray;
try {
Map<AbstractInsnNode, Frame<OriginalSourceValue>> frames = new HashMap<>();
Frame<OriginalSourceValue>[] 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;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,8 +420,8 @@ public static Optional<Boolean> predictIf(JumpInsnNode jumpInsn, Frame<OriginalS

// Get instruction from stack that is passed to if statement
OriginalSourceValue sourceValue = frame.getStack(frame.getStackSize() - 1);
Optional<Object> 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) {
Expand All @@ -438,9 +438,9 @@ public static Optional<Boolean> predictIf(JumpInsnNode jumpInsn, Frame<OriginalS
// Get instructions from stack that are passed to if statement
OriginalSourceValue sourceValue1 = frame.getStack(frame.getStackSize() - 2);
OriginalSourceValue sourceValue2 = frame.getStack(frame.getStackSize() - 1);
Optional<Object> constValue1 = sourceValue1.getConstantValue();
Optional<Object> 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) {
Expand All @@ -462,8 +462,8 @@ public static Optional<Boolean> predictIf(JumpInsnNode jumpInsn, Frame<OriginalS
*/
public static Optional<LabelNode> predictLookupSwitch(LookupSwitchInsnNode lookupSwitchInsn, Frame<OriginalSourceValue> frame) {
OriginalSourceValue sourceValue = frame.getStack(frame.getStackSize() - 1);
Optional<Object> 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);
Expand All @@ -486,8 +486,8 @@ public static Optional<LabelNode> predictLookupSwitch(LookupSwitchInsnNode looku
*/
public static Optional<LabelNode> predictTableSwitch(TableSwitchInsnNode tableSwitchInsn, Frame<OriginalSourceValue> frame) {
OriginalSourceValue sourceValue = frame.getStack(frame.getStackSize() - 1);
Optional<Object> 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;
Expand Down