Add custom Attribute for tracking original instruction indexes

master
Graham 5 years ago
parent 6464f8dc77
commit be14ffdc2d
  1. 6
      asm/src/main/java/dev/openrs2/asm/NopClassVisitor.kt
  2. 12
      asm/src/main/java/dev/openrs2/asm/classpath/ClassPath.kt
  3. 14
      asm/src/main/java/dev/openrs2/asm/classpath/Library.kt
  4. 6
      deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt
  5. 37
      deob/src/main/java/dev/openrs2/deob/OriginalPcTable.kt
  6. 55
      deob/src/main/java/dev/openrs2/deob/transform/OriginalPcRestoreTransformer.kt
  7. 28
      deob/src/main/java/dev/openrs2/deob/transform/OriginalPcSaveTransformer.kt

@ -0,0 +1,6 @@
package dev.openrs2.asm
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.Opcodes
object NopClassVisitor : ClassVisitor(Opcodes.ASM7)

@ -6,6 +6,7 @@ import dev.openrs2.asm.toBinaryClassName
import dev.openrs2.common.collect.DisjointSet import dev.openrs2.common.collect.DisjointSet
import dev.openrs2.common.collect.ForestDisjointSet import dev.openrs2.common.collect.ForestDisjointSet
import org.objectweb.asm.commons.Remapper import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.tree.AbstractInsnNode
import org.objectweb.asm.tree.ClassNode import org.objectweb.asm.tree.ClassNode
class ClassPath( class ClassPath(
@ -15,6 +16,17 @@ class ClassPath(
) { ) {
private val cache = mutableMapOf<String, ClassMetadata?>() private val cache = mutableMapOf<String, ClassMetadata?>()
/*
* XXX(gpe): this is a bit of a hack, as it makes the asm module contain
* some details that are only relevant in the deobfuscator. However, I
* can't think of a better way of storing this state at the moment - ASM
* doesn't have support for attaching arbitrary state to an
* AbstractInsnNode. We need to persist the state across all of our
* Transformers to avoid adding extraneous labels until the last possible
* moment, which would confuse some of our analyses if added earlier.
*/
val originalPcs = mutableMapOf<AbstractInsnNode, Int>()
val libraryClasses: List<ClassMetadata> val libraryClasses: List<ClassMetadata>
get() { get() {
val classes = mutableListOf<ClassMetadata>() val classes = mutableListOf<ClassMetadata>()

@ -1,6 +1,7 @@
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.NopClassVisitor
import dev.openrs2.asm.remap import dev.openrs2.asm.remap
import dev.openrs2.common.crypto.Pkcs12KeyStore import dev.openrs2.common.crypto.Pkcs12KeyStore
import dev.openrs2.common.io.DeterministicJarOutputStream import dev.openrs2.common.io.DeterministicJarOutputStream
@ -76,10 +77,21 @@ class Library constructor() : Iterable<ClassNode> {
for (clazz in classes.values) { for (clazz in classes.values) {
val writer = ClassWriter(0) val writer = ClassWriter(0)
clazz.accept(CheckClassAdapter(writer, true)) clazz.accept(writer)
jar.putNextEntry(JarEntry(clazz.name + CLASS_SUFFIX)) jar.putNextEntry(JarEntry(clazz.name + CLASS_SUFFIX))
jar.write(writer.toByteArray()) 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))
} }
} }
} }

