Convert InsnMatcher to Kotlin

pull/48/head
Graham 4 years ago
parent e4fe120bf7
commit 0b3f24fed2
  1. 323
      asm/src/main/java/dev/openrs2/asm/InsnMatcher.java
  2. 286
      asm/src/main/java/dev/openrs2/asm/InsnMatcher.kt

@ -1,323 +0,0 @@
package dev.openrs2.asm;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.Printer;
public final class InsnMatcher {
private static final int PRIVATE_USE_AREA = 0xE000;
private static final ImmutableMap<String, int[]> OPCODE_GROUPS = ImmutableMap.<String, int[]>builder()
.put("InsnNode", new int[] {
Opcodes.NOP,
Opcodes.ACONST_NULL,
Opcodes.ICONST_M1,
Opcodes.ICONST_0,
Opcodes.ICONST_1,
Opcodes.ICONST_2,
Opcodes.ICONST_3,
Opcodes.ICONST_4,
Opcodes.ICONST_5,
Opcodes.LCONST_0,
Opcodes.LCONST_1,
Opcodes.FCONST_0,
Opcodes.FCONST_1,
Opcodes.FCONST_2,
Opcodes.DCONST_0,
Opcodes.DCONST_1,
Opcodes.IALOAD,
Opcodes.LALOAD,
Opcodes.FALOAD,
Opcodes.DALOAD,
Opcodes.AALOAD,
Opcodes.BALOAD,
Opcodes.CALOAD,
Opcodes.SALOAD,
Opcodes.IASTORE,
Opcodes.LASTORE,
Opcodes.FASTORE,
Opcodes.DASTORE,
Opcodes.AASTORE,
Opcodes.BASTORE,
Opcodes.CASTORE,
Opcodes.SASTORE,
Opcodes.POP,
Opcodes.POP2,
Opcodes.DUP,
Opcodes.DUP_X1,
Opcodes.DUP_X2,
Opcodes.DUP2,
Opcodes.DUP2_X1,
Opcodes.DUP2_X2,
Opcodes.SWAP,
Opcodes.IADD,
Opcodes.LADD,
Opcodes.FADD,
Opcodes.DADD,
Opcodes.ISUB,
Opcodes.LSUB,
Opcodes.FSUB,
Opcodes.DSUB,
Opcodes.IMUL,
Opcodes.LMUL,
Opcodes.FMUL,
Opcodes.DMUL,
Opcodes.IDIV,
Opcodes.LDIV,
Opcodes.FDIV,
Opcodes.DDIV,
Opcodes.IREM,
Opcodes.LREM,
Opcodes.FREM,
Opcodes.DREM,
Opcodes.INEG,
Opcodes.LNEG,
Opcodes.FNEG,
Opcodes.DNEG,
Opcodes.ISHL,
Opcodes.LSHL,
Opcodes.ISHR,
Opcodes.LSHR,
Opcodes.IUSHR,
Opcodes.LUSHR,
Opcodes.IAND,
Opcodes.LAND,
Opcodes.IOR,
Opcodes.LOR,
Opcodes.IXOR,
Opcodes.LXOR,
Opcodes.I2L,
Opcodes.I2F,
Opcodes.I2D,
Opcodes.L2I,
Opcodes.L2F,
Opcodes.L2D,
Opcodes.F2I,
Opcodes.F2L,
Opcodes.F2D,
Opcodes.D2I,
Opcodes.D2L,
Opcodes.D2F,
Opcodes.I2B,
Opcodes.I2C,
Opcodes.I2S,
Opcodes.LCMP,
Opcodes.FCMPL,
Opcodes.FCMPG,
Opcodes.DCMPL,
Opcodes.DCMPG,
Opcodes.IRETURN,
Opcodes.LRETURN,
Opcodes.FRETURN,
Opcodes.DRETURN,
Opcodes.ARETURN,
Opcodes.RETURN,
Opcodes.ARRAYLENGTH,
Opcodes.ATHROW,
Opcodes.MONITORENTER,
Opcodes.MONITOREXIT
})
.put("IntInsnNode", new int[] {
Opcodes.BIPUSH,
Opcodes.SIPUSH,
Opcodes.NEWARRAY
})
.put("VarInsnNode", new int[] {
Opcodes.ILOAD,
Opcodes.LLOAD,
Opcodes.FLOAD,
Opcodes.DLOAD,
Opcodes.ALOAD,
Opcodes.ISTORE,
Opcodes.LSTORE,
Opcodes.FSTORE,
Opcodes.DSTORE,
Opcodes.ASTORE,
Opcodes.RET
})
.put("TypeInsnNode", new int[] {
Opcodes.NEW,
Opcodes.ANEWARRAY,
Opcodes.CHECKCAST,
Opcodes.INSTANCEOF
})
.put("FieldInsnNode", new int[] {
Opcodes.GETSTATIC,
Opcodes.PUTSTATIC,
Opcodes.GETFIELD,
Opcodes.PUTFIELD
})
.put("MethodInsnNode", new int[] {
Opcodes.INVOKEVIRTUAL,
Opcodes.INVOKESPECIAL,
Opcodes.INVOKESTATIC,
Opcodes.INVOKEINTERFACE
})
.put("InvokeDynamicInsnNode", new int[] {
Opcodes.INVOKEDYNAMIC
})
.put("JumpInsnNode", new int[] {
Opcodes.IFEQ,
Opcodes.IFNE,
Opcodes.IFLT,
Opcodes.IFGE,
Opcodes.IFGT,
Opcodes.IFLE,
Opcodes.IF_ICMPEQ,
Opcodes.IF_ICMPNE,
Opcodes.IF_ICMPLT,
Opcodes.IF_ICMPGE,
Opcodes.IF_ICMPGT,
Opcodes.IF_ICMPLE,
Opcodes.IF_ACMPEQ,
Opcodes.IF_ACMPNE,
Opcodes.GOTO,
Opcodes.JSR,
Opcodes.IFNULL,
Opcodes.IFNONNULL
})
.put("LdcInsnNode", new int[] {
Opcodes.LDC
})
.put("IincInsnNode", new int[] {
Opcodes.IINC
})
.put("TableSwitchInsnNode", new int[] {
Opcodes.TABLESWITCH
})
.put("LookupSwitchInsnNode", new int[] {
Opcodes.LOOKUPSWITCH
})
.put("MultiANewArrayInsnNode", new int[] {
Opcodes.MULTIANEWARRAY
})
.put("ICONST", new int[] {
Opcodes.ICONST_M1,
Opcodes.ICONST_0,
Opcodes.ICONST_1,
Opcodes.ICONST_2,
Opcodes.ICONST_3,
Opcodes.ICONST_4,
Opcodes.ICONST_5
})
.put("FCONST", new int[] {
Opcodes.FCONST_0,
Opcodes.FCONST_1,
Opcodes.FCONST_2
})
.put("DCONST", new int[] {
Opcodes.DCONST_0,
Opcodes.DCONST_1
})
.build();
private static char opcodeToCodepoint(int opcode) {
return (char) (PRIVATE_USE_AREA + opcode);
}
private static List<AbstractInsnNode> createRealInsnList(InsnList list) {
List<AbstractInsnNode> realInsns = new ArrayList<>();
for (var insn : list) {
if (insn.getOpcode() != -1) {
realInsns.add(insn);
}
}
return realInsns;
}
private static String createCodepointSeq(List<AbstractInsnNode> insns) {
var codepoints = new char[insns.size()];
for (var i = 0; i < codepoints.length; i++) {
codepoints[i] = opcodeToCodepoint(insns.get(i).getOpcode());
}
return new String(codepoints);
}
public static InsnMatcher compile(String expr) {
var pattern = new StringBuilder();
var opcode = new StringBuilder();
for (var i = 0; i < expr.length(); i++) {
var c = expr.charAt(i);
if (Character.isLetterOrDigit(c) || c == '_') {
opcode.append(c);
} else {
if (opcode.length() > 0) {
appendOpcodeRegex(pattern, opcode.toString());
opcode.delete(0, opcode.length());
}
if (!Character.isWhitespace(c)) {
pattern.append(c);
}
}
}
if (opcode.length() > 0) {
appendOpcodeRegex(pattern, opcode.toString());
}
return new InsnMatcher(Pattern.compile(pattern.toString()));
}
private static void appendOpcodeRegex(StringBuilder pattern, String opcode) {
for (var i = 0; i < Printer.OPCODES.length; i++) {
if (opcode.equals(Printer.OPCODES[i])) {
pattern.append(opcodeToCodepoint(i));
return;
}
}
var group = OPCODE_GROUPS.get(opcode);
if (group != null) {
pattern.append('(');
for (var i = 0; i < group.length; i++) {
pattern.append(opcodeToCodepoint(group[i]));
if (i != group.length - 1) {
pattern.append('|');
}
}
pattern.append(')');
return;
}
if (opcode.equals("AbstractInsnNode")) {
pattern.append('.');
return;
}
throw new IllegalArgumentException(opcode + " is not a valid opcode or opcode group");
}
private final Pattern pattern;
private InsnMatcher(Pattern pattern) {
this.pattern = pattern;
}
public Stream<ImmutableList<AbstractInsnNode>> match(MethodNode method) {
return match(method.instructions);
}
public Stream<ImmutableList<AbstractInsnNode>> match(InsnList list) {
Stream.Builder<ImmutableList<AbstractInsnNode>> matches = Stream.builder();
var insns = createRealInsnList(list);
var matcher = pattern.matcher(createCodepointSeq(insns));
while (matcher.find()) {
var start = matcher.start();
var end = matcher.end();
matches.add(ImmutableList.copyOf(insns.subList(start, end)));
}
return matches.build();
}
}

