90 lines
2.8 KiB
Kotlin
90 lines
2.8 KiB
Kotlin
/**
|
|
* Rename class and fields using strings from toString() method
|
|
*/
|
|
|
|
import jadx.core.deobf.NameMapper
|
|
import jadx.core.dex.attributes.AFlag
|
|
import jadx.core.dex.attributes.nodes.RenameReasonAttr
|
|
import jadx.core.dex.info.FieldInfo
|
|
import jadx.core.dex.instructions.ConstStringNode
|
|
import jadx.core.dex.instructions.IndexInsnNode
|
|
import jadx.core.dex.instructions.InsnType
|
|
import jadx.core.dex.instructions.args.InsnWrapArg
|
|
import jadx.core.dex.nodes.InsnNode
|
|
import jadx.core.dex.nodes.MethodNode
|
|
import jadx.plugins.script.runtime.data.ScriptOrderedDecompilePass
|
|
|
|
val jadx = getJadxInstance()
|
|
|
|
// StringBuilder chain replaced by STR_CONCAT instruction in SimplifyVisitor
|
|
// Search for return with STR_CONCAT and process args
|
|
jadx.addPass(object : ScriptOrderedDecompilePass(
|
|
jadx,
|
|
"DeobfFromToString",
|
|
runAfter = listOf("SimplifyVisitor"),
|
|
) {
|
|
override fun visit(mth: MethodNode) {
|
|
if (mth.methodInfo.shortId == "toString()Ljava/lang/String;") {
|
|
val returnBlock = mth.exitBlock.predecessors.firstOrNull { it.contains(AFlag.RETURN) }
|
|
val lastInsn = returnBlock?.instructions?.lastOrNull()
|
|
if (lastInsn != null && lastInsn.type == InsnType.RETURN) {
|
|
val arg = lastInsn.getArg(0)
|
|
if (arg.isInsnWrap) {
|
|
val wrapInsn = (arg as InsnWrapArg).wrapInsn
|
|
if (wrapInsn.type == InsnType.STR_CONCAT) {
|
|
log.info { "Renaming using 'toString' in class: ${mth.parentClass}" }
|
|
processArgs(mth, wrapInsn)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
val clsSepRgx = Regex("[ ({:]")
|
|
|
|
private fun processArgs(mth: MethodNode, wrapInsn: InsnNode): Boolean {
|
|
try {
|
|
var fldName: String? = null
|
|
for ((i, arg) in wrapInsn.arguments.withIndex()) {
|
|
val insn = arg.unwrap() ?: return false
|
|
if (i % 2 == 0) {
|
|
if (insn !is ConstStringNode) {
|
|
return false
|
|
}
|
|
var str = insn.string
|
|
if (i == 0) {
|
|
// class and first field name
|
|
val parts = str.split(clsSepRgx)
|
|
val clsName = parts[0]
|
|
if (NameMapper.isValidIdentifier(clsName)) {
|
|
mth.parentClass.run {
|
|
log.info { "rename class '$name' to '$clsName'" }
|
|
rename(clsName)
|
|
RenameReasonAttr.forNode(this).append("from toString()")
|
|
}
|
|
}
|
|
str = parts[1]
|
|
}
|
|
fldName = str.trim('\'', '=', ',', ' ', ':')
|
|
} else {
|
|
if (insn.type != InsnType.IGET) {
|
|
return false
|
|
}
|
|
val iget = insn as IndexInsnNode
|
|
val fldInfo = iget.index as FieldInfo
|
|
val fld = mth.parentClass.searchField(fldInfo)
|
|
if (fld != null && NameMapper.isValidIdentifier(fldName)) {
|
|
log.info { "rename field '${fld.name}' to '$fldName'" }
|
|
fld.rename(fldName)
|
|
RenameReasonAttr.forNode(fld).append("from toString()")
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
} catch (e: Exception) {
|
|
log.error(e) { "Args process failed" }
|
|
return false
|
|
}
|
|
}
|
|
})
|