Open-source multiplayer game server compatible with the RuneScape client
https://www.openrs2.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
6.1 KiB
238 lines
6.1 KiB
package dev.openrs2.deob.analysis;
|
|
|
|
import java.util.List;
|
|
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.ImmutableSet;
|
|
import com.google.common.collect.Sets;
|
|
import dev.openrs2.asm.InsnNodeUtils;
|
|
import org.objectweb.asm.Opcodes;
|
|
import org.objectweb.asm.Type;
|
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
|
import org.objectweb.asm.tree.IincInsnNode;
|
|
import org.objectweb.asm.tree.analysis.AnalyzerException;
|
|
import org.objectweb.asm.tree.analysis.BasicInterpreter;
|
|
import org.objectweb.asm.tree.analysis.BasicValue;
|
|
import org.objectweb.asm.tree.analysis.Interpreter;
|
|
|
|
public final class IntInterpreter extends Interpreter<IntValue> {
|
|
private static final int MAX_TRACKED_VALUES = 1;
|
|
|
|
private final Interpreter<BasicValue> basicInterpreter = new BasicInterpreter();
|
|
private final ImmutableSet<Integer>[] parameters;
|
|
|
|
public IntInterpreter(ImmutableSet<Integer>[] parameters) {
|
|
super(Opcodes.ASM7);
|
|
this.parameters = parameters;
|
|
}
|
|
|
|
@Override
|
|
public IntValue newValue(Type type) {
|
|
var basicValue = basicInterpreter.newValue(type);
|
|
if (basicValue == null) {
|
|
return null;
|
|
}
|
|
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
|
|
@Override
|
|
public IntValue newParameterValue(boolean isInstanceMethod, int local, Type type) {
|
|
var basicValue = basicInterpreter.newParameterValue(isInstanceMethod, local, type);
|
|
if (basicValue == null) {
|
|
return null;
|
|
}
|
|
|
|
if (parameters != null) {
|
|
int parameterIndex;
|
|
if (isInstanceMethod) {
|
|
if (local == 0) {
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
parameterIndex = local - 1;
|
|
} else {
|
|
parameterIndex = local;
|
|
}
|
|
|
|
var parameter = parameters[parameterIndex];
|
|
if (parameter != null) {
|
|
return IntValue.newConstant(basicValue, parameter);
|
|
}
|
|
}
|
|
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
|
|
@Override
|
|
public IntValue newOperation(AbstractInsnNode insn) throws AnalyzerException {
|
|
var basicValue = basicInterpreter.newOperation(insn);
|
|
if (basicValue == null) {
|
|
return null;
|
|
}
|
|
|
|
if (InsnNodeUtils.isIntConstant(insn)) {
|
|
return IntValue.newConstant(basicValue, InsnNodeUtils.getIntConstant(insn));
|
|
}
|
|
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
|
|
@Override
|
|
public IntValue copyOperation(AbstractInsnNode insn, IntValue value) {
|
|
return value;
|
|
}
|
|
|
|
@Override
|
|
public IntValue unaryOperation(AbstractInsnNode insn, IntValue value) throws AnalyzerException {
|
|
var basicValue = basicInterpreter.unaryOperation(insn, value.getBasicValue());
|
|
if (basicValue == null) {
|
|
return null;
|
|
}
|
|
|
|
if (value.isUnknown()) {
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
|
|
var set = ImmutableSet.<Integer>builder();
|
|
|
|
for (var v : value.getIntValues()) {
|
|
switch (insn.getOpcode()) {
|
|
case Opcodes.INEG:
|
|
set.add(-v);
|
|
break;
|
|
case Opcodes.IINC:
|
|
var iinc = (IincInsnNode) insn;
|
|
set.add(v + iinc.incr);
|
|
break;
|
|
case Opcodes.I2B:
|
|
set.add((int) (byte) (int) v);
|
|
break;
|
|
case Opcodes.I2C:
|
|
set.add((int) (char) (int) v);
|
|
break;
|
|
case Opcodes.I2S:
|
|
set.add((int) (short) (int) v);
|
|
break;
|
|
default:
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
}
|
|
|
|
return IntValue.newConstant(basicValue, set.build());
|
|
}
|
|
|
|
@Override
|
|
public IntValue binaryOperation(AbstractInsnNode insn, IntValue value1, IntValue value2) throws AnalyzerException {
|
|
var basicValue = basicInterpreter.binaryOperation(insn, value1.getBasicValue(), value2.getBasicValue());
|
|
if (basicValue == null) {
|
|
return null;
|
|
}
|
|
|
|
if (value1.isUnknown() || value2.isUnknown()) {
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
|
|
var set = ImmutableSet.<Integer>builder();
|
|
|
|
for (var v1 : value1.getIntValues()) {
|
|
for (var v2 : value2.getIntValues()) {
|
|
switch (insn.getOpcode()) {
|
|
case Opcodes.IADD:
|
|
set.add(v1 + v2);
|
|
break;
|
|
case Opcodes.ISUB:
|
|
set.add(v1 - v2);
|
|
break;
|
|
case Opcodes.IMUL:
|
|
set.add(v1 * v2);
|
|
break;
|
|
case Opcodes.IDIV:
|
|
if (v2 == 0) {
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
set.add(v1 / v2);
|
|
break;
|
|
case Opcodes.IREM:
|
|
if (v2 == 0) {
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
set.add(v1 % v2);
|
|
break;
|
|
case Opcodes.ISHL:
|
|
set.add(v1 << v2);
|
|
break;
|
|
case Opcodes.ISHR:
|
|
set.add(v1 >> v2);
|
|
break;
|
|
case Opcodes.IUSHR:
|
|
set.add(v1 >>> v2);
|
|
break;
|
|
case Opcodes.IAND:
|
|
set.add(v1 & v2);
|
|
break;
|
|
case Opcodes.IOR:
|
|
set.add(v1 | v2);
|
|
break;
|
|
case Opcodes.IXOR:
|
|
set.add(v1 ^ v2);
|
|
break;
|
|
default:
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
return IntValue.newConstant(basicValue, set.build());
|
|
}
|
|
|
|
@Override
|
|
public IntValue ternaryOperation(AbstractInsnNode insn, IntValue value1, IntValue value2, IntValue value3) throws AnalyzerException {
|
|
var basicValue = basicInterpreter.ternaryOperation(insn, value1.getBasicValue(), value2.getBasicValue(), value3.getBasicValue());
|
|
if (basicValue == null) {
|
|
return null;
|
|
}
|
|
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
|
|
@Override
|
|
public IntValue naryOperation(AbstractInsnNode insn, List<? extends IntValue> values) throws AnalyzerException {
|
|
var args = values.stream()
|
|
.map(IntValue::getBasicValue)
|
|
.collect(ImmutableList.toImmutableList());
|
|
var basicValue = basicInterpreter.naryOperation(insn, args);
|
|
if (basicValue == null) {
|
|
return null;
|
|
}
|
|
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
|
|
@Override
|
|
public void returnOperation(AbstractInsnNode insn, IntValue value, IntValue expected) throws AnalyzerException {
|
|
basicInterpreter.returnOperation(insn, value.getBasicValue(), expected.getBasicValue());
|
|
}
|
|
|
|
@Override
|
|
public IntValue merge(IntValue value1, IntValue value2) {
|
|
var basicValue = basicInterpreter.merge(value1.getBasicValue(), value2.getBasicValue());
|
|
if (basicValue == null) {
|
|
return null;
|
|
}
|
|
|
|
if (value1.equals(value2)) {
|
|
return value1;
|
|
}
|
|
|
|
if (value1.isUnknown() || value2.isUnknown()) {
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
|
|
var set = ImmutableSet.copyOf(Sets.union(value1.getIntValues(), value2.getIntValues()));
|
|
if (set.size() > MAX_TRACKED_VALUES) {
|
|
return IntValue.newUnknown(basicValue);
|
|
}
|
|
|
|
return IntValue.newConstant(basicValue, set);
|
|
}
|
|
}
|
|
|