Remap ClassNodes directly

This is necessary for a future commit that will track the original
indexes of AbstractInsnNodes throughout the deobfuscator, with the aim
of using this information to track local variables in the decompiled
code.

I suspect this will also improve performance, as we don't need to
re-allocate all of the tree objects.
pull/70/head
Graham 4 years ago
parent d32de8537d
commit 6464f8dc77
  1. 86
      asm/src/main/java/dev/openrs2/asm/ClassNodeRemapper.kt
  2. 26
      asm/src/main/java/dev/openrs2/asm/classpath/Library.kt

@ -0,0 +1,86 @@
package dev.openrs2.asm
import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.tree.AbstractInsnNode
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldInsnNode
import org.objectweb.asm.tree.FrameNode
import org.objectweb.asm.tree.InvokeDynamicInsnNode
import org.objectweb.asm.tree.LdcInsnNode
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.MultiANewArrayInsnNode
import org.objectweb.asm.tree.TypeInsnNode
fun ClassNode.remap(remapper: Remapper) {
val originalName = name
name = remapper.mapType(originalName)
signature = remapper.mapSignature(signature, false)
superName = remapper.mapType(superName)
if (interfaces != null) {
interfaces = interfaces.map(remapper::mapType)
}
for (field in fields) {
field.name = remapper.mapFieldName(originalName, field.name, field.desc)
field.desc = remapper.mapDesc(field.desc)
field.signature = remapper.mapSignature(field.signature, true)
if (field.value != null) {
field.value = remapper.mapValue(field.value)
}
}
for (method in methods) {
method.name = remapper.mapMethodName(originalName, method.name, method.desc)
method.desc = remapper.mapMethodDesc(method.desc)
method.signature = remapper.mapSignature(method.signature, false)
method.exceptions = method.exceptions.map(remapper::mapType)
if (method.hasCode()) {
ClassForNameUtils.remap(remapper, method)
for (insn in method.instructions) {
insn.remap(remapper)
}
for (tryCatch in method.tryCatchBlocks) {
tryCatch.type = remapper.mapType(tryCatch.type)
}
}
}
}
private fun Remapper.mapFrameType(type: Any): Any {
return if (type is String) {
mapType(type)
} else {
type
}
}
private fun AbstractInsnNode.remap(remapper: Remapper) {
when (this) {
is FrameNode -> {
local = local.map(remapper::mapFrameType)
stack = stack.map(remapper::mapFrameType)
}
is FieldInsnNode -> {
val originalOwner = owner
owner = remapper.mapType(originalOwner)
name = remapper.mapFieldName(originalOwner, name, desc)
desc = remapper.mapDesc(desc)
}
is MethodInsnNode -> {
val originalOwner = owner
owner = remapper.mapType(originalOwner)
name = remapper.mapMethodName(originalOwner, name, desc)
desc = remapper.mapDesc(desc)
}
is InvokeDynamicInsnNode -> throw UnsupportedOperationException()
is TypeInsnNode -> desc = remapper.mapType(desc)
is LdcInsnNode -> cst = remapper.mapValue(cst)
is MultiANewArrayInsnNode -> desc = remapper.mapType(desc)
}
}

@ -1,14 +1,12 @@
package dev.openrs2.asm.classpath package dev.openrs2.asm.classpath
import com.github.michaelbull.logging.InlineLogger import com.github.michaelbull.logging.InlineLogger
import dev.openrs2.asm.ClassForNameUtils import dev.openrs2.asm.remap
import dev.openrs2.asm.hasCode
import dev.openrs2.common.crypto.Pkcs12KeyStore 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.ClassWriter
import org.objectweb.asm.commons.ClassRemapper
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
@ -17,7 +15,6 @@ import java.io.OutputStream
import java.io.SequenceInputStream import java.io.SequenceInputStream
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.util.HashSet
import java.util.TreeMap import java.util.TreeMap
import java.util.jar.JarEntry import java.util.jar.JarEntry
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
@ -28,7 +25,7 @@ import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
class Library constructor() : Iterable<ClassNode> { class Library constructor() : Iterable<ClassNode> {
private val classes = TreeMap<String, ClassNode>() private var classes = TreeMap<String, ClassNode>()
constructor(library: Library) : this() { constructor(library: Library) : this() {
for (clazz in library.classes.values) { for (clazz in library.classes.values) {
@ -59,26 +56,11 @@ class Library constructor() : Iterable<ClassNode> {
} }
fun remap(remapper: Remapper) { fun remap(remapper: Remapper) {
val classNames = HashSet<String>()
for (clazz in classes.values) { for (clazz in classes.values) {
for (method in clazz.methods) { clazz.remap(remapper)
if (method.hasCode()) {
ClassForNameUtils.remap(remapper, method)
}
}
classNames.add(clazz.name)
} }
for (name in classNames) { classes = classes.mapKeysTo(TreeMap()) { (_, clazz) -> clazz.name }
val `in` = classes.remove(name)
val out = ClassNode()
`in`!!.accept(ClassRemapper(out, remapper))
classes[out.name] = out
}
} }
fun writeJar(path: Path, manifest: Manifest? = null) { fun writeJar(path: Path, manifest: Manifest? = null) {

Loading…
Cancel
Save