Separate ZKM and Jagex tracing exception transformers

This is a slightly nicer solution than running a combined transformer
until it reaches a fixed point: we know we'd only need two passes,
rather than an arbitrary number of passes.

The 667 client uses various exception types beyond RuntimeException in
ZKM exception obfuscation handlers, including Throwable, Exception,
EOFException and IOException. This separation also allows us to remove
the type constraint for ZKM handlers.

Signed-off-by: Graham <gpe@openrs2.org>
Graham 10 months ago
parent 514799920e
commit 9e001e32e1
  1. 2
      deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/BytecodeDeobfuscatorModule.kt
  2. 54
      deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/transform/ExceptionObfuscationTransformer.kt
  3. 28
      deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/transform/ExceptionTracingTransformer.kt

@ -12,6 +12,7 @@ import org.openrs2.deob.bytecode.transform.ConstantArgTransformer
import org.openrs2.deob.bytecode.transform.CopyPropagationTransformer
import org.openrs2.deob.bytecode.transform.CounterTransformer
import org.openrs2.deob.bytecode.transform.EmptyClassTransformer
import org.openrs2.deob.bytecode.transform.ExceptionObfuscationTransformer
import org.openrs2.deob.bytecode.transform.ExceptionTracingTransformer
import org.openrs2.deob.bytecode.transform.FernflowerExceptionTransformer
import org.openrs2.deob.bytecode.transform.FieldOrderTransformer
@ -56,6 +57,7 @@ public object BytecodeDeobfuscatorModule : AbstractModule() {
binder.addBinding().to(PatcherTransformer::class.java)
binder.addBinding().to(ResourceTransformer::class.java)
binder.addBinding().to(OpaquePredicateTransformer::class.java)
binder.addBinding().to(ExceptionObfuscationTransformer::class.java)
binder.addBinding().to(ExceptionTracingTransformer::class.java)
binder.addBinding().to(MonitorTransformer::class.java)
binder.addBinding().to(BitShiftTransformer::class.java)

@ -0,0 +1,54 @@
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.MethodNode
import org.openrs2.asm.classpath.ClassPath
import org.openrs2.asm.classpath.Library
import org.openrs2.asm.nextReal
import org.openrs2.asm.transform.Transformer
/**
* A [Transformer] responsible for removing [ZKM](http://www.zelix.com/klassmaster/)'s
* [exception obfuscation](https://www.zelix.com/klassmaster/featuresExceptionObfuscation.html),
* which inserts exception handlers that catch any type of exception and
* immediately re-throw them. The exception handlers are inserted in locations
* where there is no Java source code equivalent, confusing decompilers.
*/
@Singleton
public class ExceptionObfuscationTransformer : Transformer() {
private var handlers = 0
override fun preTransform(classPath: ClassPath) {
handlers = 0
}
override fun transformCode(classPath: ClassPath, library: Library, clazz: ClassNode, method: MethodNode): Boolean {
for (insn in method.instructions) {
if (insn.opcode != Opcodes.ATHROW) {
continue
}
val foundTryCatch = method.tryCatchBlocks.removeIf { tryCatch ->
tryCatch.handler.nextReal === insn
}
if (foundTryCatch) {
method.instructions.remove(insn)
handlers++
}
}
return false
}
override fun postTransform(classPath: ClassPath) {
logger.info { "Removed $handlers exception obfuscation handlers" }
}
private companion object {
private val logger = InlineLogger()
}
}

@ -11,22 +11,13 @@ import org.openrs2.asm.nextReal
import org.openrs2.asm.transform.Transformer
/**
* A [Transformer] responsible for removing two kinds of redundant exception
* handler.
*
* - [ZKM](http://www.zelix.com/klassmaster/)'s
* [exception obfuscation](https://www.zelix.com/klassmaster/featuresExceptionObfuscation.html),
* which inserts exception handlers that catch [RuntimeException]s and
* immediately re-throw them. The exception handlers are inserted in
* locations where there is no Java source code equivalent, confusing
* decompilers.
*
* - Jagex inserts a try/catch block around every method that catches
* [RuntimeException]s, wraps them with a custom [RuntimeException]
* implementation and re-throws them. The wrapped exception's message
* contains the values of the method's arguments. While this is for debugging
* and not obfuscation, it is clearly automatically-generated and thus we
* remove these exception handlers too.
* A [Transformer] responsible for removing Jagex's exception tracing. Jagex
* inserts a try/catch block around every method that catches
* [RuntimeException]s, wraps them with a custom [RuntimeException]
* implementation and re-throws them. The wrapped exception's message contains
* the values of the method's arguments. While this is for debugging and not
* obfuscation, it is clearly automatically-generated and thus we remove these
* exception handlers too.
*/
@Singleton
public class ExceptionTracingTransformer : Transformer() {
@ -42,8 +33,6 @@ public class ExceptionTracingTransformer : Transformer() {
clazz: ClassNode,
method: MethodNode
): Boolean {
var changed = false
for (match in CATCH_MATCHER.match(method)) {
val foundTryCatch = method.tryCatchBlocks.removeIf { tryCatch ->
tryCatch.type == "java/lang/RuntimeException" && tryCatch.handler.nextReal === match[0]
@ -52,11 +41,10 @@ public class ExceptionTracingTransformer : Transformer() {
if (foundTryCatch) {
match.forEach(method.instructions::remove)
tracingTryCatches++
changed = true
}
}
return changed
return false
}
override fun postTransform(classPath: ClassPath) {

Loading…
Cancel
Save