feat: add base scripting support

This commit is contained in:
Skylot
2022-07-09 17:57:44 +01:00
parent fdf170529f
commit e5e64365fc
64 changed files with 1488 additions and 53 deletions
@@ -0,0 +1,21 @@
// custom deobfuscator example
val jadx = getJadxInstance()
jadx.args.isDeobfuscationOn = false
jadx.args.renameFlags = emptySet()
val regex = """[Oo0]+""".toRegex()
var n = 0
jadx.rename.all { name, node ->
when {
name matches regex -> {
val newName = "${node.typeName()}${n++}"
println("renaming ${node.typeName()} '$node' to '$newName'")
newName
}
else -> null
}
}
jadx.afterLoad {
println("Renames count: $n")
}
@@ -0,0 +1,9 @@
// customize jadx-gui
val jadx = getJadxInstance()
jadx.gui.ifAvailable {
addMenuAction("Decompile All") {
jadx.decompile.all()
}
}
@@ -0,0 +1,29 @@
// logger is preferred for output
log.info { "Hello from jadx script!" }
// println will also work (will be redirected to logger)
println("println from script '$scriptName'")
// get jadx decompiler script instance
val jadx = getJadxInstance()
// adjust options if needed
jadx.args.isDeobfuscationOn = false
// change names
jadx.rename.all { name ->
when (name) {
"HelloWorld" -> "HelloJadx"
else -> null
}
}
// run some code after loading is finished
jadx.afterLoad {
println("Loaded classes: ${jadx.classes.size}")
// print first class code
jadx.classes.firstOrNull()?.let { cls ->
println("Class: '${cls.name}'")
println(cls.code)
}
}
@@ -0,0 +1,19 @@
// instructions modification example
import jadx.core.dex.instructions.ConstStringNode
import jadx.core.dex.instructions.InvokeNode
import jadx.core.dex.instructions.args.InsnArg
val jadx = getJadxInstance()
jadx.replace.insns { mth, insn ->
if (insn is InvokeNode) {
if (insn.callMth.shortId == "println(Ljava/lang/String;)V") {
val arg = insn.getArg(1)
val newArg = InsnArg.wrapInsnIntoArg(ConstStringNode("Jadx!"))
insn.setArg(1, newArg)
log.info { "Replace '$arg' with '$newArg' in $mth" }
}
}
null
}
@@ -0,0 +1,64 @@
// insert processing passes for different decompilation stages
import jadx.core.dex.instructions.InsnType
import jadx.core.dex.nodes.IRegion
import java.lang.Integer.max
val jadx = getJadxInstance()
// print raw instructions
jadx.stages.rawInsns { mth, insns ->
log.info { "Instructions for method: $mth" }
for ((offset, insn) in insns.withIndex()) {
insn?.let {
log.info { " 0x${offset.hex()}: $insn" }
}
}
}
// access method basic blocks
jadx.stages.mthBlocks { mth, blocks ->
// count invoke instructions
var invCount = 0
for (block in blocks) {
for (insn in block.instructions) {
if (insn.type == InsnType.INVOKE) {
invCount++
}
}
}
log.info { "Invokes count in method $mth = $invCount" }
}
// access method regions
jadx.stages.mthRegions { mth, region ->
// recursively count max depth of nested regions
fun countRegionsDepth(region: IRegion): Int {
val subBlocks = region.subBlocks
if (subBlocks.isEmpty()) {
return 0
}
var depth = 1
for (block in subBlocks) {
if (block is IRegion) {
depth = max(depth, 1 + countRegionsDepth(block))
}
}
return depth
}
val depth = countRegionsDepth(region)
log.info { "Max region depth in method $mth = $depth" }
if (depth > 5) {
jadx.debug.printMethodRegions(mth, printInsns = true)
}
}
jadx.afterLoad {
/*
Start full decompilation (optional):
1. jadx-cli start decompilation automatically
2. jadx-gui start decompilation only on class open or search, so you might need to force it
*/
// jadx.decompile.all()
}