From 223c451f2d0500e6780b6918f9d7973765ccd800 Mon Sep 17 00:00:00 2001 From: Graham Date: Fri, 12 Jan 2024 23:52:00 +0000 Subject: [PATCH] Add MultipleAssignmentTransformer This improves deobfuscation of a static field initializer in the 667 client that looks like: a = b = new X() This transformer translates it to two separate expressions: b = new X(); a = b This allows the StaticFieldUnscrambler to move both fields independently, rather than them both being forced to remain in their scrambled class. Signed-off-by: Graham --- .../bytecode/BytecodeDeobfuscatorModule.kt | 2 + .../MultipleAssignmentTransformer.kt | 57 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/transform/MultipleAssignmentTransformer.kt diff --git a/deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/BytecodeDeobfuscatorModule.kt b/deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/BytecodeDeobfuscatorModule.kt index 2dc39ce7..da0e4be8 100644 --- a/deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/BytecodeDeobfuscatorModule.kt +++ b/deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/BytecodeDeobfuscatorModule.kt @@ -22,6 +22,7 @@ import org.openrs2.deob.bytecode.transform.FinalMethodTransformer import org.openrs2.deob.bytecode.transform.InvokeSpecialTransformer import org.openrs2.deob.bytecode.transform.MethodOrderTransformer import org.openrs2.deob.bytecode.transform.MonitorTransformer +import org.openrs2.deob.bytecode.transform.MultipleAssignmentTransformer import org.openrs2.deob.bytecode.transform.OpaquePredicateTransformer import org.openrs2.deob.bytecode.transform.OriginalNameTransformer import org.openrs2.deob.bytecode.transform.OriginalPcRestoreTransformer @@ -53,6 +54,7 @@ public object BytecodeDeobfuscatorModule : AbstractModule() { binder.addBinding().to(OriginalNameTransformer::class.java) binder.addBinding().to(ClassLiteralTransformer::class.java) binder.addBinding().to(InvokeSpecialTransformer::class.java) + binder.addBinding().to(MultipleAssignmentTransformer::class.java) binder.addBinding().to(RemapTransformer::class.java) binder.addBinding().to(PatcherTransformer::class.java) binder.addBinding().to(ResourceTransformer::class.java) diff --git a/deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/transform/MultipleAssignmentTransformer.kt b/deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/transform/MultipleAssignmentTransformer.kt new file mode 100644 index 00000000..4091e76f --- /dev/null +++ b/deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/transform/MultipleAssignmentTransformer.kt @@ -0,0 +1,57 @@ +package org.openrs2.deob.bytecode.transform + +import com.github.michaelbull.logging.InlineLogger +import jakarta.inject.Singleton +import org.objectweb.asm.Opcodes +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.FieldInsnNode +import org.objectweb.asm.tree.MethodNode +import org.openrs2.asm.InsnMatcher +import org.openrs2.asm.classpath.ClassPath +import org.openrs2.asm.classpath.Library +import org.openrs2.asm.transform.Transformer +import org.openrs2.deob.bytecode.remap.StaticFieldUnscrambler + +/** + * A [Transformer] that splits multiple assignments to static fields in a + * single expression in `` methods. For example, `a = b = new X()` is + * translated to `b = new X(); a = b`. This allows [StaticFieldUnscrambler] to + * move the fields independently. + */ +@Singleton +public class MultipleAssignmentTransformer : Transformer() { + private var assignments = 0 + + override fun preTransform(classPath: ClassPath) { + assignments = 0 + } + + override fun transformCode(classPath: ClassPath, library: Library, clazz: ClassNode, method: MethodNode): Boolean { + if (method.name != "") { + return false + } + + for (match in MATCHER.match(method)) { + for (i in 0 until match.size - 1 step 2) { + val dup = match[i] + val putstatic = match[i + 1] as FieldInsnNode + + method.instructions.remove(dup) + method.instructions.insert(putstatic, FieldInsnNode(Opcodes.GETSTATIC, putstatic.owner, putstatic.name, putstatic.desc)) + + assignments++ + } + } + + return false + } + + override fun postTransform(classPath: ClassPath) { + logger.info { "Split $assignments multiple assignment expressions into separate expressions" } + } + + private companion object { + private val logger = InlineLogger() + private val MATCHER = InsnMatcher.compile("(DUP PUTSTATIC)+ PUTSTATIC") + } +}