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