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