@ -24,6 +24,8 @@ import dev.openrs2.deob.transform.InvokeSpecialTransformer
import dev.openrs2.deob.transform.MethodOrderTransformer import dev.openrs2.deob.transform.MethodOrderTransformer
import dev.openrs2.deob.transform.OpaquePredicateTransformer import dev.openrs2.deob.transform.OpaquePredicateTransformer
import dev.openrs2.deob.transform.OriginalNameTransformer import dev.openrs2.deob.transform.OriginalNameTransformer
import dev.openrs2.deob.transform.OriginalPcRestoreTransformer
import dev.openrs2.deob.transform.OriginalPcSaveTransformer
import dev.openrs2.deob.transform.OverrideTransformer import dev.openrs2.deob.transform.OverrideTransformer
import dev.openrs2.deob.transform.RemapTransformer import dev.openrs2.deob.transform.RemapTransformer
import dev.openrs2.deob.transform.ResetTransformer import dev.openrs2.deob.transform.ResetTransformer
@ -151,6 +153,7 @@ class Deobfuscator(private val input: Path, private val output: Path) {
companion object { companion object {
private val logger = InlineLogger() private val logger = InlineLogger()
private val TRANSFORMERS = listOf( private val TRANSFORMERS = listOf(
OriginalPcSaveTransformer(),
OriginalNameTransformer(), OriginalNameTransformer(),
*Bundler.TRANSFORMERS.toTypedArray(), *Bundler.TRANSFORMERS.toTypedArray(),
ResourceTransformer(), ResourceTransformer(),
@ -173,7 +176,8 @@ class Deobfuscator(private val input: Path, private val output: Path) {
EmptyClassTransformer(), EmptyClassTransformer(),
MethodOrderTransformer(), MethodOrderTransformer(),
VisibilityTransformer(), VisibilityTransformer(),
OverrideTransformer() OverrideTransformer(),
OriginalPcRestoreTransformer()
) )
} }
} }

@ -0,0 +1,37 @@
package dev.openrs2.deob
import org.objectweb.asm.Attribute
import org.objectweb.asm.ByteVector
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Label
import org.objectweb.asm.tree.LabelNode
class OriginalPcTable(private val pcs: Map<LabelNode, Int>) : Attribute("OriginalPcTable") {
override fun isCodeAttribute(): Boolean {
return true
}
override fun isUnknown(): Boolean {
return false
}
override fun getLabels(): Array<Label> {
return pcs.keys.map(LabelNode::getLabel).toTypedArray()
}
override fun write(
classWriter: ClassWriter,
code: ByteArray?,
codeLength: Int,
maxStack: Int,
maxLocals: Int
): ByteVector {
val buf = ByteVector()
buf.putShort(pcs.size)
for ((label, pc) in pcs) {
buf.putShort(label.label.offset)
buf.putShort(pc)
}
return buf
}
}

@ -0,0 +1,55 @@
package dev.openrs2.deob.transform
import com.github.michaelbull.logging.InlineLogger
import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library
import dev.openrs2.asm.removeDeadCode
import dev.openrs2.asm.transform.Transformer
import dev.openrs2.deob.OriginalPcTable
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.LabelNode
import org.objectweb.asm.tree.MethodNode
class OriginalPcRestoreTransformer : Transformer() {
private var originalPcsRestored = 0
override fun preTransform(classPath: ClassPath) {
originalPcsRestored = 0
}
override fun transformCode(classPath: ClassPath, library: Library, clazz: ClassNode, method: MethodNode): Boolean {
method.removeDeadCode(clazz.name)
val pcs = mutableMapOf<LabelNode, Int>()
for (insn in method.instructions) {
if (insn.opcode == -1) {
continue
}
val originalPc = classPath.originalPcs[insn] ?: continue
val label = LabelNode()
method.instructions.insertBefore(insn, label)
pcs[label] = originalPc
originalPcsRestored++
}
if (method.attrs == null) {
method.attrs = mutableListOf()
}
method.attrs.add(OriginalPcTable(pcs))
return false
}
override fun postTransform(classPath: ClassPath) {
logger.info { "Restored $originalPcsRestored original instruction indexes" }
}
companion object {
private val logger = InlineLogger()
}
}

@ -0,0 +1,28 @@
package dev.openrs2.deob.transform
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 org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.MethodNode
class OriginalPcSaveTransformer : Transformer() {
override fun transformCode(classPath: ClassPath, library: Library, clazz: ClassNode, method: MethodNode): Boolean {
for ((pc, insn) in method.instructions.withIndex()) {
if (insn.opcode != -1) {
classPath.originalPcs[insn] = pc
}
}
return false
}
override fun postTransform(classPath: ClassPath) {
logger.info { "Saved ${classPath.originalPcs.size} original instruction indexes" }
}
companion object {
private val logger = InlineLogger()
}
}
Loading…
Cancel
Save