|
|
|
package dev.openrs2.deob;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.nio.file.Files;
|
|
|
|
import java.nio.file.Path;
|
|
|
|
import java.nio.file.Paths;
|
|
|
|
|
|
|
|
import com.google.common.collect.ImmutableList;
|
|
|
|
import dev.openrs2.asm.classpath.ClassPath;
|
|
|
|
import dev.openrs2.asm.classpath.Library;
|
|
|
|
import dev.openrs2.asm.transform.Transformer;
|
|
|
|
import dev.openrs2.bundler.Bundler;
|
|
|
|
import dev.openrs2.deob.remap.PrefixRemapper;
|
|
|
|
import dev.openrs2.deob.transform.AccessTransformer;
|
|
|
|
import dev.openrs2.deob.transform.BitShiftTransformer;
|
|
|
|
import dev.openrs2.deob.transform.BitwiseOpTransformer;
|
|
|
|
import dev.openrs2.deob.transform.CanvasTransformer;
|
|
|
|
import dev.openrs2.deob.transform.CounterTransformer;
|
|
|
|
import dev.openrs2.deob.transform.DummyArgTransformer;
|
|
|
|
import dev.openrs2.deob.transform.DummyLocalTransformer;
|
|
|
|
import dev.openrs2.deob.transform.ExceptionTracingTransformer;
|
|
|
|
import dev.openrs2.deob.transform.FieldOrderTransformer;
|
|
|
|
import dev.openrs2.deob.transform.OpaquePredicateTransformer;
|
|
|
|
import dev.openrs2.deob.transform.OriginalNameTransformer;
|
|
|
|
import dev.openrs2.deob.transform.RemapTransformer;
|
|
|
|
import dev.openrs2.deob.transform.ResetTransformer;
|
|
|
|
import dev.openrs2.deob.transform.UnusedArgTransformer;
|
|
|
|
import org.objectweb.asm.tree.analysis.AnalyzerException;
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
public final class Deobfuscator {
|
|
|
|
private static final Logger logger = LoggerFactory.getLogger(Deobfuscator.class);
|
|
|
|
|
|
|
|
private static final ImmutableList<Transformer> TRANSFORMERS = ImmutableList.<Transformer>builder()
|
|
|
|
.add(new OriginalNameTransformer())
|
|
|
|
.addAll(Bundler.TRANSFORMERS)
|
|
|
|
.add(new OpaquePredicateTransformer())
|
|
|
|
.add(new ExceptionTracingTransformer())
|
|
|
|
.add(new BitShiftTransformer())
|
|
|
|
.add(new CounterTransformer())
|
|
|
|
.add(new CanvasTransformer())
|
|
|
|
.add(new FieldOrderTransformer())
|
|
|
|
.add(new BitwiseOpTransformer())
|
|
|
|
.add(new RemapTransformer())
|
|
|
|
.add(new DummyArgTransformer())
|
|
|
|
.add(new DummyLocalTransformer())
|
|
|
|
.add(new UnusedArgTransformer())
|
|
|
|
.add(new ResetTransformer())
|
|
|
|
.add(new AccessTransformer())
|
|
|
|
.build();
|
|
|
|
|
|
|
|
public static void main(String[] args) throws IOException, AnalyzerException {
|
|
|
|
var deobfuscator = new Deobfuscator(Paths.get("nonfree/code"), Paths.get("nonfree/code/deob"));
|
|
|
|
deobfuscator.run();
|
|
|
|
}
|
|
|
|
|
|
|
|
private final Path input, output;
|
|
|
|
|
|
|
|
public Deobfuscator(Path input, Path output) {
|
|
|
|
this.input = input;
|
|
|
|
this.output = output;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void run() throws IOException, AnalyzerException {
|
|
|
|
/* read input jars/packs */
|
|
|
|
logger.info("Reading input jars");
|
|
|
|
var unpacker = Library.readJar(input.resolve("game_unpacker.dat"));
|
|
|
|
var glUnpacker = new Library(unpacker);
|
|
|
|
var loader = Library.readJar(input.resolve("loader.jar"));
|
|
|
|
var glLoader = Library.readJar(input.resolve("loader_gl.jar"));
|
|
|
|
var gl = Library.readPack(input.resolve("jaggl.pack200"));
|
|
|
|
var client = Library.readJar(input.resolve("runescape.jar"));
|
|
|
|
var glClient = Library.readPack(input.resolve("runescape_gl.pack200"));
|
|
|
|
|
|
|
|
// TODO(gpe): it'd be nice to have separate signlink.jar and
|
|
|
|
// signlink-unsigned.jar files so we don't (effectively) deobfuscate
|
|
|
|
// runescape.jar twice with different sets of names, but thinking about
|
|
|
|
// how this would work is tricky (as the naming must match)
|
|
|
|
var unsignedClient = new Library(client);
|
|
|
|
|
|
|
|
/* overwrite client's classes with signed classes from the loader */
|
|
|
|
logger.info("Moving signed classes from loader");
|
|
|
|
var signLink = new Library();
|
|
|
|
SignedClassUtils.move(loader, client, signLink);
|
|
|
|
|
|
|
|
logger.info("Moving signed classes from loader_gl");
|
|
|
|
var glSignLink = new Library();
|
|
|
|
SignedClassUtils.move(glLoader, glClient, glSignLink);
|
|
|
|
|
|
|
|
/* move unpack class out of the loader (so the unpacker and loader can both depend on it) */
|
|
|
|
logger.info("Moving unpack from loader to unpack");
|
|
|
|
var unpack = new Library();
|
|
|
|
unpack.add(loader.remove("unpack"));
|
|
|
|
|
|
|
|
logger.info("Moving unpack from loader_gl to unpack_gl");
|
|
|
|
var glUnpack = new Library();
|
|
|
|
glUnpack.add(glLoader.remove("unpack"));
|
|
|
|
|
|
|
|
/* move DRI classes out of jaggl (so we can place javah-generated headers in a separate directory) */
|
|
|
|
logger.info("Moving DRI classes from jaggl to jaggl_dri");
|
|
|
|
var glDri = new Library();
|
|
|
|
glDri.add(gl.remove("com/sun/opengl/impl/x11/DRIHack"));
|
|
|
|
glDri.add(gl.remove("com/sun/opengl/impl/x11/DRIHack$1"));
|
|
|
|
glDri.add(gl.remove("jaggl/X11/dri"));
|
|
|
|
|
|
|
|
/* prefix remaining loader/unpacker classes (to avoid conflicts when we rename in the same classpath as the client) */
|
|
|
|
logger.info("Prefixing loader and unpacker class names");
|
|
|
|
loader.remap(PrefixRemapper.create(loader, "loader_"));
|
|
|
|
glLoader.remap(PrefixRemapper.create(glLoader, "loader_"));
|
|
|
|
unpacker.remap(PrefixRemapper.create(unpacker, "unpacker_"));
|
|
|
|
glUnpacker.remap(PrefixRemapper.create(glUnpacker, "unpacker_"));
|
|
|
|
|
|
|
|
/* bundle libraries together into a common classpath */
|
|
|
|
var runtime = ClassLoader.getPlatformClassLoader();
|
|
|
|
var classPath = new ClassPath(runtime, ImmutableList.of(), ImmutableList.of(client, loader, signLink, unpack, unpacker));
|
|
|
|
var glClassPath = new ClassPath(runtime, ImmutableList.of(gl, glDri), ImmutableList.of(glClient, glLoader, glSignLink, glUnpack, glUnpacker));
|
|
|
|
var unsignedClassPath = new ClassPath(runtime, ImmutableList.of(), ImmutableList.of(unsignedClient));
|
|
|
|
|
|
|
|
/* deobfuscate */
|
|
|
|
logger.info("Transforming client");
|
|
|
|
for (var transformer : TRANSFORMERS) {
|
|
|
|
logger.info("Running transformer {}", transformer.getClass().getSimpleName());
|
|
|
|
transformer.transform(classPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.info("Transforming client_gl");
|
|
|
|
for (var transformer : TRANSFORMERS) {
|
|
|
|
logger.info("Running transformer {}", transformer.getClass().getSimpleName());
|
|
|
|
transformer.transform(glClassPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.info("Transforming client_unsigned");
|
|
|
|
for (var transformer : TRANSFORMERS) {
|
|
|
|
logger.info("Running transformer {}", transformer.getClass().getSimpleName());
|
|
|
|
transformer.transform(unsignedClassPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* write output jars */
|
|
|
|
logger.info("Writing output jars");
|
|
|
|
|
|
|
|
Files.createDirectories(output);
|
|
|
|
|
|
|
|
client.writeJar(output.resolve("runescape.jar"));
|
|
|
|
loader.writeJar(output.resolve("loader.jar"));
|
|
|
|
signLink.writeJar(output.resolve("signlink.jar"));
|
|
|
|
unpack.writeJar(output.resolve("unpack.jar"));
|
|
|
|
unpacker.writeJar(output.resolve("unpacker.jar"));
|
|
|
|
|
|
|
|
gl.writeJar(output.resolve("jaggl.jar"));
|
|
|
|
glDri.writeJar(output.resolve("jaggl_dri.jar"));
|
|
|
|
glClient.writeJar(output.resolve("runescape_gl.jar"));
|
|
|
|
glLoader.writeJar(output.resolve("loader_gl.jar"));
|
|
|
|
glSignLink.writeJar(output.resolve("signlink_gl.jar"));
|
|
|
|
glUnpack.writeJar(output.resolve("unpack_gl.jar"));
|
|
|
|
glUnpacker.writeJar(output.resolve("unpacker_gl.jar"));
|
|
|
|
|
|
|
|
unsignedClient.writeJar(output.resolve("runescape_unsigned.jar"));
|
|
|
|
}
|
|
|
|
}
|