From e6402d52c2a014a02f67a441467ff233c7df7811 Mon Sep 17 00:00:00 2001 From: Graham Date: Sat, 7 Mar 2020 10:43:16 +0000 Subject: [PATCH] Add StackFrameClassWriter This allows us to avoid needing to worry about manipulating stack frames in individual transformers, which adds lots of complexity. It's much easier to just make ASM generate them for us. Signed-off-by: Graham --- .../openrs2/asm/classpath/ClassMetadata.kt | 22 ++++++++++++++ .../java/dev/openrs2/asm/classpath/Library.kt | 21 +++++++------ .../asm/classpath/StackFrameClassWriter.kt | 21 +++++++++++++ .../main/java/dev/openrs2/bundler/Bundler.kt | 20 ++++++------- .../main/java/dev/openrs2/bundler/Resource.kt | 13 ++++---- .../java/dev/openrs2/deob/Deobfuscator.kt | 30 +++++++++---------- 6 files changed, 85 insertions(+), 42 deletions(-) create mode 100644 asm/src/main/java/dev/openrs2/asm/classpath/StackFrameClassWriter.kt diff --git a/asm/src/main/java/dev/openrs2/asm/classpath/ClassMetadata.kt b/asm/src/main/java/dev/openrs2/asm/classpath/ClassMetadata.kt index 0a3b80beff..e82a85a76e 100644 --- a/asm/src/main/java/dev/openrs2/asm/classpath/ClassMetadata.kt +++ b/asm/src/main/java/dev/openrs2/asm/classpath/ClassMetadata.kt @@ -38,6 +38,28 @@ abstract class ClassMetadata { return false } + fun isAssignableFrom(type: ClassMetadata): Boolean { + return type == this || isSuperClassOf(type) || isSuperInterfaceOf(type) + } + + private tailrec fun isSuperClassOf(type: ClassMetadata): Boolean { + val superClass = type.superClass ?: return false + if (superClass == this) { + return true + } + return isSuperClassOf(superClass) + } + + private fun isSuperInterfaceOf(type: ClassMetadata): Boolean { + for (superInterface in type.superInterfaces) { + if (superInterface == this || isSuperInterfaceOf(superInterface)) { + return true + } + } + + return false + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is ClassMetadata) return false diff --git a/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt b/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt index a3218a984f..212c3eeb91 100644 --- a/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt +++ b/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt @@ -7,7 +7,6 @@ import dev.openrs2.common.crypto.Pkcs12KeyStore import dev.openrs2.common.io.DeterministicJarOutputStream import dev.openrs2.common.io.SkipOutputStream import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassWriter import org.objectweb.asm.commons.Remapper import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.util.CheckClassAdapter @@ -64,18 +63,18 @@ class Library constructor() : Iterable { classes = classes.mapKeysTo(TreeMap()) { (_, clazz) -> clazz.name } } - fun writeJar(path: Path, manifest: Manifest? = null) { + fun writeJar(classPath: ClassPath, path: Path, manifest: Manifest? = null) { logger.info { "Writing jar $path" } Files.newOutputStream(path).use { - writeJar(it, manifest) + writeJar(classPath, it, manifest) } } - fun writeJar(out: OutputStream, manifest: Manifest? = null) { + fun writeJar(classPath: ClassPath, out: OutputStream, manifest: Manifest? = null) { DeterministicJarOutputStream.create(out, manifest).use { jar -> for (clazz in classes.values) { - val writer = ClassWriter(0) + val writer = StackFrameClassWriter(classPath) clazz.accept(writer) @@ -96,12 +95,12 @@ class Library constructor() : Iterable { } } - fun writeSignedJar(path: Path, keyStore: Pkcs12KeyStore, manifest: Manifest? = null) { + fun writeSignedJar(classPath: ClassPath, path: Path, keyStore: Pkcs12KeyStore, manifest: Manifest? = null) { logger.info { "Writing signed jar $path" } val unsignedPath = Files.createTempFile("tmp", ".jar") try { - writeJar(unsignedPath, manifest) + writeJar(classPath, unsignedPath, manifest) keyStore.signJar(unsignedPath) DeterministicJarOutputStream.repack(unsignedPath, path) } finally { @@ -109,10 +108,10 @@ class Library constructor() : Iterable { } } - fun writePack(out: OutputStream) { + fun writePack(classPath: ClassPath, out: OutputStream) { val temp = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) try { - writeJar(temp) + writeJar(classPath, temp) JarInputStream(Files.newInputStream(temp)).use { `in` -> val headerSize = GZIP_HEADER.size.toLong() @@ -126,7 +125,7 @@ class Library constructor() : Iterable { } } - fun writeJs5(out: OutputStream) { + fun writeJs5(classPath: ClassPath, out: OutputStream) { // TODO(gpe): implement } @@ -151,7 +150,7 @@ class Library constructor() : Iterable { val clazz = ClassNode() val reader = ClassReader(`in`) - reader.accept(JsrInliner(clazz), ClassReader.SKIP_DEBUG) + reader.accept(JsrInliner(clazz), ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES) library.add(clazz) } diff --git a/asm/src/main/java/dev/openrs2/asm/classpath/StackFrameClassWriter.kt b/asm/src/main/java/dev/openrs2/asm/classpath/StackFrameClassWriter.kt new file mode 100644 index 0000000000..2f6ce2dc1f --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/classpath/StackFrameClassWriter.kt @@ -0,0 +1,21 @@ +package dev.openrs2.asm.classpath + +import org.objectweb.asm.ClassWriter + +class StackFrameClassWriter(private val classPath: ClassPath) : ClassWriter(COMPUTE_FRAMES) { + override fun getCommonSuperClass(type1: String, type2: String): String { + var c = classPath[type1]!! + val d = classPath[type2]!! + return when { + c.isAssignableFrom(d) -> type1 + d.isAssignableFrom(c) -> type2 + c.`interface` || d.`interface` -> "java/lang/Object" + else -> { + do { + c = c.superClass!! + } while (!c.isAssignableFrom(d)) + c.name + } + } + } +} diff --git a/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt b/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt index da3533056c..595443c932 100644 --- a/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt +++ b/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt @@ -72,13 +72,13 @@ class Bundler @Inject constructor(publicKeyTransformer: PublicKeyTransformer) { // compress resources logger.info { "Compressing resources" } - val unpackerJar = Resource.compressJar("unpackclass.pack", "game_unpacker.dat", unpacker) - val clientPack = Resource.compressPack("runescape.pack200", "main_file_cache.dat0", client) - val clientJs5 = Resource.compressJs5("runescape.js5", "main_file_cache.dat1", client) - val glClientPack = Resource.compressPack("runescape_gl.pack200", "main_file_cache.dat3", glClient) - val glClientJs5 = Resource.compressJs5("runescape_gl.js5", "main_file_cache.dat4", glClient) - val glPack = Resource.compressPack("jaggl.pack200", "main_file_cache.dat5", gl) - val glJs5 = Resource.compressJs5("jaggl.js5", "main_file_cache.dat6", gl) + val unpackerJar = Resource.compressJar("unpackclass.pack", "game_unpacker.dat", classPath, unpacker) + val clientPack = Resource.compressPack("runescape.pack200", "main_file_cache.dat0", classPath, client) + val clientJs5 = Resource.compressJs5("runescape.js5", "main_file_cache.dat1", classPath, client) + val glClientPack = Resource.compressPack("runescape_gl.pack200", "main_file_cache.dat3", glClassPath, glClient) + val glClientJs5 = Resource.compressJs5("runescape_gl.js5", "main_file_cache.dat4", glClassPath, glClient) + val glPack = Resource.compressPack("jaggl.pack200", "main_file_cache.dat5", glClassPath, gl) + val glJs5 = Resource.compressJs5("jaggl.js5", "main_file_cache.dat6", glClassPath, gl) val glNatives = Resource.compressGlNatives() val miscNatives = Resource.compressMiscNatives() @@ -117,11 +117,11 @@ class Bundler @Inject constructor(publicKeyTransformer: PublicKeyTransformer) { } // write unsigned client and loaders - client.writeJar(output.resolve("runescape.jar"), unsignedManifest) + client.writeJar(classPath, output.resolve("runescape.jar"), unsignedManifest) val keyStore = Pkcs12KeyStore.open(keyStorePath) - loader.writeSignedJar(output.resolve("loader.jar"), keyStore, signedManifest) - glLoader.writeSignedJar(output.resolve("loader_gl.jar"), keyStore, signedManifest) + loader.writeSignedJar(classPath, output.resolve("loader.jar"), keyStore, signedManifest) + glLoader.writeSignedJar(glClassPath, output.resolve("loader_gl.jar"), keyStore, signedManifest) } companion object { diff --git a/bundler/src/main/java/dev/openrs2/bundler/Resource.kt b/bundler/src/main/java/dev/openrs2/bundler/Resource.kt index 83c2589a9b..4ed7e5d6ec 100644 --- a/bundler/src/main/java/dev/openrs2/bundler/Resource.kt +++ b/bundler/src/main/java/dev/openrs2/bundler/Resource.kt @@ -1,6 +1,7 @@ package dev.openrs2.bundler import com.github.michaelbull.logging.InlineLogger +import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.Library import java.io.ByteArrayOutputStream import java.nio.file.Files @@ -64,23 +65,23 @@ class Resource( return Resource(source, destination, crc.value.toInt(), digest.digest(), uncompressed.size, content) } - fun compressJar(source: String, destination: String, library: Library): Resource { + fun compressJar(source: String, destination: String, classPath: ClassPath, library: Library): Resource { ByteArrayOutputStream().use { out -> - library.writeJar(out) + library.writeJar(classPath, out) return compress(source, destination, out.toByteArray()) } } - fun compressPack(source: String, destination: String, library: Library): Resource { + fun compressPack(source: String, destination: String, classPath: ClassPath, library: Library): Resource { ByteArrayOutputStream().use { out -> - library.writePack(out) + library.writePack(classPath, out) return compress(source, destination, out.toByteArray()) } } - fun compressJs5(source: String, destination: String, library: Library): Resource { + fun compressJs5(source: String, destination: String, classPath: ClassPath, library: Library): Resource { ByteArrayOutputStream().use { out -> - library.writeJs5(out) + library.writeJs5(classPath, out) return compress(source, destination, out.toByteArray()) } } diff --git a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt index fc5d6223a5..c220fab447 100644 --- a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt +++ b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt @@ -133,21 +133,21 @@ class Deobfuscator(private val input: Path, private val output: Path) { 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")) + client.writeJar(classPath, output.resolve("runescape.jar")) + loader.writeJar(classPath, output.resolve("loader.jar")) + signLink.writeJar(classPath, output.resolve("signlink.jar")) + unpack.writeJar(classPath, output.resolve("unpack.jar")) + unpacker.writeJar(classPath, output.resolve("unpacker.jar")) + + gl.writeJar(glClassPath, output.resolve("jaggl.jar")) + glDri.writeJar(glClassPath, output.resolve("jaggl_dri.jar")) + glClient.writeJar(glClassPath, output.resolve("runescape_gl.jar")) + glLoader.writeJar(glClassPath, output.resolve("loader_gl.jar")) + glSignLink.writeJar(glClassPath, output.resolve("signlink_gl.jar")) + glUnpack.writeJar(glClassPath, output.resolve("unpack_gl.jar")) + glUnpacker.writeJar(glClassPath, output.resolve("unpacker_gl.jar")) + + unsignedClient.writeJar(unsignedClassPath, output.resolve("runescape_unsigned.jar")) } companion object {