Skip to content

Commit

Permalink
Merge pull request #85 from EpicPlayerA10/fix/fix-npe-on-optional-of
Browse files Browse the repository at this point in the history
Fix NPE on Optional.of
  • Loading branch information
narumii authored Aug 31, 2024
2 parents e703928 + b340ff9 commit c96a242
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 50 deletions.
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

0 comments on commit c96a242

Please sign in to comment.