Convert OpaquePredicateTransformer to Kotlin

pull/48/head
Graham 4 years ago
parent 1c2d2f1264
commit f56b288663
  1. 125
      deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.java
  2. 113
      deob/src/main/java/dev/openrs2/deob/transform/OpaquePredicateTransformer.kt

@ -1,125 +0,0 @@
package dev.openrs2.deob.transform;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import dev.openrs2.asm.InsnMatcher;
import dev.openrs2.asm.MemberRef;
import dev.openrs2.asm.MethodNodeUtilsKt;
import dev.openrs2.asm.classpath.ClassPath;
import dev.openrs2.asm.classpath.Library;
import dev.openrs2.asm.transform.Transformer;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class OpaquePredicateTransformer extends Transformer {
private static final Logger logger = LoggerFactory.getLogger(OpaquePredicateTransformer.class);
private static final InsnMatcher FLOW_OBSTRUCTOR_INITIALIZER_MATCHER = InsnMatcher.compile("(GETSTATIC | ILOAD) IFEQ (((GETSTATIC ISTORE)? IINC ILOAD) | ((GETSTATIC | ILOAD) IFEQ ICONST GOTO ICONST)) PUTSTATIC");
private static final InsnMatcher OPAQUE_PREDICATE_MATCHER = InsnMatcher.compile("(GETSTATIC | ILOAD) (IFEQ | IFNE)");
private static final InsnMatcher STORE_MATCHER = InsnMatcher.compile("GETSTATIC ISTORE");
private final Set<MemberRef> flowObstructors = new HashSet<>();
private int opaquePredicates, stores;
@Override
public void preTransform(ClassPath classPath) {
flowObstructors.clear();
opaquePredicates = 0;
stores = 0;
for (var library : classPath.getLibraries()) {
for (var clazz : library) {
for (var method : clazz.methods) {
if (MethodNodeUtilsKt.hasCode(method)) {
findFlowObstructors(library, method);
}
}
}
}
logger.info("Identified flow obstructors {}", flowObstructors);
}
private void findFlowObstructors(Library library, MethodNode method) {
FLOW_OBSTRUCTOR_INITIALIZER_MATCHER.match(method).forEach(match -> {
/* add flow obstructor to set */
var putstatic = (FieldInsnNode) match.get(match.size() - 1);
flowObstructors.add(new MemberRef(putstatic));
/* remove initializer */
match.forEach(method.instructions::remove);
/* remove field */
var owner = library.get(putstatic.owner);
owner.fields.removeIf(field -> field.name.equals(putstatic.name) && field.desc.equals(putstatic.desc));
});
}
private boolean isFlowObstructor(FieldInsnNode insn) {
return flowObstructors.contains(new MemberRef(insn));
}
private boolean isOpaquePredicate(MethodNode method, List<AbstractInsnNode> match) {
var load = match.get(0);
/* flow obstructor loaded directly? */
if (load.getOpcode() == Opcodes.GETSTATIC) {
var getstatic = (FieldInsnNode) load;
return isFlowObstructor(getstatic);
}
/* flow obstructor loaded via local variable? */
var iload = (VarInsnNode) load;
return STORE_MATCHER.match(method).anyMatch(storeMatch -> {
var getstatic = (FieldInsnNode) storeMatch.get(0);
var istore = (VarInsnNode) storeMatch.get(1);
return isFlowObstructor(getstatic) && iload.var == istore.var;
});
}
private boolean isRedundantStore(List<AbstractInsnNode> match) {
var getstatic = (FieldInsnNode) match.get(0);
return isFlowObstructor(getstatic);
}
@Override
public boolean transformCode(ClassPath classPath, Library library, ClassNode clazz, MethodNode method) {
/* find and fix opaque predicates */
OPAQUE_PREDICATE_MATCHER.match(method).filter(match -> isOpaquePredicate(method, match)).forEach(match -> {
var branch = (JumpInsnNode) match.get(1);
if (branch.getOpcode() == Opcodes.IFEQ) {
/* branch is always taken */
method.instructions.remove(match.get(0));
branch.setOpcode(Opcodes.GOTO);
} else { /* IFNE */
/* branch is never taken */
match.forEach(method.instructions::remove);
}
opaquePredicates++;
});
/* remove redundant stores */
STORE_MATCHER.match(method).filter(this::isRedundantStore).forEach(match -> {
match.forEach(method.instructions::remove);
stores++;
});
return false;
}
@Override
public void postTransform(ClassPath classPath) {
logger.info("Removed {} opaque predicates and {} redundant stores", opaquePredicates, stores);
}
}

