feat: add base scripting support
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
package jadx.plugins.script
|
||||
|
||||
import jadx.api.plugins.JadxPlugin
|
||||
import jadx.api.plugins.JadxPluginContext
|
||||
import jadx.api.plugins.JadxPluginInfo
|
||||
import jadx.api.plugins.gui.JadxGuiContext
|
||||
import jadx.api.plugins.pass.JadxPassContext
|
||||
import jadx.plugins.script.passes.JadxScriptAfterLoadPass
|
||||
import jadx.plugins.script.runner.ScriptEval
|
||||
|
||||
class JadxScriptPlugin : JadxPlugin {
|
||||
|
||||
override fun getPluginInfo() = JadxPluginInfo("jadx-script", "Jadx Script", "Scripting support for jadx")
|
||||
|
||||
override fun init(init: JadxPluginContext) {
|
||||
val scriptStates = ScriptEval().process(init) ?: return
|
||||
init.passContext.addPass(JadxScriptAfterLoadPass(scriptStates))
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package jadx.plugins.script.passes
|
||||
|
||||
import jadx.api.core.nodes.IJadxDecompiler
|
||||
import jadx.api.plugins.pass.impl.SimpleJadxPassInfo
|
||||
import jadx.api.plugins.pass.types.JadxAfterLoadPass
|
||||
import jadx.plugins.script.runner.ScriptStates
|
||||
import mu.KotlinLogging
|
||||
|
||||
private val LOG = KotlinLogging.logger {}
|
||||
|
||||
class JadxScriptAfterLoadPass(private val scriptStates: ScriptStates) : JadxAfterLoadPass {
|
||||
|
||||
override fun getInfo() = SimpleJadxPassInfo("JadxScriptAfterLoad", "Execute scripts 'afterLoad' block")
|
||||
|
||||
override fun init(decompiler: IJadxDecompiler) {
|
||||
for (script in scriptStates.getScripts()) {
|
||||
if (script.error) {
|
||||
continue
|
||||
}
|
||||
try {
|
||||
for (b in script.scriptData.afterLoad) {
|
||||
b.invoke()
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
script.error = true
|
||||
LOG.error(e) { "Error executing 'afterLoad' block in script: ${script.scriptFile.name}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
package jadx.plugins.script.runner
|
||||
|
||||
import jadx.api.JadxDecompiler
|
||||
import jadx.api.plugins.JadxPluginContext
|
||||
import jadx.api.plugins.pass.JadxPassContext
|
||||
import jadx.plugins.script.runtime.JadxScript
|
||||
import jadx.plugins.script.runtime.JadxScriptData
|
||||
import mu.KotlinLogging
|
||||
import java.io.File
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.host.toScriptSource
|
||||
import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost
|
||||
import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate
|
||||
import kotlin.script.experimental.jvmhost.createJvmEvaluationConfigurationFromTemplate
|
||||
|
||||
private val LOG = KotlinLogging.logger {}
|
||||
|
||||
class ScriptEval {
|
||||
|
||||
fun process(init: JadxPluginContext): ScriptStates? {
|
||||
val jadx = init.decompiler as JadxDecompiler
|
||||
val scripts = jadx.args.inputFiles.filter { f -> f.name.endsWith(".jadx.kts") }
|
||||
if (scripts.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
val scriptStates = ScriptStates()
|
||||
for (scriptFile in scripts) {
|
||||
val scriptData = JadxScriptData(jadx, init, scriptFile)
|
||||
load(scriptFile, scriptData)
|
||||
scriptStates.add(scriptFile, scriptData)
|
||||
}
|
||||
return scriptStates
|
||||
}
|
||||
|
||||
private fun load(scriptFile: File, scriptData: JadxScriptData) {
|
||||
LOG.debug { "Loading script: ${scriptFile.absolutePath}" }
|
||||
val result = eval(scriptFile, scriptData)
|
||||
processEvalResult(result, scriptFile)
|
||||
}
|
||||
|
||||
private fun eval(scriptFile: File, scriptData: JadxScriptData): ResultWithDiagnostics<EvaluationResult> {
|
||||
val compilationConf = createJvmCompilationConfigurationFromTemplate<JadxScript>()
|
||||
val evalConf = createJvmEvaluationConfigurationFromTemplate<JadxScript> {
|
||||
constructorArgs(scriptData)
|
||||
}
|
||||
return BasicJvmScriptingHost().eval(scriptFile.toScriptSource(), compilationConf, evalConf)
|
||||
}
|
||||
|
||||
private fun processEvalResult(res: ResultWithDiagnostics<EvaluationResult>, scriptFile: File) {
|
||||
when (res) {
|
||||
is ResultWithDiagnostics.Success -> {
|
||||
val result = res.value.returnValue
|
||||
if (result is ResultValue.Error) {
|
||||
result.error.printStackTrace()
|
||||
}
|
||||
}
|
||||
is ResultWithDiagnostics.Failure -> {
|
||||
LOG.error { "Script execution failed: ${scriptFile.name}" }
|
||||
res.reports
|
||||
.filter { it.severity >= ScriptDiagnostic.Severity.ERROR }
|
||||
.forEach { r ->
|
||||
LOG.error(r.exception) { r.render(withSeverity = false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package jadx.plugins.script.runner
|
||||
|
||||
import jadx.plugins.script.runtime.JadxScriptData
|
||||
import java.io.File
|
||||
|
||||
data class ScriptStateData(
|
||||
val scriptFile: File,
|
||||
val scriptData: JadxScriptData,
|
||||
var error: Boolean = false
|
||||
)
|
||||
|
||||
class ScriptStates {
|
||||
|
||||
private val data: MutableList<ScriptStateData> = ArrayList()
|
||||
|
||||
fun add(scriptFile: File, scriptData: JadxScriptData) {
|
||||
data.add(ScriptStateData(scriptFile, scriptData))
|
||||
}
|
||||
|
||||
fun getScripts() = data
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
jadx.plugins.script.JadxScriptPlugin
|
||||
Reference in New Issue
Block a user