forked from openrs2/openrs2
Signed-off-by: Gary Tierney <gary.tierney@fastmail.com>feat/deob-ir
parent
bbcdba3b34
commit
1d64a932bb
@ -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) |
||||
} |
||||
} |
Loading…
Reference in new issue