From ccb37f120b0b26e7b4dc38745efdd84f52966ba4 Mon Sep 17 00:00:00 2001 From: Graham Date: Sat, 29 Feb 2020 22:23:11 +0000 Subject: [PATCH] Add INVOKESPECIAL transformer The obfuscator (or compiler, potentially?) converts INVOKEVIRTUAL instructions to INVOKESPECIAL in the following circumstances: * The owner of the method is the same as the class containing the INVOKE instruction. * The owner's ACC_FINAL flag is set. When those two conditions are met, and when ACC_SUPER is set on the owner (which is always true in the RuneScape client, and the flag is ignored in modern JVMs), then INVOKESPECIAL and INVOKEVIRTUAL are equivalent. This has not caused problems until now. However, the future static scrambling transformer will break the first condition in some cases, as it moves methods between classes. This transformer reverses the obfuscation, such that INVOKEVIRTUAL is used again. INVOKEVIRTUAL instructions may be moved between classes without complication. --- .../java/dev/openrs2/deob/Deobfuscator.kt | 2 + .../transform/InvokeSpecialTransformer.kt | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 deob/src/main/java/dev/openrs2/deob/transform/InvokeSpecialTransformer.kt diff --git a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt index 9e4a477ec3..a05492801a 100644 --- a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt +++ b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt @@ -19,6 +19,7 @@ import dev.openrs2.deob.transform.DummyLocalTransformer import dev.openrs2.deob.transform.ExceptionTracingTransformer import dev.openrs2.deob.transform.FieldOrderTransformer import dev.openrs2.deob.transform.FinalTransformer +import dev.openrs2.deob.transform.InvokeSpecialTransformer import dev.openrs2.deob.transform.OpaquePredicateTransformer import dev.openrs2.deob.transform.OriginalNameTransformer import dev.openrs2.deob.transform.OverrideTransformer @@ -164,6 +165,7 @@ class Deobfuscator(private val input: Path, private val output: Path) { ResetTransformer(), FinalTransformer(), ClassLiteralTransformer(), + InvokeSpecialTransformer(), VisibilityTransformer(), OverrideTransformer() ) diff --git a/deob/src/main/java/dev/openrs2/deob/transform/InvokeSpecialTransformer.kt b/deob/src/main/java/dev/openrs2/deob/transform/InvokeSpecialTransformer.kt new file mode 100644 index 0000000000..3d7bc15089 --- /dev/null +++ b/deob/src/main/java/dev/openrs2/deob/transform/InvokeSpecialTransformer.kt @@ -0,0 +1,48 @@ +package dev.openrs2.deob.transform + +import com.github.michaelbull.logging.InlineLogger +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.ClassNode +import org.objectweb.asm.tree.MethodInsnNode +import org.objectweb.asm.tree.MethodNode + +class InvokeSpecialTransformer : Transformer() { + private var invokeSpecialsReplaced = 0 + + override fun preTransform(classPath: ClassPath) { + invokeSpecialsReplaced = 0 + } + + override fun transformCode(classPath: ClassPath, library: Library, clazz: ClassNode, method: MethodNode): Boolean { + for (insn in method.instructions) { + if (insn !is MethodInsnNode || insn.opcode != Opcodes.INVOKESPECIAL) { + continue + } else if (insn.name == "") { + continue + } else if (insn.owner != clazz.name) { + continue + } + + val owner = classPath.getNode(insn.owner)!! + if ((owner.access and Opcodes.ACC_FINAL) == 0) { + continue + } + + insn.opcode = Opcodes.INVOKEVIRTUAL + invokeSpecialsReplaced++ + } + + return false + } + + override fun postTransform(classPath: ClassPath) { + logger.info { "Replaced $invokeSpecialsReplaced INVOKESPECIALs with INVOKEVIRTUAL" } + } + + companion object { + private val logger = InlineLogger() + } +}