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 <gpe@openrs2.dev>
master
Graham 5 years ago
parent 1805873385
commit e6402d52c2
  1. 22
      asm/src/main/java/dev/openrs2/asm/classpath/ClassMetadata.kt
  2. 21
      asm/src/main/java/dev/openrs2/asm/classpath/Library.kt
  3. 21
      asm/src/main/java/dev/openrs2/asm/classpath/StackFrameClassWriter.kt
  4. 20
      bundler/src/main/java/dev/openrs2/bundler/Bundler.kt
  5. 13
      bundler/src/main/java/dev/openrs2/bundler/Resource.kt
  6. 30
      deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt

@ -38,6 +38,28 @@ abstract class ClassMetadata {
return false 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 { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (other !is ClassMetadata) return false if (other !is ClassMetadata) return false

@ -7,7 +7,6 @@ import dev.openrs2.common.crypto.Pkcs12KeyStore
import dev.openrs2.common.io.DeterministicJarOutputStream import dev.openrs2.common.io.DeterministicJarOutputStream
import dev.openrs2.common.io.SkipOutputStream import dev.openrs2.common.io.SkipOutputStream
import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.commons.Remapper import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.util.CheckClassAdapter import org.objectweb.asm.util.CheckClassAdapter
@ -64,18 +63,18 @@ class Library constructor() : Iterable<ClassNode> {
classes = classes.mapKeysTo(TreeMap()) { (_, clazz) -> clazz.name } 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" } logger.info { "Writing jar $path" }
Files.newOutputStream(path).use { 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 -> DeterministicJarOutputStream.create(out, manifest).use { jar ->
for (clazz in classes.values) { for (clazz in classes.values) {
val writer = ClassWriter(0) val writer = StackFrameClassWriter(classPath)
clazz.accept(writer) clazz.accept(writer)
@ -96,12 +95,12 @@ class Library constructor() : Iterable<ClassNode> {
} }
} }
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" } logger.info { "Writing signed jar $path" }
val unsignedPath = Files.createTempFile("tmp", ".jar") val unsignedPath = Files.createTempFile("tmp", ".jar")
try { try {
writeJar(unsignedPath, manifest) writeJar(classPath, unsignedPath, manifest)
keyStore.signJar(unsignedPath) keyStore.signJar(unsignedPath)
DeterministicJarOutputStream.repack(unsignedPath, path) DeterministicJarOutputStream.repack(unsignedPath, path)
} finally { } finally {
@ -109,10 +108,10 @@ class Library constructor() : Iterable<ClassNode> {
} }
} }
fun writePack(out: OutputStream) { fun writePack(classPath: ClassPath, out: OutputStream) {
val temp = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) val temp = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX)
try { try {
writeJar(temp) writeJar(classPath, temp)
JarInputStream(Files.newInputStream(temp)).use { `in` -> JarInputStream(Files.newInputStream(temp)).use { `in` ->
val headerSize = GZIP_HEADER.size.toLong() val headerSize = GZIP_HEADER.size.toLong()
@ -126,7 +125,7 @@ class Library constructor() : Iterable<ClassNode> {
} }
} }
fun writeJs5(out: OutputStream) { fun writeJs5(classPath: ClassPath, out: OutputStream) {
// TODO(gpe): implement // TODO(gpe): implement
} }
@ -151,7 +150,7 @@ class Library constructor() : Iterable<ClassNode> {
val clazz = ClassNode() val clazz = ClassNode()
val reader = ClassReader(`in`) 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) library.add(clazz)
} }

@ -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
}
}
}
}

