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>
pull/74/head
Graham 4 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
}
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

@ -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<ClassNode> {
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<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" }
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<ClassNode> {
}
}
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<ClassNode> {
}
}
fun writeJs5(out: OutputStream) {
fun writeJs5(classPath: ClassPath, out: OutputStream) {
// TODO(gpe): implement
}
@ -151,7 +150,7 @@ class Library constructor() : Iterable<ClassNode> {
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)
}

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

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

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

Loading…
Cancel
Save