OpenRS2 Deobfuscator
Views
Actions
Namespaces
Variants
Tools
Install Java 11 & kotlinc
git clone https://git.openrs2.org/openrs2/openrs2.git
2004 - 2006 RS2 Client
This is by no means a comprehensive guide, just some steps I took when deobfuscating 225. Some transformers had to be disabled to get it working properly, expect to play around with the transformers for any other version. The deobfuscator code was designed for 2009-2010 RS2 and has had some minor work to partially support 2004 (specifically 225).
NOTE: If any mistakes are made the nonfree folder needs to be cleared out before re-running the build. You can leave lib alone. Check for share/deob/map/client.yaml if you re-run gradle build after deob, it will process whatever the last output was.
1. Create nonfree/lib/
directory, place obfuscated runescape.jar
in nonfree/lib/
.
2. Delete profile mappings from share/deob/map/
.
3. Add signlink entries to share/deob/profile.yaml
. See below.
4. Edit deob-bytecode
's BytecodeDeobfuscator.kt
. Change client to load runescape.jar
using JarLibraryReader
, remove gl
, loader
, unpack
, signlink
, and reduce remapping code to only the client remapper. See below.
5. Remove everything other than client from deob-util
's Module.kt
. See below.
6. Remove GlRegistry
, GlRegistryProvider
, and GlTransformer
from deob-ast
's AstDeobfuscatorModule.kt
(expect some linting errors to clean up from unused imports). See below.
7. (Recommended) Remove PatcherTransformer
and all Unused_____Transformer
s from deob-bytecode
's BytecodeDeobfuscatorModule.kt
. See below.
8. Build and run:
gradlew build
java -jar all/build/libs/openrs2.jar deob
You should have some new java files in nonfree/client/src/main/java
now. Compile them with deob-annotations in your javac classpath.
I've encountered one minor error when compiling, either find the problematic transformer or just edit the code to address it. For this specific one I just removed the referenced line:
Static15.java:1452: error: unreachable statement
return;
profile.yaml
---
excluded_classes:
- "*!client"
- "*!sign/signlink"
excluded_methods:
- "*!**.<clinit> *"
- "*!**.<init> *"
- "*!**.main *"
- "*!**.quit *"
- "*!sign/signlink.* *"
excluded_fields:
- "*!sign/signlink.* *"
entry_points:
- "*!**.<clinit> *"
- "*!**.main *"
- "*!client.<init> *"
- "*!sign/signlink.* *"
scrambled_libraries:
- client
max_obfuscated_name_len: 2
BytecodeDeobfuscator.kt
Removed: all but client processing code
package org.openrs2.deob.bytecode
import com.github.michaelbull.logging.InlineLogger
import org.openrs2.asm.classpath.ClassPath
import org.openrs2.asm.classpath.Library
import org.openrs2.asm.io.JarLibraryReader
import org.openrs2.asm.io.JarLibraryWriter
import org.openrs2.asm.transform.Transformer
import org.openrs2.deob.bytecode.remap.ClassNamePrefixRemapper
import org.openrs2.deob.bytecode.remap.StripClassNamePrefixRemapper
import java.nio.file.Files
import java.nio.file.Path
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
public class BytecodeDeobfuscator @Inject constructor(
@DeobfuscatorQualifier private val transformers: Set<Transformer>
) {
public fun run(input: Path, output: Path) {
// read input jars/packs
logger.info { "Reading input jars" }
val client = Library.read("client", input.resolve("runescape.jar"), JarLibraryReader)
val clientRemapper = ClassNamePrefixRemapper(client)
client.remap(clientRemapper)
// bundle libraries together into a common classpath
val runtime = ClassLoader.getPlatformClassLoader()
val classPath = ClassPath(
runtime,
dependencies = emptyList(),
libraries = listOf(client)
)
// deobfuscate
logger.info { "Transforming client" }
for (transformer in transformers) {
logger.info { "Running transformer ${transformer.javaClass.simpleName}" }
transformer.transform(classPath)
}
// strip class name prefixes
client.remap(StripClassNamePrefixRemapper)
// write output jars
logger.info { "Writing output jars" }
Files.createDirectories(output)
client.write(output.resolve("client.jar"), JarLibraryWriter, classPath)
}
private companion object {
private val logger = InlineLogger()
}
}
Module.kt
Removed: all but client
package org.openrs2.deob.util
import java.nio.file.Path
public class Module(public val name: String, public val dependencies: Set<Module> = emptySet()) {
public val jar: Path = Path.of("nonfree/var/cache/deob").resolve("$name.jar")
public val sources: Path = Path.of("nonfree").resolve(name).resolve("src/main/java")
public val transitiveDependencies: Set<Module> =
dependencies.plus(dependencies.flatMap { it.transitiveDependencies })
public companion object {
private val CLIENT = Module("client")
public val ALL: Set<Module> = setOf(CLIENT)
}
}
AstDeobfuscatorModule.kt
Removed: GlTransformer
, GlRegistry
, GlRegistryProvider
Add/remove transformers as necessary as different versions may have conflicts.
package org.openrs2.deob.ast
import com.google.inject.AbstractModule
import com.google.inject.multibindings.Multibinder
import org.openrs2.deob.ast.transform.AddSubTransformer
import org.openrs2.deob.ast.transform.BinaryExprOrderTransformer
import org.openrs2.deob.ast.transform.BitMaskTransformer
import org.openrs2.deob.ast.transform.CharLiteralTransformer
import org.openrs2.deob.ast.transform.ComplementTransformer
import org.openrs2.deob.ast.transform.EncloseTransformer
import org.openrs2.deob.ast.transform.ForLoopConditionTransformer
import org.openrs2.deob.ast.transform.HexLiteralTransformer
import org.openrs2.deob.ast.transform.IfElseTransformer
import org.openrs2.deob.ast.transform.IncrementTransformer
import org.openrs2.deob.ast.transform.NegativeLiteralTransformer
import org.openrs2.deob.ast.transform.NewInstanceTransformer
import org.openrs2.deob.ast.transform.NotTransformer
import org.openrs2.deob.ast.transform.RedundantCastTransformer
import org.openrs2.deob.ast.transform.TernaryTransformer
import org.openrs2.deob.ast.transform.Transformer
import org.openrs2.deob.ast.transform.UnencloseTransformer
import org.openrs2.deob.ast.transform.ValueOfTransformer
public object AstDeobfuscatorModule : AbstractModule() {
override fun configure() {
// bind(GlRegistry::class.java)
// .toProvider(GlRegistryProvider::class.java)
// .`in`(Scopes.SINGLETON)
val binder = Multibinder.newSetBinder(binder(), Transformer::class.java)
binder.addBinding().to(UnencloseTransformer::class.java)
binder.addBinding().to(NegativeLiteralTransformer::class.java)
binder.addBinding().to(ComplementTransformer::class.java)
binder.addBinding().to(NotTransformer::class.java)
binder.addBinding().to(CharLiteralTransformer::class.java)
binder.addBinding().to(IfElseTransformer::class.java)
binder.addBinding().to(TernaryTransformer::class.java)
binder.addBinding().to(BinaryExprOrderTransformer::class.java)
binder.addBinding().to(AddSubTransformer::class.java)
// binder.addBinding().to(IdentityTransformer::class.java)
binder.addBinding().to(BitMaskTransformer::class.java)
binder.addBinding().to(HexLiteralTransformer::class.java)
binder.addBinding().to(ValueOfTransformer::class.java)
binder.addBinding().to(NewInstanceTransformer::class.java)
binder.addBinding().to(IncrementTransformer::class.java)
binder.addBinding().to(ForLoopConditionTransformer::class.java)
binder.addBinding().to(RedundantCastTransformer::class.java)
// binder.addBinding().to(GlTransformer::class.java)
binder.addBinding().to(EncloseTransformer::class.java)
}
}
BytecodeDeobfuscatorModule.kt
Removed: UnusedLocalTransformer
, UnusedMethodTransformer
, UnusedArgTransformer
, ConstantArgTransformer
, PatcherTransformer
The ones I've outlined aren't necessary, I've just had the Unused* transformers and ConstantArg transformer remove code I wanted to keep. Patcher transformer is a good one, but has some changes specific to OpenRS2 / 2009 RS so you can be more selective in that module file.
Some client versions may require commenting out FinalFieldTransformer, possibly due to a deobfuscation edge case?
package org.openrs2.deob.bytecode
import com.google.inject.AbstractModule
import com.google.inject.Scopes
import com.google.inject.multibindings.Multibinder
import org.openrs2.asm.transform.Transformer
import org.openrs2.deob.bytecode.transform.BitShiftTransformer
import org.openrs2.deob.bytecode.transform.BitwiseOpTransformer
import org.openrs2.deob.bytecode.transform.CanvasTransformer
import org.openrs2.deob.bytecode.transform.ClassLiteralTransformer
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.ExceptionTracingTransformer
import org.openrs2.deob.bytecode.transform.FernflowerExceptionTransformer
import org.openrs2.deob.bytecode.transform.FieldOrderTransformer
import org.openrs2.deob.bytecode.transform.FinalClassTransformer
import org.openrs2.deob.bytecode.transform.FinalFieldTransformer
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.OpaquePredicateTransformer
import org.openrs2.deob.bytecode.transform.OriginalNameTransformer
import org.openrs2.deob.bytecode.transform.OriginalPcRestoreTransformer
import org.openrs2.deob.bytecode.transform.OriginalPcSaveTransformer
import org.openrs2.deob.bytecode.transform.OverrideTransformer
import org.openrs2.deob.bytecode.transform.RedundantGotoTransformer
import org.openrs2.deob.bytecode.transform.RemapTransformer
import org.openrs2.deob.bytecode.transform.ResetTransformer
import org.openrs2.deob.bytecode.transform.VisibilityTransformer
import org.openrs2.deob.util.DeobfuscatorUtilModule
import org.openrs2.patcher.PatcherModule
import org.openrs2.patcher.transform.ResourceTransformer
public object BytecodeDeobfuscatorModule : AbstractModule() {
override fun configure() {
install(PatcherModule)
install(DeobfuscatorUtilModule)
bind(Profile::class.java)
.toProvider(ProfileProvider::class.java)
.`in`(Scopes.SINGLETON)
val binder = Multibinder.newSetBinder(binder(), Transformer::class.java, DeobfuscatorQualifier::class.java)
binder.addBinding().to(OriginalPcSaveTransformer::class.java)
binder.addBinding().to(OriginalNameTransformer::class.java)
binder.addBinding().to(ClassLiteralTransformer::class.java)
binder.addBinding().to(InvokeSpecialTransformer::class.java)
binder.addBinding().to(RemapTransformer::class.java)
// binder.addBinding().to(PatcherTransformer::class.java)
binder.addBinding().to(ResourceTransformer::class.java)
binder.addBinding().to(OpaquePredicateTransformer::class.java)
binder.addBinding().to(ExceptionTracingTransformer::class.java)
binder.addBinding().to(MonitorTransformer::class.java)
binder.addBinding().to(BitShiftTransformer::class.java)
binder.addBinding().to(CanvasTransformer::class.java)
binder.addBinding().to(FieldOrderTransformer::class.java)
binder.addBinding().to(BitwiseOpTransformer::class.java)
//binder.addBinding().to(ConstantArgTransformer::class.java)
binder.addBinding().to(CopyPropagationTransformer::class.java)
// binder.addBinding().to(UnusedLocalTransformer::class.java)
// binder.addBinding().to(UnusedMethodTransformer::class.java)
// binder.addBinding().to(UnusedArgTransformer::class.java)
binder.addBinding().to(CounterTransformer::class.java)
binder.addBinding().to(ResetTransformer::class.java)
binder.addBinding().to(EmptyClassTransformer::class.java)
binder.addBinding().to(MethodOrderTransformer::class.java)
binder.addBinding().to(VisibilityTransformer::class.java)
binder.addBinding().to(FinalClassTransformer::class.java)
binder.addBinding().to(FinalMethodTransformer::class.java)
binder.addBinding().to(FinalFieldTransformer::class.java)
binder.addBinding().to(OverrideTransformer::class.java)
binder.addBinding().to(RedundantGotoTransformer::class.java)
binder.addBinding().to(OriginalPcRestoreTransformer::class.java)
binder.addBinding().to(FernflowerExceptionTransformer::class.java)
}
}