forked from openrs2/openrs2
parent
e4fe120bf7
commit
0b3f24fed2
@ -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…
Reference in new issue