diff --git a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.java b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.java index 8c878077..103b89ad 100644 --- a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.java +++ b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.java @@ -12,6 +12,7 @@ import dev.openrs2.asm.transform.Transformer; import dev.openrs2.deob.remap.ClassNamePrefixer; import dev.openrs2.deob.remap.TypedRemapper; import dev.openrs2.deob.transform.BitShiftTransformer; +import dev.openrs2.deob.transform.BitwiseOpTransformer; import dev.openrs2.deob.transform.CanvasTransformer; import dev.openrs2.deob.transform.ClassForNameTransformer; import dev.openrs2.deob.transform.CounterTransformer; @@ -34,6 +35,7 @@ public final class Deobfuscator { new CounterTransformer(), new CanvasTransformer(), new FieldOrderTransformer(), + new BitwiseOpTransformer(), new DummyTransformer() ); diff --git a/deob/src/main/java/dev/openrs2/deob/transform/BitwiseOpTransformer.java b/deob/src/main/java/dev/openrs2/deob/transform/BitwiseOpTransformer.java new file mode 100644 index 00000000..0738b177 --- /dev/null +++ b/deob/src/main/java/dev/openrs2/deob/transform/BitwiseOpTransformer.java @@ -0,0 +1,97 @@ +package dev.openrs2.deob.transform; + +import java.util.HashMap; +import java.util.Map; + +import dev.openrs2.asm.InsnMatcher; +import dev.openrs2.asm.MemberRef; +import dev.openrs2.asm.classpath.ClassPath; +import dev.openrs2.asm.transform.Transformer; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.InsnNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.VarInsnNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class BitwiseOpTransformer extends Transformer { + private static final Logger logger = LoggerFactory.getLogger(BitwiseOpTransformer.class); + + private static final InsnMatcher BITWISE_OP_MATCHER = InsnMatcher.compile("^ILOAD ILOAD (IXOR | IAND | IOR) IRETURN$"); + private static final String BITWISE_OP_DESC = "(II)I"; + + private final Map methodOps = new HashMap<>(); + private int inlinedOps; + + @Override + protected void preTransform(ClassPath classPath) { + methodOps.clear(); + inlinedOps = 0; + + for (var library : classPath.getLibraries()) { + for (var clazz : library) { + for (var method : clazz.methods) { + if ((method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) != 0) { + continue; + } + + if ((method.access & Opcodes.ACC_STATIC) == 0) { + continue; + } + + if (!method.desc.equals(BITWISE_OP_DESC)) { + continue; + } + + BITWISE_OP_MATCHER.match(method).findAny().ifPresent(match -> { + var iload0 = (VarInsnNode) match.get(0); + if (iload0.var != 0) { + return; + } + + var iload1 = (VarInsnNode) match.get(1); + if (iload1.var != 1) { + return; + } + + var methodRef = new MemberRef(clazz.name, method.name, method.desc); + methodOps.put(methodRef, match.get(2).getOpcode()); + }); + } + } + } + } + + @Override + protected boolean transformClass(ClassNode clazz) { + clazz.methods.removeIf(m -> methodOps.containsKey(new MemberRef(clazz.name, m.name, m.desc))); + return false; + } + + @Override + protected boolean transformCode(ClassNode clazz, MethodNode method) { + for (var it = method.instructions.iterator(); it.hasNext(); ) { + var insn = it.next(); + if (insn.getOpcode() != Opcodes.INVOKESTATIC) { + continue; + } + + var invokestatic = (MethodInsnNode) insn; + var methodRef = new MemberRef(invokestatic.owner, invokestatic.name, invokestatic.desc); + var opcode = methodOps.get(methodRef); + if (opcode != null) { + it.set(new InsnNode(opcode)); + inlinedOps++; + } + } + + return false; + } + + @Override + protected void postTransform(ClassPath classPath) { + logger.info("Inlined {} bitwise ops and removed {} redundant methods", inlinedOps, methodOps.size()); + } +}