@ -0,0 +1,286 @@
package dev.openrs2.asm
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.AbstractInsnNode
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.MethodNode
import org.objectweb.asm.util.Printer
import java.util.stream.Stream
import kotlin.streams.asStream
class InsnMatcher private constructor(private val regex: Regex) {
// TODO(gpe): use Sequence<T> when the rest of the deobfuscator is ported to Kotlin
fun match(method: MethodNode): Stream<List<AbstractInsnNode>> {
return match(method.instructions)
}
fun match(list: InsnList): Stream<List<AbstractInsnNode>> {
val insns = list.filter { it.opcode != -1 }.toList()
val codepoints = insns.map { opcodeToCodepoint(it.opcode) }.toCharArray()
return regex.findAll(String(codepoints)).map {
insns.subList(it.range.first, it.range.last + 1)
}.asStream()
}
companion object {
private const val PRIVATE_USE_AREA = 0xE000
private val OPCODE_GROUPS = mapOf(
"InsnNode" to intArrayOf(
Opcodes.NOP,
Opcodes.ACONST_NULL,
Opcodes.ICONST_M1,
Opcodes.ICONST_0,
Opcodes.ICONST_1,
Opcodes.ICONST_2,
Opcodes.ICONST_3,
Opcodes.ICONST_4,
Opcodes.ICONST_5,
Opcodes.LCONST_0,
Opcodes.LCONST_1,
Opcodes.FCONST_0,
Opcodes.FCONST_1,
Opcodes.FCONST_2,
Opcodes.DCONST_0,
Opcodes.DCONST_1,
Opcodes.IALOAD,
Opcodes.LALOAD,
Opcodes.FALOAD,
Opcodes.DALOAD,
Opcodes.AALOAD,
Opcodes.BALOAD,
Opcodes.CALOAD,
Opcodes.SALOAD,
Opcodes.IASTORE,
Opcodes.LASTORE,
Opcodes.FASTORE,
Opcodes.DASTORE,
Opcodes.AASTORE,
Opcodes.BASTORE,
Opcodes.CASTORE,
Opcodes.SASTORE,
Opcodes.POP,
Opcodes.POP2,
Opcodes.DUP,
Opcodes.DUP_X1,
Opcodes.DUP_X2,
Opcodes.DUP2,
Opcodes.DUP2_X1,
Opcodes.DUP2_X2,
Opcodes.SWAP,
Opcodes.IADD,
Opcodes.LADD,
Opcodes.FADD,
Opcodes.DADD,
Opcodes.ISUB,
Opcodes.LSUB,
Opcodes.FSUB,
Opcodes.DSUB,
Opcodes.IMUL,
Opcodes.LMUL,
Opcodes.FMUL,
Opcodes.DMUL,
Opcodes.IDIV,
Opcodes.LDIV,
Opcodes.FDIV,
Opcodes.DDIV,
Opcodes.IREM,
Opcodes.LREM,
Opcodes.FREM,
Opcodes.DREM,
Opcodes.INEG,
Opcodes.LNEG,
Opcodes.FNEG,
Opcodes.DNEG,
Opcodes.ISHL,
Opcodes.LSHL,
Opcodes.ISHR,
Opcodes.LSHR,
Opcodes.IUSHR,
Opcodes.LUSHR,
Opcodes.IAND,
Opcodes.LAND,
Opcodes.IOR,
Opcodes.LOR,
Opcodes.IXOR,
Opcodes.LXOR,
Opcodes.I2L,
Opcodes.I2F,
Opcodes.I2D,
Opcodes.L2I,
Opcodes.L2F,
Opcodes.L2D,
Opcodes.F2I,
Opcodes.F2L,
Opcodes.F2D,
Opcodes.D2I,
Opcodes.D2L,
Opcodes.D2F,
Opcodes.I2B,
Opcodes.I2C,
Opcodes.I2S,
Opcodes.LCMP,
Opcodes.FCMPL,
Opcodes.FCMPG,
Opcodes.DCMPL,
Opcodes.DCMPG,
Opcodes.IRETURN,
Opcodes.LRETURN,
Opcodes.FRETURN,
Opcodes.DRETURN,
Opcodes.ARETURN,
Opcodes.RETURN,
Opcodes.ARRAYLENGTH,
Opcodes.ATHROW,
Opcodes.MONITORENTER,
Opcodes.MONITOREXIT
),
"IntInsnNode" to intArrayOf(
Opcodes.BIPUSH,
Opcodes.SIPUSH,
Opcodes.NEWARRAY
),
"VarInsnNode" to intArrayOf(
Opcodes.ILOAD,
Opcodes.LLOAD,
Opcodes.FLOAD,
Opcodes.DLOAD,
Opcodes.ALOAD,
Opcodes.ISTORE,
Opcodes.LSTORE,
Opcodes.FSTORE,
Opcodes.DSTORE,
Opcodes.ASTORE,
Opcodes.RET
),
"TypeInsnNode" to intArrayOf(
Opcodes.NEW,
Opcodes.ANEWARRAY,
Opcodes.CHECKCAST,
Opcodes.INSTANCEOF
),
"FieldInsnNode" to intArrayOf(
Opcodes.GETSTATIC,
Opcodes.PUTSTATIC,
Opcodes.GETFIELD,
Opcodes.PUTFIELD
),
"MethodInsnNode" to intArrayOf(
Opcodes.INVOKEVIRTUAL,
Opcodes.INVOKESPECIAL,
Opcodes.INVOKESTATIC,
Opcodes.INVOKEINTERFACE
),
"InvokeDynamicInsnNode" to intArrayOf(
Opcodes.INVOKEDYNAMIC
),
"JumpInsnNode" to intArrayOf(
Opcodes.IFEQ,
Opcodes.IFNE,
Opcodes.IFLT,
Opcodes.IFGE,
Opcodes.IFGT,
Opcodes.IFLE,
Opcodes.IF_ICMPEQ,
Opcodes.IF_ICMPNE,
Opcodes.IF_ICMPLT,
Opcodes.IF_ICMPGE,
Opcodes.IF_ICMPGT,
Opcodes.IF_ICMPLE,
Opcodes.IF_ACMPEQ,
Opcodes.IF_ACMPNE,
Opcodes.GOTO,
Opcodes.JSR,
Opcodes.IFNULL,
Opcodes.IFNONNULL
),
"LdcInsnNode" to intArrayOf(
Opcodes.LDC
),
"IincInsnNode" to intArrayOf(
Opcodes.IINC
),
"TableSwitchInsnNode" to intArrayOf(
Opcodes.TABLESWITCH
),
"LookupSwitchInsnNode" to intArrayOf(
Opcodes.LOOKUPSWITCH
),
"MultiANewArrayInsnNode" to intArrayOf(
Opcodes.MULTIANEWARRAY
),
"ICONST" to intArrayOf(
Opcodes.ICONST_M1,
Opcodes.ICONST_0,
Opcodes.ICONST_1,
Opcodes.ICONST_2,
Opcodes.ICONST_3,
Opcodes.ICONST_4,
Opcodes.ICONST_5
),
"FCONST" to intArrayOf(
Opcodes.FCONST_0,
Opcodes.FCONST_1,
Opcodes.FCONST_2
),
"DCONST" to intArrayOf(
Opcodes.DCONST_0,
Opcodes.DCONST_1
)
)
private fun opcodeToCodepoint(opcode: Int): Char {
return (PRIVATE_USE_AREA + opcode).toChar()
}
private fun appendOpcodeRegex(pattern: StringBuilder, opcode: String) {
val i = Printer.OPCODES.indexOf(opcode)
if (i != -1) {
pattern.append(opcodeToCodepoint(i))
return
}
val group = OPCODE_GROUPS[opcode]
if (group != null) {
pattern.append('(')
group.map { opcodeToCodepoint(it) }.joinTo(pattern, "|")
pattern.append(')')
return
}
if (opcode == "AbstractInsnNode") {
pattern.append('.')
return
}
throw IllegalArgumentException("$opcode is not a valid opcode or opcode group")
}
@JvmStatic
fun compile(regex: String): InsnMatcher {
val pattern = StringBuilder()
val opcode = StringBuilder()
for (c in regex) {
if (c.isLetterOrDigit() || c == '_') {
opcode.append(c)
} else {
if (opcode.isNotEmpty()) {
appendOpcodeRegex(pattern, opcode.toString())
opcode.delete(0, opcode.length)
}
if (!c.isWhitespace()) {
pattern.append(c)
}
}
}
if (opcode.isNotEmpty()) {
appendOpcodeRegex(pattern, opcode.toString())
opcode.delete(0, opcode.length)
}
return InsnMatcher(Regex(pattern.toString()))
}
}
}
Loading…
Cancel
Save