From 26348b8a2ee606a94a839ac4d1565ab24b8b516e Mon Sep 17 00:00:00 2001 From: Scu11 Date: Tue, 14 Apr 2020 15:12:12 +0100 Subject: [PATCH] Move Library#write methods to dedicated classes Signed-off-by: Scu11 --- .../java/dev/openrs2/asm/classpath/Library.kt | 84 ------------------- .../dev/openrs2/asm/io/JarLibraryWriter.kt | 44 ++++++++++ .../dev/openrs2/asm/io/Js5LibraryWriter.kt | 11 +++ .../java/dev/openrs2/asm/io/LibraryWriter.kt | 8 ++ .../openrs2/asm/io/Pack200LibraryWriter.kt | 34 ++++++++ .../openrs2/asm/io/SignedJarLibraryWriter.kt | 52 ++++++++++++ .../main/java/dev/openrs2/bundler/Bundler.kt | 26 +++++- .../main/java/dev/openrs2/bundler/Resource.kt | 25 ++++-- .../java/dev/openrs2/deob/Deobfuscator.kt | 34 +++++--- .../util/io/DeterministicJarOutputStream.kt | 4 +- 10 files changed, 212 insertions(+), 110 deletions(-) create mode 100644 asm/src/main/java/dev/openrs2/asm/io/JarLibraryWriter.kt create mode 100644 asm/src/main/java/dev/openrs2/asm/io/Js5LibraryWriter.kt create mode 100644 asm/src/main/java/dev/openrs2/asm/io/LibraryWriter.kt create mode 100644 asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryWriter.kt create mode 100644 asm/src/main/java/dev/openrs2/asm/io/SignedJarLibraryWriter.kt 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 d7a9a3cb..03a9bc64 100644 --- a/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt +++ b/asm/src/main/java/dev/openrs2/asm/classpath/Library.kt @@ -1,26 +1,16 @@ package dev.openrs2.asm.classpath import com.github.michaelbull.logging.InlineLogger -import dev.openrs2.asm.ClassVersionUtils -import dev.openrs2.asm.NopClassVisitor import dev.openrs2.asm.remap import dev.openrs2.compress.gzip.Gzip -import dev.openrs2.crypto.Pkcs12KeyStore -import dev.openrs2.util.io.DeterministicJarOutputStream import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.Opcodes import org.objectweb.asm.commons.Remapper import org.objectweb.asm.tree.ClassNode -import org.objectweb.asm.util.CheckClassAdapter -import java.io.OutputStream import java.nio.file.Files import java.nio.file.Path import java.util.TreeMap -import java.util.jar.JarEntry import java.util.jar.JarInputStream import java.util.jar.JarOutputStream -import java.util.jar.Manifest import java.util.jar.Pack200 class Library() : Iterable { @@ -62,80 +52,6 @@ class Library() : Iterable { classes = classes.mapKeysTo(TreeMap()) { (_, clazz) -> clazz.name } } - fun writeJar(classPath: ClassPath, path: Path, manifest: Manifest? = null) { - logger.info { "Writing jar $path" } - - Files.newOutputStream(path).use { - writeJar(classPath, it, manifest) - } - } - - fun writeJar(classPath: ClassPath, out: OutputStream, manifest: Manifest? = null) { - DeterministicJarOutputStream.create(out, manifest).use { jar -> - for (clazz in classes.values) { - val writer = if (ClassVersionUtils.gte(clazz.version, Opcodes.V1_7)) { - StackFrameClassWriter(classPath) - } else { - ClassWriter(ClassWriter.COMPUTE_MAXS) - } - - clazz.accept(writer) - - jar.putNextEntry(JarEntry(clazz.name + CLASS_SUFFIX)) - jar.write(writer.toByteArray()) - - /* - * XXX(gpe): CheckClassAdapter breaks the Label offset - * calculation in the OriginalPcTable's write method, so we do - * a second pass without any attributes to check the class, - * feeding the callbacks into a no-op visitor. - */ - for (method in clazz.methods) { - method.attrs?.clear() - } - clazz.accept(CheckClassAdapter(NopClassVisitor, true)) - } - } - } - - fun writeSignedJar(classPath: ClassPath, path: Path, keyStore: Pkcs12KeyStore, manifest: Manifest? = null) { - logger.info { "Writing signed jar $path" } - - val unsignedPath = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) - try { - writeJar(classPath, unsignedPath, manifest) - - val signedPath = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) - try { - keyStore.signJar(unsignedPath, signedPath) - DeterministicJarOutputStream.repack(signedPath, path) - } finally { - Files.deleteIfExists(signedPath) - } - } finally { - Files.deleteIfExists(unsignedPath) - } - } - - fun writePack(classPath: ClassPath, out: OutputStream) { - val temp = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) - try { - writeJar(classPath, temp) - - JarInputStream(Files.newInputStream(temp)).use { `in` -> - Gzip.createHeaderlessOutputStream(out).use { gzip -> - Pack200.newPacker().pack(`in`, gzip) - } - } - } finally { - Files.deleteIfExists(temp) - } - } - - fun writeJs5(classPath: ClassPath, out: OutputStream) { - // TODO(gpe): implement - } - companion object { private val logger = InlineLogger() private const val CLASS_SUFFIX = ".class" diff --git a/asm/src/main/java/dev/openrs2/asm/io/JarLibraryWriter.kt b/asm/src/main/java/dev/openrs2/asm/io/JarLibraryWriter.kt new file mode 100644 index 00000000..a3a4a91a --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/JarLibraryWriter.kt @@ -0,0 +1,44 @@ +package dev.openrs2.asm.io + +import dev.openrs2.asm.ClassVersionUtils +import dev.openrs2.asm.NopClassVisitor +import dev.openrs2.asm.classpath.ClassPath +import dev.openrs2.asm.classpath.Library +import dev.openrs2.asm.classpath.StackFrameClassWriter +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.Opcodes +import org.objectweb.asm.util.CheckClassAdapter +import java.util.jar.JarEntry +import java.util.jar.JarOutputStream + +class JarLibraryWriter(private val output: JarOutputStream) : LibraryWriter { + override fun write(classPath: ClassPath, library: Library) { + for (clazz in library) { + val writer = if (ClassVersionUtils.gte(clazz.version, Opcodes.V1_7)) { + StackFrameClassWriter(classPath) + } else { + ClassWriter(ClassWriter.COMPUTE_MAXS) + } + + clazz.accept(writer) + + output.putNextEntry(JarEntry(clazz.name + CLASS_SUFFIX)) + output.write(writer.toByteArray()) + + /* + * XXX(gpe): CheckClassAdapter breaks the Label offset + * calculation in the OriginalPcTable's write method, so we do + * a second pass without any attributes to check the class, + * feeding the callbacks into a no-op visitor. + */ + for (method in clazz.methods) { + method.attrs?.clear() + } + clazz.accept(CheckClassAdapter(NopClassVisitor, true)) + } + } + + private companion object { + private const val CLASS_SUFFIX = ".class" + } +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/Js5LibraryWriter.kt b/asm/src/main/java/dev/openrs2/asm/io/Js5LibraryWriter.kt new file mode 100644 index 00000000..6a70e511 --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/Js5LibraryWriter.kt @@ -0,0 +1,11 @@ +package dev.openrs2.asm.io + +import dev.openrs2.asm.classpath.ClassPath +import dev.openrs2.asm.classpath.Library +import java.io.OutputStream + +class Js5LibraryWriter(private val output: OutputStream) : LibraryWriter { + override fun write(classPath: ClassPath, library: Library) { + // TODO(gpe): implement + } +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/LibraryWriter.kt b/asm/src/main/java/dev/openrs2/asm/io/LibraryWriter.kt new file mode 100644 index 00000000..b04ee79f --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/LibraryWriter.kt @@ -0,0 +1,8 @@ +package dev.openrs2.asm.io + +import dev.openrs2.asm.classpath.ClassPath +import dev.openrs2.asm.classpath.Library + +interface LibraryWriter { + fun write(classPath: ClassPath, library: Library) +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryWriter.kt b/asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryWriter.kt new file mode 100644 index 00000000..a8dbea86 --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryWriter.kt @@ -0,0 +1,34 @@ +package dev.openrs2.asm.io + +import dev.openrs2.asm.classpath.ClassPath +import dev.openrs2.asm.classpath.Library +import dev.openrs2.compress.gzip.Gzip +import dev.openrs2.util.io.DeterministicJarOutputStream +import java.io.OutputStream +import java.nio.file.Files +import java.util.jar.JarInputStream +import java.util.jar.Pack200 + +class Pack200LibraryWriter(private val output: OutputStream) : LibraryWriter { + override fun write(classPath: ClassPath, library: Library) { + val tempJar = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) + try { + DeterministicJarOutputStream(Files.newOutputStream(tempJar)).use { tempOutput -> + JarLibraryWriter(tempOutput).write(classPath, library) + } + + JarInputStream(Files.newInputStream(tempJar)).use { input -> + Gzip.createHeaderlessOutputStream(output).use { gzip -> + Pack200.newPacker().pack(input, gzip) + } + } + } finally { + Files.deleteIfExists(tempJar) + } + } + + private companion object { + private const val TEMP_PREFIX = "tmp" + private const val JAR_SUFFIX = ".jar" + } +} diff --git a/asm/src/main/java/dev/openrs2/asm/io/SignedJarLibraryWriter.kt b/asm/src/main/java/dev/openrs2/asm/io/SignedJarLibraryWriter.kt new file mode 100644 index 00000000..12a81842 --- /dev/null +++ b/asm/src/main/java/dev/openrs2/asm/io/SignedJarLibraryWriter.kt @@ -0,0 +1,52 @@ +package dev.openrs2.asm.io + +import dev.openrs2.asm.classpath.ClassPath +import dev.openrs2.asm.classpath.Library +import dev.openrs2.crypto.Pkcs12KeyStore +import dev.openrs2.util.io.DeterministicJarOutputStream +import java.io.OutputStream +import java.nio.file.Files +import java.nio.file.Path +import java.util.jar.JarInputStream +import java.util.jar.Manifest + +class SignedJarLibraryWriter( + private val output: OutputStream, + private val manifest: Manifest, + private val keyStore: Pkcs12KeyStore +) : LibraryWriter { + override fun write(classPath: ClassPath, library: Library) { + val unsignedJar = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) + try { + DeterministicJarOutputStream(Files.newOutputStream(unsignedJar), manifest).use { unsignedOutput -> + JarLibraryWriter(unsignedOutput).write(classPath, library) + } + + val signedJar = Files.createTempFile(TEMP_PREFIX, JAR_SUFFIX) + try { + keyStore.signJar(unsignedJar, signedJar) + repack(signedJar) + } finally { + Files.deleteIfExists(signedJar) + } + } finally { + Files.deleteIfExists(unsignedJar) + } + } + + private fun repack(signedJar: Path) { + JarInputStream(Files.newInputStream(signedJar)).use { input -> + DeterministicJarOutputStream(output, input.manifest).use { output -> + generateSequence { input.nextJarEntry }.forEach { + output.putNextEntry(it) + input.copyTo(output) + } + } + } + } + + private companion object { + private const val TEMP_PREFIX = "tmp" + private const val JAR_SUFFIX = ".jar" + } +} diff --git a/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt b/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt index f7db7039..8f568761 100644 --- a/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt +++ b/bundler/src/main/java/dev/openrs2/bundler/Bundler.kt @@ -4,9 +4,13 @@ import com.github.michaelbull.logging.InlineLogger import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.Library import dev.openrs2.asm.transform.Transformer +import dev.openrs2.asm.io.JarLibraryWriter +import dev.openrs2.asm.io.SignedJarLibraryWriter import dev.openrs2.bundler.transform.ResourceTransformer import dev.openrs2.conf.Config import dev.openrs2.crypto.Pkcs12KeyStore +import dev.openrs2.util.io.DeterministicJarOutputStream +import java.nio.file.Files import java.nio.file.Path import java.util.jar.Attributes import java.util.jar.Attributes.Name.MANIFEST_VERSION @@ -115,11 +119,27 @@ class Bundler @Inject constructor( } // write unsigned client and loaders - client.writeJar(classPath, output.resolve("runescape.jar"), unsignedManifest) + writeJar(classPath, client, output.resolve("runescape.jar")) val keyStore = Pkcs12KeyStore.open(keyStorePath, config.game) - loader.writeSignedJar(classPath, output.resolve("loader.jar"), keyStore, signedManifest) - glLoader.writeSignedJar(glClassPath, output.resolve("loader_gl.jar"), keyStore, signedManifest) + writeSignedJar(classPath, loader, output.resolve("loader.jar"), keyStore) + writeSignedJar(glClassPath, glLoader, output.resolve("loader_gl.jar"), keyStore) + } + + private fun writeJar(classPath: ClassPath, library: Library, path: Path) { + logger.info { "Writing jar $path" } + + DeterministicJarOutputStream(Files.newOutputStream(path), unsignedManifest).use { output -> + JarLibraryWriter(output).write(classPath, library) + } + } + + private fun writeSignedJar(classPath: ClassPath, library: Library, path: Path, keyStore: Pkcs12KeyStore) { + logger.info { "Writing signed jar $path" } + + Files.newOutputStream(path).use { + SignedJarLibraryWriter(it, signedManifest, keyStore).write(classPath, library) + } } 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 4ed7e5d6..d49a3b94 100644 --- a/bundler/src/main/java/dev/openrs2/bundler/Resource.kt +++ b/bundler/src/main/java/dev/openrs2/bundler/Resource.kt @@ -3,6 +3,10 @@ package dev.openrs2.bundler import com.github.michaelbull.logging.InlineLogger import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.Library +import dev.openrs2.asm.io.JarLibraryWriter +import dev.openrs2.asm.io.Js5LibraryWriter +import dev.openrs2.asm.io.Pack200LibraryWriter +import dev.openrs2.util.io.DeterministicJarOutputStream import java.io.ByteArrayOutputStream import java.nio.file.Files import java.nio.file.Path @@ -66,23 +70,26 @@ class Resource( } fun compressJar(source: String, destination: String, classPath: ClassPath, library: Library): Resource { - ByteArrayOutputStream().use { out -> - library.writeJar(classPath, out) - return compress(source, destination, out.toByteArray()) + ByteArrayOutputStream().use { output -> + DeterministicJarOutputStream(output).use { jarOutput -> + JarLibraryWriter(jarOutput).write(classPath, library) + } + + return compress(source, destination, output.toByteArray()) } } fun compressPack(source: String, destination: String, classPath: ClassPath, library: Library): Resource { - ByteArrayOutputStream().use { out -> - library.writePack(classPath, out) - return compress(source, destination, out.toByteArray()) + ByteArrayOutputStream().use { output -> + Pack200LibraryWriter(output).write(classPath, library) + return compress(source, destination, output.toByteArray()) } } fun compressJs5(source: String, destination: String, classPath: ClassPath, library: Library): Resource { - ByteArrayOutputStream().use { out -> - library.writeJs5(classPath, out) - return compress(source, destination, out.toByteArray()) + ByteArrayOutputStream().use { output -> + Js5LibraryWriter(output).write(classPath, library) + return compress(source, destination, output.toByteArray()) } } diff --git a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt index 5cfe4a8b..aebb5dce 100644 --- a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt +++ b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt @@ -3,8 +3,10 @@ package dev.openrs2.deob import com.github.michaelbull.logging.InlineLogger import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.Library +import dev.openrs2.asm.io.JarLibraryWriter import dev.openrs2.asm.transform.Transformer import dev.openrs2.deob.remap.PrefixRemapper +import dev.openrs2.util.io.DeterministicJarOutputStream import java.nio.file.Files import java.nio.file.Path import javax.inject.Inject @@ -100,20 +102,28 @@ class Deobfuscator @Inject constructor( Files.createDirectories(output) - 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")) - unpackClass.writeJar(classPath, output.resolve("unpackclass.jar")) + writeJar(classPath, client, output.resolve("runescape.jar")) + writeJar(classPath, loader, output.resolve("loader.jar")) + writeJar(classPath, signLink, output.resolve("signlink.jar")) + writeJar(classPath, unpack, output.resolve("unpack.jar")) + writeJar(classPath, unpackClass, output.resolve("unpackclass.jar")) - gl.writeJar(glClassPath, output.resolve("jaggl.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")) - glUnpackClass.writeJar(glClassPath, output.resolve("unpackclass_gl.jar")) + writeJar(glClassPath, gl, output.resolve("jaggl.jar")) + writeJar(glClassPath, glClient, output.resolve("runescape_gl.jar")) + writeJar(glClassPath, glLoader, output.resolve("loader_gl.jar")) + writeJar(glClassPath, glSignLink, output.resolve("signlink_gl.jar")) + writeJar(glClassPath, glUnpack, output.resolve("unpack_gl.jar")) + writeJar(glClassPath, glUnpackClass, output.resolve("unpackclass_gl.jar")) - unsignedClient.writeJar(unsignedClassPath, output.resolve("runescape_unsigned.jar")) + writeJar(unsignedClassPath, unsignedClient, output.resolve("runescape_unsigned.jar")) + } + + private fun writeJar(classPath: ClassPath, library: Library, path: Path) { + logger.info { "Writing jar $path" } + + DeterministicJarOutputStream(Files.newOutputStream(path)).use { output -> + JarLibraryWriter(output).write(classPath, library) + } } companion object { diff --git a/util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.kt b/util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.kt index 29b658f2..76820ba2 100644 --- a/util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.kt +++ b/util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.kt @@ -10,8 +10,8 @@ import java.util.jar.Manifest import java.util.zip.ZipEntry class DeterministicJarOutputStream : JarOutputStream { - private constructor(out: OutputStream) : super(out) - private constructor(out: OutputStream, man: Manifest) : super(out, man) + constructor(out: OutputStream) : super(out) + constructor(out: OutputStream, man: Manifest) : super(out, man) override fun putNextEntry(ze: ZipEntry) { ze.creationTime = UNIX_EPOCH