/** * 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 } } })