@ -72,13 +72,13 @@ class Bundler @Inject constructor(publicKeyTransformer: PublicKeyTransformer) {
// compress resources // compress resources
logger.info { "Compressing resources" } logger.info { "Compressing resources" }
val unpackerJar = Resource.compressJar("unpackclass.pack", "game_unpacker.dat", unpacker) val unpackerJar = Resource.compressJar("unpackclass.pack", "game_unpacker.dat", classPath, unpacker)
val clientPack = Resource.compressPack("runescape.pack200", "main_file_cache.dat0", client) val clientPack = Resource.compressPack("runescape.pack200", "main_file_cache.dat0", classPath, client)
val clientJs5 = Resource.compressJs5("runescape.js5", "main_file_cache.dat1", client) val clientJs5 = Resource.compressJs5("runescape.js5", "main_file_cache.dat1", classPath, client)
val glClientPack = Resource.compressPack("runescape_gl.pack200", "main_file_cache.dat3", glClient) val glClientPack = Resource.compressPack("runescape_gl.pack200", "main_file_cache.dat3", glClassPath, glClient)
val glClientJs5 = Resource.compressJs5("runescape_gl.js5", "main_file_cache.dat4", glClient) val glClientJs5 = Resource.compressJs5("runescape_gl.js5", "main_file_cache.dat4", glClassPath, glClient)
val glPack = Resource.compressPack("jaggl.pack200", "main_file_cache.dat5", gl) val glPack = Resource.compressPack("jaggl.pack200", "main_file_cache.dat5", glClassPath, gl)
val glJs5 = Resource.compressJs5("jaggl.js5", "main_file_cache.dat6", gl) val glJs5 = Resource.compressJs5("jaggl.js5", "main_file_cache.dat6", glClassPath, gl)
val glNatives = Resource.compressGlNatives() val glNatives = Resource.compressGlNatives()
val miscNatives = Resource.compressMiscNatives() val miscNatives = Resource.compressMiscNatives()
@ -117,11 +117,11 @@ class Bundler @Inject constructor(publicKeyTransformer: PublicKeyTransformer) {
} }
// write unsigned client and loaders // 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) val keyStore = Pkcs12KeyStore.open(keyStorePath)
loader.writeSignedJar(output.resolve("loader.jar"), keyStore, signedManifest) loader.writeSignedJar(classPath, output.resolve("loader.jar"), keyStore, signedManifest)
glLoader.writeSignedJar(output.resolve("loader_gl.jar"), keyStore, signedManifest) glLoader.writeSignedJar(glClassPath, output.resolve("loader_gl.jar"), keyStore, signedManifest)
} }
companion object { companion object {

@ -1,6 +1,7 @@
package dev.openrs2.bundler package dev.openrs2.bundler
import com.github.michaelbull.logging.InlineLogger import com.github.michaelbull.logging.InlineLogger
import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library import dev.openrs2.asm.classpath.Library
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.nio.file.Files import java.nio.file.Files
@ -64,23 +65,23 @@ class Resource(
return Resource(source, destination, crc.value.toInt(), digest.digest(), uncompressed.size, content) 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 -> ByteArrayOutputStream().use { out ->
library.writeJar(out) library.writeJar(classPath, out)
return compress(source, destination, out.toByteArray()) 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 -> ByteArrayOutputStream().use { out ->
library.writePack(out) library.writePack(classPath, out)
return compress(source, destination, out.toByteArray()) 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 -> ByteArrayOutputStream().use { out ->
library.writeJs5(out) library.writeJs5(classPath, out)
return compress(source, destination, out.toByteArray()) return compress(source, destination, out.toByteArray())
} }
} }

@ -133,21 +133,21 @@ class Deobfuscator(private val input: Path, private val output: Path) {
Files.createDirectories(output) Files.createDirectories(output)
client.writeJar(output.resolve("runescape.jar")) client.writeJar(classPath, output.resolve("runescape.jar"))
loader.writeJar(output.resolve("loader.jar")) loader.writeJar(classPath, output.resolve("loader.jar"))
signLink.writeJar(output.resolve("signlink.jar")) signLink.writeJar(classPath, output.resolve("signlink.jar"))
unpack.writeJar(output.resolve("unpack.jar")) unpack.writeJar(classPath, output.resolve("unpack.jar"))
unpacker.writeJar(output.resolve("unpacker.jar")) unpacker.writeJar(classPath, output.resolve("unpacker.jar"))
gl.writeJar(output.resolve("jaggl.jar")) gl.writeJar(glClassPath, output.resolve("jaggl.jar"))
glDri.writeJar(output.resolve("jaggl_dri.jar")) glDri.writeJar(glClassPath, output.resolve("jaggl_dri.jar"))
glClient.writeJar(output.resolve("runescape_gl.jar")) glClient.writeJar(glClassPath, output.resolve("runescape_gl.jar"))
glLoader.writeJar(output.resolve("loader_gl.jar")) glLoader.writeJar(glClassPath, output.resolve("loader_gl.jar"))
glSignLink.writeJar(output.resolve("signlink_gl.jar")) glSignLink.writeJar(glClassPath, output.resolve("signlink_gl.jar"))
glUnpack.writeJar(output.resolve("unpack_gl.jar")) glUnpack.writeJar(glClassPath, output.resolve("unpack_gl.jar"))
glUnpacker.writeJar(output.resolve("unpacker_gl.jar")) glUnpacker.writeJar(glClassPath, output.resolve("unpacker_gl.jar"))
unsignedClient.writeJar(output.resolve("runescape_unsigned.jar")) unsignedClient.writeJar(unsignedClassPath, output.resolve("runescape_unsigned.jar"))
} }
companion object { companion object {

Loading…
Cancel
Save