Move Library#write methods to dedicated classes

Signed-off-by: Scu11 <scu11@openrs2.dev>
pull/105/head
Scu11 4 years ago committed by Graham
parent b40eedfb3b
commit 26348b8a2e
  1. 84
      asm/src/main/java/dev/openrs2/asm/classpath/Library.kt
  2. 44
      asm/src/main/java/dev/openrs2/asm/io/JarLibraryWriter.kt
  3. 11
      asm/src/main/java/dev/openrs2/asm/io/Js5LibraryWriter.kt
  4. 8
      asm/src/main/java/dev/openrs2/asm/io/LibraryWriter.kt
  5. 34
      asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryWriter.kt
  6. 52
      asm/src/main/java/dev/openrs2/asm/io/SignedJarLibraryWriter.kt
  7. 26
      bundler/src/main/java/dev/openrs2/bundler/Bundler.kt
  8. 25
      bundler/src/main/java/dev/openrs2/bundler/Resource.kt
  9. 34
      deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt
  10. 4
      util/src/main/java/dev/openrs2/util/io/DeterministicJarOutputStream.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<ClassNode> {
@ -62,80 +52,6 @@ class Library() : Iterable<ClassNode> {
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"

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save