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 0a3b80be..e82a85a7 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 a3218a98..212c3eeb 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 00000000..2f6ce2dc --- /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 da353305..595443c9 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 83c2589a..4ed7e5d6 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 fc5d6223..c220fab4 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 {