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.
pull/66/head
Graham 4 years ago
parent a82d2e3cef
commit ccb37f120b
  1. 2
      deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt
  2. 48
      deob/src/main/java/dev/openrs2/deob/transform/InvokeSpecialTransformer.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()
)

@ -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 == "<init>") {
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()
}
}
Loading…
Cancel
Save