forked from openrs2/openrs2
parent
f56b288663
commit
1864b6b630
@ -1,239 +0,0 @@ |
|||||||
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.InsnNodeUtilsKt; |
|
||||||
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; |
|
||||||
} |
|
||||||
|
|
||||||
var v = InsnNodeUtilsKt.getIntConstant(insn); |
|
||||||
if (v != null) { |
|
||||||
return IntValue.newConstant(basicValue, v); |
|
||||||
} |
|
||||||
|
|
||||||
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); |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,167 @@ |
|||||||
|
package dev.openrs2.deob.analysis |
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet |
||||||
|
import com.google.common.collect.Sets |
||||||
|
import dev.openrs2.asm.intConstant |
||||||
|
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.Interpreter |
||||||
|
|
||||||
|
class IntInterpreter(private val parameters: Array<ImmutableSet<Int>?>?) : Interpreter<IntValue>(Opcodes.ASM7) { |
||||||
|
private val basicInterpreter = BasicInterpreter() |
||||||
|
|
||||||
|
override fun newValue(type: Type?): IntValue? { |
||||||
|
val basicValue = basicInterpreter.newValue(type) ?: return null |
||||||
|
return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
|
||||||
|
override fun newParameterValue(isInstanceMethod: Boolean, local: Int, type: Type): IntValue { |
||||||
|
val basicValue = basicInterpreter.newParameterValue(isInstanceMethod, local, type) |
||||||
|
|
||||||
|
if (parameters != null) { |
||||||
|
val parameterIndex = when { |
||||||
|
isInstanceMethod && local == 0 -> return IntValue.newUnknown(basicValue) |
||||||
|
isInstanceMethod -> local - 1 |
||||||
|
else -> local |
||||||
|
} |
||||||
|
|
||||||
|
val parameter = parameters[parameterIndex] |
||||||
|
if (parameter != null) { |
||||||
|
return IntValue.newConstant(basicValue, parameter) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
|
||||||
|
@Throws(AnalyzerException::class) |
||||||
|
override fun newOperation(insn: AbstractInsnNode): IntValue { |
||||||
|
val basicValue = basicInterpreter.newOperation(insn) |
||||||
|
val v = insn.intConstant |
||||||
|
return if (v != null) { |
||||||
|
IntValue.newConstant(basicValue, v) |
||||||
|
} else { |
||||||
|
IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun copyOperation(insn: AbstractInsnNode, value: IntValue): IntValue { |
||||||
|
return value |
||||||
|
} |
||||||
|
|
||||||
|
@Throws(AnalyzerException::class) |
||||||
|
override fun unaryOperation(insn: AbstractInsnNode, value: IntValue): IntValue? { |
||||||
|
val basicValue = basicInterpreter.unaryOperation(insn, value.basicValue) ?: return null |
||||||
|
|
||||||
|
if (value.isUnknown) { |
||||||
|
return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
|
||||||
|
val set = ImmutableSet.builder<Int>() |
||||||
|
for (v in value.intValues) { |
||||||
|
val result = when { |
||||||
|
insn.opcode == Opcodes.INEG -> -v |
||||||
|
insn is IincInsnNode -> v + insn.incr |
||||||
|
insn.opcode == Opcodes.I2B -> v.toByte().toInt() |
||||||
|
insn.opcode == Opcodes.I2C -> v.toChar().toInt() |
||||||
|
insn.opcode == Opcodes.I2S -> v.toShort().toInt() |
||||||
|
else -> return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
set.add(result) |
||||||
|
} |
||||||
|
return IntValue.newConstant(basicValue, set.build()) |
||||||
|
} |
||||||
|
|
||||||
|
@Throws(AnalyzerException::class) |
||||||
|
override fun binaryOperation(insn: AbstractInsnNode, value1: IntValue, value2: IntValue): IntValue? { |
||||||
|
val basicValue = basicInterpreter.binaryOperation(insn, value1.basicValue, value2.basicValue) ?: return null |
||||||
|
|
||||||
|
if (value1.isUnknown || value2.isUnknown) { |
||||||
|
return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
|
||||||
|
val set = ImmutableSet.builder<Int>() |
||||||
|
for (v1 in value1.intValues) { |
||||||
|
for (v2 in value2.intValues) { |
||||||
|
val result = when (insn.opcode) { |
||||||
|
Opcodes.IADD -> v1 + v2 |
||||||
|
Opcodes.ISUB -> v1 - v2 |
||||||
|
Opcodes.IMUL -> v1 * v2 |
||||||
|
Opcodes.IDIV -> { |
||||||
|
if (v2 == 0) { |
||||||
|
return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
v1 / v2 |
||||||
|
} |
||||||
|
Opcodes.IREM -> { |
||||||
|
if (v2 == 0) { |
||||||
|
return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
v1 % v2 |
||||||
|
} |
||||||
|
Opcodes.ISHL -> v1 shl v2 |
||||||
|
Opcodes.ISHR -> v1 shr v2 |
||||||
|
Opcodes.IUSHR -> v1 ushr v2 |
||||||
|
Opcodes.IAND -> v1 and v2 |
||||||
|
Opcodes.IOR -> v1 or v2 |
||||||
|
Opcodes.IXOR -> v1 xor v2 |
||||||
|
else -> return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
set.add(result) |
||||||
|
} |
||||||
|
} |
||||||
|
return IntValue.newConstant(basicValue, set.build()) |
||||||
|
} |
||||||
|
|
||||||
|
@Throws(AnalyzerException::class) |
||||||
|
override fun ternaryOperation( |
||||||
|
insn: AbstractInsnNode, |
||||||
|
value1: IntValue, |
||||||
|
value2: IntValue, |
||||||
|
value3: IntValue |
||||||
|
): IntValue? { |
||||||
|
val basicValue = |
||||||
|
basicInterpreter.ternaryOperation(insn, value1.basicValue, value2.basicValue, value3.basicValue) |
||||||
|
?: return null |
||||||
|
return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
|
||||||
|
@Throws(AnalyzerException::class) |
||||||
|
override fun naryOperation(insn: AbstractInsnNode, values: List<IntValue>): IntValue? { |
||||||
|
val args = values.map { it.basicValue }.toList() |
||||||
|
val basicValue = basicInterpreter.naryOperation(insn, args) ?: return null |
||||||
|
return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
|
||||||
|
@Throws(AnalyzerException::class) |
||||||
|
override fun returnOperation(insn: AbstractInsnNode, value: IntValue, expected: IntValue) { |
||||||
|
basicInterpreter.returnOperation(insn, value.basicValue, expected.basicValue) |
||||||
|
} |
||||||
|
|
||||||
|
override fun merge(value1: IntValue, value2: IntValue): IntValue { |
||||||
|
val basicValue = basicInterpreter.merge(value1.basicValue, value2.basicValue) |
||||||
|
|
||||||
|
if (value1 == value2) { |
||||||
|
return value1 |
||||||
|
} |
||||||
|
|
||||||
|
if (value1.isUnknown || value2.isUnknown) { |
||||||
|
return IntValue.newUnknown(basicValue) |
||||||
|
} |
||||||
|
|
||||||
|
val set = ImmutableSet.copyOf(Sets.union(value1.intValues, value2.intValues)) |
||||||
|
return if (set.size > MAX_TRACKED_VALUES) { |
||||||
|
IntValue.newUnknown(basicValue) |
||||||
|
} else { |
||||||
|
IntValue.newConstant(basicValue, set) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
companion object { |
||||||
|
private const val MAX_TRACKED_VALUES = 1 |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue