Add a CLI tool to interact with the deobfuscator

Signed-off-by: Gary Tierney <gary.tierney@fastmail.com>
feat/deob-ir
Gary Tierney 4 years ago
parent bbcdba3b34
commit 1d64a932bb
  1. 2
      buildSrc/src/main/java/Versions.kt
  2. 36
      deob-cli/build.gradle.kts
  3. 49
      deob-cli/src/main/java/dev/openrs2/deob/cli/DeobfuscatorClassLoader.kt
  4. 36
      deob-cli/src/main/java/dev/openrs2/deob/cli/DeobfuscatorCli.kt
  5. 3
      deob-cli/src/main/java/dev/openrs2/deob/cli/DeobfuscatorOptions.kt
  6. 26
      deob-cli/src/main/java/dev/openrs2/deob/cli/ir/MethodScopedCommand.kt
  7. 36
      deob-cli/src/main/java/dev/openrs2/deob/cli/ir/PrintCfgCommand.kt
  8. 1
      settings.gradle.kts

@ -1,6 +1,7 @@
object Versions {
const val asm = "7.3.1"
const val bouncyCastle = "1.64"
const val clikt = "2.5.0"
const val dependencyLicenseReport = "1.13"
const val fernflower = "1.0.4-SNAPSHOT"
const val guava = "28.2-jre"
@ -8,6 +9,7 @@ object Versions {
const val inlineLogger = "1.0.2"
const val javaParser = "3.15.14"
const val jdom = "2.0.6"
const val jgrapht = "1.4.0"
const val jimfs = "1.1"
const val junit = "5.6.0"
const val kotlin = "1.3.70"

@ -0,0 +1,36 @@
plugins {
`maven-publish`
application
kotlin("jvm")
}
application {
mainClassName = "dev.openrs2.deob.cli.DeobfuscatorCliKt"
}
dependencies {
implementation(project(":asm"))
implementation(project(":deob"))
implementation(project(":deob-ir"))
implementation("com.github.ajalt:clikt:${Versions.clikt}")
implementation("com.google.guava:guava:${Versions.guava}")
implementation("org.jgrapht:jgrapht-io:${Versions.jgrapht}")
implementation("org.jgrapht:jgrapht-guava:${Versions.jgrapht}")
}
publishing {
publications.create<MavenPublication>("maven") {
from(components["java"])
pom {
packaging = "jar"
name.set("OpenRS2 Deobfuscator CLI")
description.set(
"""
A command-line interface that provides access to the deobfuscators
analyis, deobfuscation, and decompilation tools.
""".trimIndent()
)
}
}
}

@ -0,0 +1,49 @@
package dev.openrs2.deob.cli
import org.objectweb.asm.ClassReader
import org.objectweb.asm.tree.ClassNode
import java.io.File
import java.io.InputStream
import java.lang.IllegalArgumentException
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
private fun load(input: InputStream) = input.use {
val node = ClassNode()
val reader = ClassReader(input)
reader.accept(node, ClassReader.SKIP_DEBUG)
node
}
interface DeobfuscatorClassLoader {
fun load(name: String): ClassNode
}
object SystemClassLoader : DeobfuscatorClassLoader {
override fun load(name: String): ClassNode {
val classPath = "/${name.replace('.', File.separatorChar)}.class"
val classFile = this.javaClass.getResourceAsStream(classPath)
return load(classFile)
}
}
class ClasspathClassLoader(val classPath: List<Path>) : DeobfuscatorClassLoader {
override fun load(name: String): ClassNode {
val relativePath = Paths.get("/${name.replace('.', File.separatorChar)}.class")
for (entry in classPath) {
val classFilePath = entry.resolve(relativePath)
if (!Files.exists(classFilePath)) {
continue
}
return load(Files.newInputStream(classFilePath))
}
throw IllegalArgumentException("Unable to find class named $name")
}
}

@ -0,0 +1,36 @@
package dev.openrs2.deob.cli
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.subcommands
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.option
import dev.openrs2.deob.cli.ir.PrintCfgCommand
import java.io.File
import java.nio.file.Path
import java.nio.file.Paths
class DeobfuscatorCli : CliktCommand(name = "deob") {
val classpath: String by option(help = "Change the classpath used to resolve class files")
.default("system")
override fun run() {
val loader = when (classpath) {
"system" -> SystemClassLoader
else -> {
val paths : List<Path> = classpath.split(File.pathSeparatorChar).map {
Paths.get(it)
}
val classLoader = ClasspathClassLoader(paths)
classLoader
}
}
currentContext.obj = DeobfuscatorOptions(loader)
}
}
fun main(args: Array<String>) = DeobfuscatorCli()
.subcommands(PrintCfgCommand)
.main(args)

@ -0,0 +1,3 @@
package dev.openrs2.deob.cli
data class DeobfuscatorOptions(val classLoader: DeobfuscatorClassLoader)

@ -0,0 +1,26 @@
package dev.openrs2.deob.cli.ir
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.requireObject
import com.github.ajalt.clikt.parameters.arguments.argument
import dev.openrs2.deob.cli.DeobfuscatorOptions
import dev.openrs2.deob.ir.Method
import dev.openrs2.deob.ir.translation.IrDecompiler
abstract class MethodScopedCommand(command: String) : CliktCommand(name = command) {
val className: String by argument(help = "Fully qualified name of the class name to disassemble")
val methodName: String by argument(help = "Name of the method to print the control flow graph for")
val options by requireObject<DeobfuscatorOptions>()
final override fun run() {
val clazz = PrintCfgCommand.options.classLoader.load(PrintCfgCommand.className)
val method = clazz.methods.find { it.name == PrintCfgCommand.methodName }!!
val decompiler = IrDecompiler(clazz, method)
val ir = decompiler.decompile()
run(ir)
}
abstract fun run(method: Method)
}

@ -0,0 +1,36 @@
package dev.openrs2.deob.cli.ir
import com.google.common.graph.EndpointPair
import dev.openrs2.deob.ir.Method
import dev.openrs2.deob.ir.flow.BasicBlock
import org.jgrapht.Graph
import org.jgrapht.graph.guava.MutableGraphAdapter
import org.jgrapht.nio.DefaultAttribute
import org.jgrapht.nio.GraphExporter
import org.jgrapht.nio.dot.DOTExporter
typealias BlockGraph = Graph<BasicBlock, EndpointPair<BasicBlock>>
typealias BlockGraphExporter = GraphExporter<BasicBlock, EndpointPair<BasicBlock>>
fun dotExporter(): BlockGraphExporter {
val exporter = DOTExporter<BasicBlock, EndpointPair<BasicBlock>>()
exporter.setVertexAttributeProvider {
val label = it.toString().replace("\n", "\\l")
mapOf(
"label" to DefaultAttribute.createAttribute(label)
)
}
return exporter
}
object PrintCfgCommand : MethodScopedCommand("ir-print-cfg") {
override fun run(method: Method) {
val graph: BlockGraph = MutableGraphAdapter(method.cfg)
val exporter: BlockGraphExporter = dotExporter()
exporter.exportGraph(graph, System.out)
}
}

@ -11,6 +11,7 @@ include(
"deob",
"deob-annotations",
"deob-ast",
"deob-cli",
"deob-ir",
"game"
)

Loading…
Cancel
Save