From 0b3f24fed2a490dd6d706282aea74bc61d368b78 Mon Sep 17 00:00:00 2001 From: Graham Date: Sun, 15 Dec 2019 22:17:08 +0000 Subject: [PATCH] Convert InsnMatcher to Kotlin --- .../java/dev/openrs2/asm/InsnMatcher.java | 323 ------------------ .../main/java/dev/openrs2/asm/InsnMatcher.kt | 286 ++++++++++++++++ 2 files changed, 286 insertions(+), 323 deletions(-) delete mode 100644 asm/src/main/java/dev/openrs2/asm/InsnMatcher.java create mode 100644 asm/src/main/java/dev/openrs2/asm/InsnMatcher.kt diff --git a/asm/src/main/java/dev/openrs2/asm/InsnMatcher.java b/asm/src/main/java/dev/openrs2/asm/InsnMatcher.java deleted file mode 100644 index 5876133d..00000000 --- a/asm/src/main/java/dev/openrs2/asm/InsnMatcher.java +++ /dev/null @@ -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 OPCODE_GROUPS = ImmutableMap.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 createRealInsnList(InsnList list) { - List realInsns = new ArrayList<>(); - for (var insn : list) { - if (insn.getOpcode() != -1) { - realInsns.add(insn); - } - } - return realInsns; - } - - private static String createCodepointSeq(List 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> match(MethodNode method) { - return match(method.instructions); - } - - public Stream> match(InsnList list) { - Stream.Builder> 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(); - } -} diff --git a/asm/src/main/java/dev/openrs2/asm/InsnMatcher.kt b/asm/src/main/java/dev/openrs2/asm/InsnMatcher.kt new file mode 100644 index 00000000..9df865a6 --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/InsnMatcher.kt @@ -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 when the rest of the deobfuscator is ported to Kotlin + fun match(method: MethodNode): Stream> { + return match(method.instructions) + } + + fun match(list: InsnList): Stream> { + 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())) + } + } +}