Move Library#write methods to dedicated classes

Signed-off-by: Scu11 <scu11@openrs2.dev>
pull/102/head
Scu11 4 years ago
parent 609576be63
commit aa6d0cb6bb
  1. 84
      asm/src/main/java/dev/openrs2/asm/classpath/Library.kt
  2. 45
      asm/src/main/java/dev/openrs2/asm/io/JarLibraryWriter.kt
  3. 12
      asm/src/main/java/dev/openrs2/asm/io/Js5LibraryWriter.kt
  4. 8
      asm/src/main/java/dev/openrs2/asm/io/LibraryWriter.kt
  5. 36
      asm/src/main/java/dev/openrs2/asm/io/Pack200LibraryWriter.kt
  6. 54
      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. 28
      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,45 @@
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,12 @@
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,36 @@
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,54 @@
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"
}
}

@ -3,6 +3,8 @@ 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.SignedJarLibraryWriter
import dev.openrs2.bundler.transform.BufferSizeTransformer
import dev.openrs2.bundler.transform.CachePathTransformer
import dev.openrs2.bundler.transform.HostCheckTransformer
@ -14,6 +16,8 @@ import dev.openrs2.bundler.transform.ResourceTransformer
import dev.openrs2.bundler.transform.RightClickTransformer
import dev.openrs2.bundler.transform.TypoTransformer
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
@ -109,11 +113,27 @@ class Bundler @Inject constructor(publicKeyTransformer: PublicKeyTransformer) {
}
// 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)
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())
}
}

@ -5,6 +5,7 @@ import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library
import dev.openrs2.asm.classpath.Library.Companion.readJar
import dev.openrs2.asm.classpath.Library.Companion.readPack
import dev.openrs2.asm.io.JarLibraryWriter
import dev.openrs2.bundler.Bundler
import dev.openrs2.bundler.transform.ResourceTransformer
import dev.openrs2.deob.SignedClassUtils.move
@ -36,6 +37,7 @@ import dev.openrs2.deob.transform.UnusedArgTransformer
import dev.openrs2.deob.transform.UnusedLocalTransformer
import dev.openrs2.deob.transform.UnusedMethodTransformer
import dev.openrs2.deob.transform.VisibilityTransformer
import dev.openrs2.util.io.DeterministicJarOutputStream
import java.nio.file.Files
import java.nio.file.Path
@ -124,20 +126,28 @@ class Deobfuscator(private val input: Path, private val output: Path) {
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 {

@ -1,17 +1,15 @@
package dev.openrs2.util.io
import java.io.OutputStream
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.attribute.FileTime
import java.util.jar.JarInputStream
import java.util.jar.JarOutputStream
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
@ -22,25 +20,5 @@ class DeterministicJarOutputStream : JarOutputStream {
companion object {
private val UNIX_EPOCH = FileTime.fromMillis(0)
fun create(out: OutputStream, manifest: Manifest? = null): JarOutputStream {
return if (manifest != null) {
DeterministicJarOutputStream(out, manifest)
} else {
DeterministicJarOutputStream(out)
}
}
fun repack(src: Path, dest: Path) {
JarInputStream(Files.newInputStream(src)).use { input ->
create(Files.newOutputStream(dest), input.manifest).use { output ->
while (true) {
val entry = input.nextJarEntry ?: break
output.putNextEntry(entry)
input.copyTo(output)
}
}
}
}
}
}

Loading…
Cancel
Save