@ -0,0 +1,113 @@
package dev.openrs2.deob.transform
import com.github.michaelbull.logging.InlineLogger
import dev.openrs2.asm.InsnMatcher
import dev.openrs2.asm.MemberRef
import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library
import dev.openrs2.asm.hasCode
import dev.openrs2.asm.transform.Transformer
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.*
class OpaquePredicateTransformer : Transformer() {
private val flowObstructors = mutableSetOf<MemberRef>()
private var opaquePredicates = 0
private var stores = 0
override fun preTransform(classPath: ClassPath) {
flowObstructors.clear()
opaquePredicates = 0
stores = 0
for (library in classPath.libraries) {
for (clazz in library) {
for (method in clazz.methods) {
if (method.hasCode()) {
findFlowObstructors(library, method)
}
}
}
}
logger.info { "Identified flow obstructors $flowObstructors" }
}
private fun findFlowObstructors(library: Library, method: MethodNode) {
FLOW_OBSTRUCTOR_INITIALIZER_MATCHER.match(method).forEach {
// add flow obstructor to set
val putstatic = it.last() as FieldInsnNode
flowObstructors.add(MemberRef(putstatic))
// remove initializer
it.forEach(method.instructions::remove)
// remove field
val owner = library[putstatic.owner]!!
owner.fields.removeIf { it.name == putstatic.name && it.desc == putstatic.desc }
}
}
private fun isFlowObstructor(insn: FieldInsnNode): Boolean {
return flowObstructors.contains(MemberRef(insn))
}
private fun isOpaquePredicate(method: MethodNode, match: List<AbstractInsnNode>): Boolean {
val load = match[0]
// flow obstructor loaded directly?
if (load is FieldInsnNode && load.opcode == Opcodes.GETSTATIC) {
return isFlowObstructor(load)
}
// flow obstructor loaded via local variable
val iload = load as VarInsnNode
return STORE_MATCHER.match(method).anyMatch {
val getstatic = it[0] as FieldInsnNode
val istore = it[1] as VarInsnNode
return@anyMatch isFlowObstructor(getstatic) && iload.`var` == istore.`var`
}
}
private fun isRedundantStore(match: List<AbstractInsnNode>): Boolean {
val getstatic = match[0] as FieldInsnNode
return isFlowObstructor(getstatic)
}
override fun transformCode(classPath: ClassPath, library: Library, clazz: ClassNode, method: MethodNode): Boolean {
// find and fix opaque predicates
OPAQUE_PREDICATE_MATCHER.match(method).filter { isOpaquePredicate(method, it) }.forEach {
val branch = it[1] as JumpInsnNode
if (branch.opcode == Opcodes.IFEQ) {
// branch is always taken
method.instructions.remove(it[0])
branch.opcode = Opcodes.GOTO
} else { // IFNE
// branch is never taken
it.forEach(method.instructions::remove)
}
opaquePredicates++
}
// remove redundant stores
STORE_MATCHER.match(method).filter(this::isRedundantStore).forEach {
it.forEach(method.instructions::remove)
stores++
}
return false
}
override fun postTransform(classPath: ClassPath) {
logger.info { "Removed $opaquePredicates opaque predicates and $stores redundant stores" }
}
companion object {
private val logger = InlineLogger()
private val FLOW_OBSTRUCTOR_INITIALIZER_MATCHER =
InsnMatcher.compile("(GETSTATIC | ILOAD) IFEQ (((GETSTATIC ISTORE)? IINC ILOAD) | ((GETSTATIC | ILOAD) IFEQ ICONST GOTO ICONST)) PUTSTATIC")
private val OPAQUE_PREDICATE_MATCHER = InsnMatcher.compile("(GETSTATIC | ILOAD) (IFEQ | IFNE)")
private val STORE_MATCHER = InsnMatcher.compile("GETSTATIC ISTORE")
}
}
Loading…
Cancel
Save