diff --git a/build.gradle b/build.gradle index f23af76..0797105 100644 --- a/build.gradle +++ b/build.gradle @@ -62,4 +62,4 @@ dependencies { compileResolvable 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0-Beta' implementation 'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.5.0' api 'org.apache.commons:commons-text:1.10.0' -} \ No newline at end of file +} diff --git a/src/main/kotlin/com/yoavst/jeb/bridge/UIBridge.kt b/src/main/kotlin/com/yoavst/jeb/bridge/UIBridge.kt index 0e29039..e26cc46 100644 --- a/src/main/kotlin/com/yoavst/jeb/bridge/UIBridge.kt +++ b/src/main/kotlin/com/yoavst/jeb/bridge/UIBridge.kt @@ -2,7 +2,6 @@ package com.yoavst.jeb.bridge import com.pnfsoftware.jeb.client.api.IGraphicalClientContext import com.pnfsoftware.jeb.core.output.IItem -import com.pnfsoftware.jeb.core.units.code.android.dex.IDexItem import com.pnfsoftware.jeb.core.units.code.android.dex.IDexMethod import com.pnfsoftware.jeb.core.units.code.android.dex.IDexType import com.yoavst.jeb.utils.currentFocusedMethod diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgMassRenaming.kt b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgMassRenaming.kt index f496e6c..4e53d8a 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgMassRenaming.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgMassRenaming.kt @@ -12,6 +12,7 @@ import com.yoavst.jeb.utils.* import com.yoavst.jeb.utils.renaming.RenameEngine import com.yoavst.jeb.utils.renaming.RenameReason import com.yoavst.jeb.utils.renaming.RenameRequest +import java.util.stream.Collectors class ConstArgMassRenaming( private val renamers: Map, @@ -24,13 +25,13 @@ class ConstArgMassRenaming( fun processUnit(unit: IDexUnit, renameEngine: RenameEngine) { val decompiler = unit.decompilerRef - var seq = getXrefs(unit).asSequence() - seq = if (isOperatingOnlyOnThisClass) { - seq.filter { it.classType == UIBridge.currentClass } + var stream = getXrefs(unit).parallelStream() + stream = if (isOperatingOnlyOnThisClass) { + stream.filter { it.classType == UIBridge.currentClass } } else { - seq.filter { classFilter.matches(it.classType.implementingClass) } + stream.filter { classFilter.matches(it.classType.implementingClass) } } - seq.forEach { processMethod(it, unit, decompiler, renameEngine) } + stream.forEach { processMethod(it, unit, decompiler, renameEngine) } } fun propagate(unit: IDexUnit, renameEngine: RenameEngine) { @@ -86,14 +87,17 @@ class ConstArgMassRenaming( throw e } } + element is IJavaNew && element.constructorSignature in renamers -> { // the method we were looking for was a constructor processMatchedMethod(assignee, renamers[element.constructorSignature]!!) { element.arguments[it] } } + recursive -> { // Try sub elements element.subElements.forEach { traverseElement(it, assignee) } } + else -> { } } @@ -150,6 +154,7 @@ class ConstArgMassRenaming( } renameEngine.renameField(request, field, cls) } + is IJavaInstanceField -> { val field = element.field ?: run { logger.warning("Failed to get field: $element") @@ -157,9 +162,11 @@ class ConstArgMassRenaming( } renameEngine.renameField(request, field, cls) } + is IJavaIdentifier -> { renameEngine.renameIdentifier(request, element, unit) } + is IJavaCall -> { // maybe toString or valueOf on argument if (element.methodName == "toString" || element.methodName == "valueOf") { @@ -169,6 +176,7 @@ class ConstArgMassRenaming( return } } + else -> { logger.debug("Unsupported argument type: ${element.elementType}") return @@ -220,10 +228,22 @@ class ConstArgMassRenaming( } - private fun getXrefs(unit: IDexUnit): Set = renamers.keys.asSequence().mapNotNull(unit::getMethod).onEach { - logger.info("Found method: ${it.currentSignature}") - }.mapToPair(unit::xrefsFor).onEach { (method, xrefs) -> - if (xrefs.isNotEmpty()) - logger.info("Found ${xrefs.size} xrefs for method: ${method.currentSignature}") - }.flatMap { it.second }.mapTo(mutableSetOf(), unit::getMethod) -} \ No newline at end of file + + private fun getXrefs(unit: IDexUnit): Set = renamers + .keys + .parallelStream() + .mapNotNull(unit::getMethod) + .peek { + logger.info("Found method: ${it.currentSignature}") + } + .mapToPair(unit::xrefsFor) + .peek { (method, xrefs) -> + if (xrefs.isNotEmpty()) + logger.info("Found ${xrefs.size} xrefs for method: ${method.currentSignature}") + } + .flatMap { (method, xrefs) -> + xrefs.stream() + } + .map(unit::getMethod) + .collect(Collectors.toSet()) +} diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgMassRenamingPlugin.kt b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgMassRenamingPlugin.kt index 32987cf..a46ebb9 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgMassRenamingPlugin.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgMassRenamingPlugin.kt @@ -1,6 +1,9 @@ package com.yoavst.jeb.plugins.constarg -import com.pnfsoftware.jeb.core.* +import com.pnfsoftware.jeb.core.BooleanOptionDefinition +import com.pnfsoftware.jeb.core.IOptionDefinition +import com.pnfsoftware.jeb.core.IPluginInformation +import com.pnfsoftware.jeb.core.PluginInformation import com.pnfsoftware.jeb.core.units.code.android.IDexUnit import com.yoavst.jeb.bridge.UIBridge import com.yoavst.jeb.plugins.JEB_VERSION @@ -12,26 +15,26 @@ import com.yoavst.jeb.utils.renaming.RenameEngine import java.io.File class ConstArgMassRenamingPlugin : - BasicEnginesPlugin( - supportsClassFilter = true, - defaultForScopeOnThisClass = false, - defaultForScopeOnThisFunction = false, - ) { + BasicEnginesPlugin( + supportsClassFilter = true, + defaultForScopeOnThisClass = false, + defaultForScopeOnThisFunction = false, + ) { private lateinit var signatures: Map override fun getPluginInformation(): IPluginInformation = PluginInformation( - "Const arg mass renaming plugin", - "Fire the plugin to change names using information from a constant string argument to function", - "Yoav Sternberg", - PLUGIN_VERSION, - JEB_VERSION, - null + "Const arg mass renaming plugin", + "Fire the plugin to change names using information from a constant string argument to function", + "Yoav Sternberg", + PLUGIN_VERSION, + JEB_VERSION, + null ) override fun getExecutionOptionDefinitions(): List { return super.getExecutionOptionDefinitions() + BooleanOptionDefinition( - USE_BUILTIN, - true, - """Use the builtin method signature list. It supports Bundle, Intent, ContentValues and shared preferences. + USE_BUILTIN, + true, + """Use the builtin method signature list. It supports Bundle, Intent, ContentValues and shared preferences. If you have a suggestion to add to the global list, Please contact Yoav Sternberg.""" ) } @@ -53,14 +56,14 @@ If you have a suggestion to add to the global list, Please contact Yoav Sternber } } else { signatures = - RenameSignaturesFileParser.parseSignatures(javaClass.classLoader.getResource("rename_signatures.md")!!.readText(), ".") + RenameSignaturesFileParser.parseSignatures(javaClass.classLoader.getResource("rename_signatures.md")!!.readText(), ".") } return true } override fun processUnit(unit: IDexUnit, renameEngine: RenameEngine) { val massRenamer = ConstArgMassRenaming( - signatures, isOperatingOnlyOnThisClass, classFilter + signatures, isOperatingOnlyOnThisClass, classFilter ) if (isOperatingOnlyOnThisMethod) { diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgRenamingPlugin.kt b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgRenamingPlugin.kt index 9ea66a2..1fb94ed 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgRenamingPlugin.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/ConstArgRenamingPlugin.kt @@ -1,8 +1,6 @@ package com.yoavst.jeb.plugins.constarg import com.pnfsoftware.jeb.core.* -import com.pnfsoftware.jeb.core.units.NotificationType -import com.pnfsoftware.jeb.core.units.UnitNotification import com.pnfsoftware.jeb.core.units.code.android.IDexUnit import com.pnfsoftware.jeb.core.units.code.android.dex.IDexMethod import com.yoavst.jeb.bridge.UIBridge @@ -17,12 +15,12 @@ import java.io.File import kotlin.properties.Delegates class ConstArgRenamingPlugin : - BasicEnginesPlugin( - supportsClassFilter = true, - defaultForScopeOnThisClass = false, - defaultForScopeOnThisFunction = false, - usingSelectedMethod = true - ) { + BasicEnginesPlugin( + supportsClassFilter = true, + defaultForScopeOnThisClass = false, + defaultForScopeOnThisFunction = false, + usingSelectedMethod = true + ) { private var constArgumentIndex by Delegates.notNull() private var renamedArgumentIndex = -1 @@ -30,30 +28,30 @@ class ConstArgRenamingPlugin : private lateinit var renamer: (String) -> RenameResult override fun getPluginInformation(): IPluginInformation = PluginInformation( - "Const arg renaming plugin", - "Fire the plugin to change names using information from a constant string argument to function", - "Yoav Sternberg", - PLUGIN_VERSION, - JEB_VERSION, - null + "Const arg renaming plugin", + "Fire the plugin to change names using information from a constant string argument to function", + "Yoav Sternberg", + PLUGIN_VERSION, + JEB_VERSION, + null ) override fun getExecutionOptionDefinitions(): List { val options = super.getExecutionOptionDefinitions().toMutableList() options += ListOptionDefinition( - TargetRenameTag, - RenameTarget.Class.name, - "What do we want to renamed based on the argument", - *RenameTarget.values().map(RenameTarget::name).toTypedArray() + TargetRenameTag, + RenameTarget.Class.name, + "What do we want to renamed based on the argument", + *RenameTarget.values().map(RenameTarget::name).toTypedArray() ) options += OptionDefinition( - TargetConstArgIndex, - "0", - "What is the index of the const argument that will be used as name?" + TargetConstArgIndex, + "0", + "What is the index of the const argument that will be used as name?" ) options += OptionDefinition( - TargetArgumentToBeRenamedPosition, - "Optional: If renaming an argument, what is its index" + TargetArgumentToBeRenamedPosition, + "Optional: If renaming an argument, what is its index" ) return options } @@ -99,6 +97,7 @@ class ConstArgRenamingPlugin : } scriptRenamer(File(scriptPath).readText()) } + RenameTarget.Class -> classRenamer RenameTarget.Method -> methodRenamer RenameTarget.Argument -> { @@ -112,6 +111,7 @@ class ConstArgRenamingPlugin : } argumentRenamer } + RenameTarget.Assignee -> assigneeRenamer } return true @@ -119,9 +119,9 @@ class ConstArgRenamingPlugin : override fun processUnit(unit: IDexUnit, renameEngine: RenameEngine) { val massRenamer = ConstArgMassRenaming( - mapOf( - renameMethod.getSignature(false) to ExtendedRenamer(constArgumentIndex, renamer, renamedArgumentIndex) - ), isOperatingOnlyOnThisClass, classFilter + mapOf( + renameMethod.getSignature(false) to ExtendedRenamer(constArgumentIndex, renamer, renamedArgumentIndex) + ), isOperatingOnlyOnThisClass, classFilter ) if (isOperatingOnlyOnThisMethod) { diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/GetXPlugin.kt b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/GetXPlugin.kt index 5112fdf..469ebd1 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/GetXPlugin.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/GetXPlugin.kt @@ -9,6 +9,7 @@ import com.yoavst.jeb.plugins.PLUGIN_VERSION import com.yoavst.jeb.utils.* import com.yoavst.jeb.utils.renaming.RenameEngine import java.util.* +import java.util.stream.Collectors class GetXPlugin : BasicEnginesPlugin(supportsClassFilter = true, defaultForScopeOnThisClass = false) { override fun getPluginInformation(): IPluginInformation = PluginInformation( @@ -21,10 +22,12 @@ class GetXPlugin : BasicEnginesPlugin(supportsClassFilter = true, defaultForScop ) override fun processUnit(unit: IDexUnit, renameEngine: RenameEngine) { - val visitor = simpleNameMethodVisitor(renameEngine) - val renamers = unit.methods.asSequence().mapToPairNotNull(visitor).associate { (method, result) -> - method.currentSignature to result - } + val visitor: (IDexMethod) -> ExtendedRenamer? = simpleNameMethodVisitor(renameEngine) + + val renamers = unit.methods.parallelStream().mapToPairNotNull(visitor).filter { (method, result) -> + result != null + }.collect(Collectors.toMap({ (method, result) -> method.currentSignature }, { (method, result) -> result })) + ConstArgMassRenaming(renamers, isOperatingOnlyOnThisClass, classFilter, recursive = false).processUnit(unit, renameEngine) } @@ -38,9 +41,11 @@ class GetXPlugin : BasicEnginesPlugin(supportsClassFilter = true, defaultForScop method.parameterTypes.size == 0 -> { ExtendedRenamer(-1, { RenameResult(assigneeName = fieldName) }, -1) } + method.isStatic && method.parameterTypes.size == 1 -> { ExtendedRenamer(-1, { RenameResult(argumentName = fieldName) }, 0) } + else -> null } } else if (name.startsWith("set") && name.length >= 4) { @@ -49,9 +54,11 @@ class GetXPlugin : BasicEnginesPlugin(supportsClassFilter = true, defaultForScop method.parameterTypes.size == 1 -> { ExtendedRenamer(-1, { RenameResult(argumentName = fieldName) }, 0) } + method.isStatic && method.parameterTypes.size == 2 -> { ExtendedRenamer(-1, { RenameResult(argumentName = fieldName) }, 1) } + else -> null } } else null diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/RenameResult.kt b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/RenameResult.kt index 0f17d66..ff96463 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/RenameResult.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/RenameResult.kt @@ -1,8 +1,8 @@ package com.yoavst.jeb.plugins.constarg data class RenameResult( - val className: String? = null, - val methodName: String? = null, - val argumentName: String? = null, - val assigneeName: String? = null + val className: String? = null, + val methodName: String? = null, + val argumentName: String? = null, + val assigneeName: String? = null ) diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/RenameSignaturesFileParser.kt b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/RenameSignaturesFileParser.kt index 5b3c021..b0549bc 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/RenameSignaturesFileParser.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/RenameSignaturesFileParser.kt @@ -22,8 +22,8 @@ object RenameSignaturesFileParser { } val constArgIndex = split[2].toIntOrNull() ?: throw IllegalArgumentException("Invalid line: '$it'") val target = RenameTarget.valueOf( - split[0].lowercase(Locale.getDefault()) - .replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }) + split[0].lowercase(Locale.getDefault()) + .replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }) split[1] to when (target) { RenameTarget.Class -> ExtendedRenamer(constArgIndex, classRenamer) RenameTarget.Method -> ExtendedRenamer(constArgIndex, methodRenamer) @@ -32,9 +32,11 @@ object RenameSignaturesFileParser { throw IllegalArgumentException("Invalid line, no renamed argument index: '$it'") } val renamedArgIndex = - split[3].toIntOrNull() ?: throw IllegalArgumentException("Invalid line, renamed argument is not int: '$it'") + split[3].toIntOrNull() + ?: throw IllegalArgumentException("Invalid line, renamed argument is not int: '$it'") ExtendedRenamer(constArgIndex, argumentRenamer, renamedArgIndex) } + RenameTarget.Assignee -> ExtendedRenamer(constArgIndex, assigneeRenamer) RenameTarget.Custom -> { if (split.size < 4) { @@ -42,15 +44,17 @@ object RenameSignaturesFileParser { } val filename = split[3] val script = if (filename.startsWith("jar:")) { - javaClass.classLoader.getResourceAsStream(filename.substringAfter("jar:"))?.bufferedReader()?.readText() ?: run { - throw IllegalArgumentException("Invalid line, no such file in jar: '$it'") - } + javaClass.classLoader.getResourceAsStream(filename.substringAfter("jar:"))?.bufferedReader()?.readText() + ?: run { + throw IllegalArgumentException("Invalid line, no such file in jar: '$it'") + } } else { File(basePath, split[3]).readText() } if (split.size == 5) { val renamedArgIndex = - split[4].toIntOrNull() ?: throw IllegalArgumentException("Invalid line, renamed argument is not int: '$it'") + split[4].toIntOrNull() + ?: throw IllegalArgumentException("Invalid line, renamed argument is not int: '$it'") ExtendedRenamer(constArgIndex, scriptRenamer(script), renamedArgIndex) } else { ExtendedRenamer(constArgIndex, scriptRenamer(script)) diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/Renamers.kt b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/Renamers.kt index 3872902..86e9703 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/constarg/Renamers.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/constarg/Renamers.kt @@ -15,7 +15,7 @@ val assigneeRenamer: (String) -> RenameResult = { RenameResult(assigneeName = it fun scriptRenamer(script: String): (String) -> RenameResult { val baseInterpreter = PythonInterpreter().apply { exec( - """ + """ def split2(s, sep, at_least): '''Split the string using separator. Result array will be at least the given length.''' arr = s.split(sep) @@ -35,10 +35,10 @@ fun scriptRenamer(script: String): (String) -> RenameResult { baseInterpreter[INPUT] = tag baseInterpreter.exec(script) RenameResult( - baseInterpreter[CLASS]?.asStringOrNull(), - baseInterpreter[METHOD]?.asStringOrNull(), - baseInterpreter[ARGUMENT]?.asStringOrNull(), - baseInterpreter[ASSIGNEE]?.asStringOrNull() + baseInterpreter[CLASS]?.asStringOrNull(), + baseInterpreter[METHOD]?.asStringOrNull(), + baseInterpreter[ARGUMENT]?.asStringOrNull(), + baseInterpreter[ASSIGNEE]?.asStringOrNull() ) } diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/BaseDalvikSimulator.kt b/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/BaseDalvikSimulator.kt index fb5f435..47fcd2f 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/BaseDalvikSimulator.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/BaseDalvikSimulator.kt @@ -92,7 +92,7 @@ abstract class BaseDalvikSimulator(protected val clazz: IDexClass, protected val } protected fun IDexType.isSubClass(): Boolean = - implementingClass?.supertypes?.any { it.index == classIndex } == true + implementingClass?.supertypes?.any { it.index == classIndex } == true //endregion } diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/EnumRenamingPlugin.kt b/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/EnumRenamingPlugin.kt index e49e83e..7e75df8 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/EnumRenamingPlugin.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/EnumRenamingPlugin.kt @@ -25,12 +25,12 @@ cls.a = temp */ class EnumRenamingPlugin : BasicEnginesPlugin(supportsClassFilter = true, defaultForScopeOnThisClass = false) { override fun getPluginInformation(): IPluginInformation = PluginInformation( - "Enum fields renaming", - "Fire the plugin to change obfuscated enum field names to their real name if available", - "Yoav Sternberg", - PLUGIN_VERSION, - JEB_VERSION, - null + "Enum fields renaming", + "Fire the plugin to change obfuscated enum field names to their real name if available", + "Yoav Sternberg", + PLUGIN_VERSION, + JEB_VERSION, + null ) override fun processUnit(unit: IDexUnit, renameEngine: RenameEngine) { @@ -39,7 +39,7 @@ class EnumRenamingPlugin : BasicEnginesPlugin(supportsClassFilter = true, defaul processClass(cls, renameEngine) } else { unit.subclassesOf("Ljava/lang/Enum;").filter(classFilter::matches) - .forEach { processClass(it, renameEngine) } + .forEach { processClass(it, renameEngine) } } unit.refresh() } diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/MultiEnumStaticConstructorSimulator.kt b/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/MultiEnumStaticConstructorSimulator.kt index c363a03..fa7cebc 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/MultiEnumStaticConstructorSimulator.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/MultiEnumStaticConstructorSimulator.kt @@ -9,10 +9,10 @@ import com.yoavst.jeb.utils.renaming.RenameReason import com.yoavst.jeb.utils.renaming.RenameRequest class MultiEnumStaticConstructorSimulator( - clazz: IDexClass, - constructors: List, - renameEngine: RenameEngine -): BaseDalvikSimulator(clazz, renameEngine) { + clazz: IDexClass, + constructors: List, + renameEngine: RenameEngine +) : BaseDalvikSimulator(clazz, renameEngine) { private val constructorIndices = constructors.mapTo(mutableSetOf()) { it.prototypeIndex } override fun onInvokeDirect(instruction: IDalvikInstruction) { @@ -27,9 +27,9 @@ class MultiEnumStaticConstructorSimulator( } else { // the enum constructor was moved to a subclass val singleEnumConstructorSimulator = SingleEnumConstructorSimulator( - invokedMethod.classType.implementingClass!!, - constructorIndices, - renameEngine + invokedMethod.classType.implementingClass!!, + constructorIndices, + renameEngine ) singleEnumConstructorSimulator.run(invokedMethod) if (singleEnumConstructorSimulator.name != null) { @@ -90,7 +90,7 @@ class MultiEnumStaticConstructorSimulator( return } trace { "Renaming $field to ${value.value}" } - renameEngine.renameField(RenameRequest(value.value!!, RenameReason.EnumName), field, clazz) + renameEngine.renameField(RenameRequest(value.value!!, RenameReason.EnumName), field, clazz) } is RegisterValue.StringValue, null -> { diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/SingleEnumConstructorSimulator.kt b/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/SingleEnumConstructorSimulator.kt index 11c871a..bb4bafd 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/SingleEnumConstructorSimulator.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/enumsupport/SingleEnumConstructorSimulator.kt @@ -6,7 +6,7 @@ import com.pnfsoftware.jeb.core.units.code.android.dex.IDexMethod import com.yoavst.jeb.utils.renaming.RenameEngine class SingleEnumConstructorSimulator(clazz: IDexClass, private val superInits: Set, renameEngine: RenameEngine) : - BaseDalvikSimulator(clazz, renameEngine) { + BaseDalvikSimulator(clazz, renameEngine) { var name: String? = null private var thisInstance = RegisterValue.EnumInstanceValue(null) diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/kotlin/IntrinsicsMethodDetector.kt b/src/main/kotlin/com/yoavst/jeb/plugins/kotlin/IntrinsicsMethodDetector.kt index e95f778..17ebedd 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/kotlin/IntrinsicsMethodDetector.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/kotlin/IntrinsicsMethodDetector.kt @@ -73,12 +73,15 @@ object IntrinsicsMethodDetector { method.parameterTypes[0].originalSignature == "Ljava/lang/Throwable;" -> { return IntrinsicDetectionResult("sanitizeStackTrace") } + method.parameterTypes[0].originalSignature == "Ljava/lang/Object;" -> { return IntrinsicDetectionResult("checkNotNull") } + method.returnType.originalSignature == "Ljava/lang/String;" -> { return IntrinsicDetectionResult("createParameterIsNullExceptionMessage") } + else -> { @Suppress("UNCHECKED_CAST") val instructions = method.instructions as List @@ -99,6 +102,7 @@ object IntrinsicsMethodDetector { IntrinsicDetectionResult("throwParameterIsNullIAE") } } + "Ljava/lang/NullPointerException;" -> { return if (method.genericFlags and ICodeItem.FLAG_PUBLIC != 0) { IntrinsicDetectionResult("throwJavaNpe") @@ -145,9 +149,11 @@ object IntrinsicsMethodDetector { "Z" -> { return IntrinsicDetectionResult("areEqual") } + "I" -> { return IntrinsicDetectionResult("compare") } + "Ljava/lang/Throwable;" -> { return IntrinsicDetectionResult("sanitizeStackTrace") } @@ -159,11 +165,13 @@ object IntrinsicsMethodDetector { "Ljava/lang/String;" -> { return IntrinsicDetectionResult("checkHasClass") } + "Ljava/lang/Object;" -> { return IntrinsicDetectionResult("stringPlus") } } } + "I" -> { if (method.parameterTypes[1].signature == "Ljava/lang/String;") { return IntrinsicDetectionResult("reifiedOperationMarker") diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/kotlin/KotlinAnnotationPlugin.kt b/src/main/kotlin/com/yoavst/jeb/plugins/kotlin/KotlinAnnotationPlugin.kt index d6ba0eb..f31b543 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/kotlin/KotlinAnnotationPlugin.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/kotlin/KotlinAnnotationPlugin.kt @@ -47,7 +47,7 @@ class KotlinAnnotationPlugin : BasicEnginesPlugin(supportsClassFilter = true, de null ) - override fun getExecutionOptionDefinitions(): List { + override fun getExecutionOptionDefinfitions(): List { return super.getExecutionOptionDefinitions() + OptionDefinition( KOTLIN_METADATA_SIGNATURE, "Lkotlin/Metadata;", @@ -148,7 +148,7 @@ class KotlinAnnotationPlugin : BasicEnginesPlugin(supportsClassFilter = true, de val annotationTypeIndex = annotationClass.classTypeIndex - var seq = unit.classes.asSequence() + var seq = unit.classes.parallelStream() seq = if (isOperatingOnlyOnThisClass) { seq.filter { it.classType == UIBridge.currentClass } } else { @@ -183,8 +183,8 @@ class KotlinAnnotationPlugin : BasicEnginesPlugin(supportsClassFilter = true, de if (KOTLIN_METADATA_COMMENT_PREFIX !in originalComment) { val comment = header.toStringBlock() if (originalComment.isBlank()) { - unit.setCommentBackport(cls.currentSignature, comment) - } else { + unit.setCommentBackport(cls.currentSignature, comment) + } else { unit.setCommentBackport(cls.currentSignature, originalComment + "\n\n" + comment) } } @@ -195,6 +195,7 @@ class KotlinAnnotationPlugin : BasicEnginesPlugin(supportsClassFilter = true, de val name = classInfo.name.split("/").last().replace(".", "$") renameEngine.renameClass(RenameRequest(name, RenameReason.KotlinName), cls) } + is KotlinClassMetadata.FileFacade -> Unit is KotlinClassMetadata.SyntheticClass -> Unit is KotlinClassMetadata.MultiFileClassFacade -> Unit @@ -202,6 +203,7 @@ class KotlinAnnotationPlugin : BasicEnginesPlugin(supportsClassFilter = true, de is KotlinClassMetadata.Unknown -> { logger.warning("Encountered unknown class: ${cls.currentName}. It probably means the metadata lib version is old.") } + null -> Unit } } diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/resourcesname/ResourcesNamePlugin.kt b/src/main/kotlin/com/yoavst/jeb/plugins/resourcesname/ResourcesNamePlugin.kt index 618b2ab..d57baff 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/resourcesname/ResourcesNamePlugin.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/resourcesname/ResourcesNamePlugin.kt @@ -30,19 +30,19 @@ import javax.xml.parsers.DocumentBuilderFactory * should we add xrefs support for this case? */ class ResourcesNamePlugin : BasicEnginesPlugin( - supportsClassFilter = true, defaultForScopeOnThisClass = false, - defaultForScopeOnThisFunction = false + supportsClassFilter = true, defaultForScopeOnThisClass = false, + defaultForScopeOnThisFunction = false ) { private lateinit var resources: MultiMap private lateinit var intToResourceId: Map override fun getPluginInformation(): IPluginInformation = PluginInformation( - "Resources name restore", - "Fire the plugin to recreate R class and replace constants in code", - "Yoav Sternberg", - PLUGIN_VERSION, - JEB_VERSION, - null + "Resources name restore", + "Fire the plugin to recreate R class and replace constants in code", + "Yoav Sternberg", + PLUGIN_VERSION, + JEB_VERSION, + null ) override fun preProcess(): Boolean { @@ -76,14 +76,19 @@ class ResourcesNamePlugin : BasicEnginesPlugin( processClass(UIBridge.currentClass!!.implementingClass!!, decompiler, renameEngine) } } else { - unit.classes.asSequence().filter(classFilter::matches).onEach { - processClass(it, decompiler, renameEngine) - }.flatMap(IDexClass::getMethods) - .forEach { preProcessMethod(it, unit, decompiler, renameEngine) } + unit.classes.parallelStream() + .filter(classFilter::matches) + .peek { processClass(it, decompiler, renameEngine) } + .forEach { + it.methods.parallelStream().forEach { + preProcessMethod(it, unit, decompiler, renameEngine) + } + } } } private fun processClass(cls: IDexClass, decompiler: IDexDecompilerUnit, renameEngine: RenameEngine) { + // Take a class, check if it has static fields which can be converted to resource names. // check if we have a static field which is a resource cls.fields.asSequence().filter { it.staticInitializer?.type == IDexValue.VALUE_INT }.forEach { field -> val intValue = field.staticInitializer!!.int @@ -94,6 +99,7 @@ class ResourcesNamePlugin : BasicEnginesPlugin( } private fun preProcessMethod(method: IDexMethod, unit: IDexUnit, decompiler: IDexDecompilerUnit, engine: RenameEngine) { + // Check if a method uses a resource ID and if yes decompile it and rename if (method.isInternal && method.instructions != null) { @Suppress("UNCHECKED_CAST") val instructions = method.instructions as List @@ -143,6 +149,7 @@ class ResourcesNamePlugin : BasicEnginesPlugin( val resource = intToResourceId[value] ?: continue resource.addXref(method, unit, packageSignature, instruction) } + instruction.opcode == DalvikInstructionOpcodes.OP_FILL_ARRAY_DATA -> { // fill-array-data REGISTER, ARRAY_DATA for (element in instruction.arrayData.elements) { @@ -158,6 +165,7 @@ class ResourcesNamePlugin : BasicEnginesPlugin( } } } + instruction.isSwitch -> { // switch data consists of pairs (value, address) for ((value, _) in instruction.switchData.elements) { @@ -171,9 +179,9 @@ class ResourcesNamePlugin : BasicEnginesPlugin( private fun ResourceId.addXref(method: IDexMethod, unit: IDexUnit, packageSignature: String, instruction: IDalvikInstruction) { unit.referenceManager.addFieldReference( - toField(packageSignature), - "${method.signature}+${instruction.offset}", - DexReferenceType.GET + toField(packageSignature), + "${method.signature}+${instruction.offset}", + DexReferenceType.GET ) } @@ -207,7 +215,7 @@ class ResourcesNamePlugin : BasicEnginesPlugin( } private fun parseAndroidPublicXml(): Pair, Map> = - javaClass.classLoader.getResourceAsStream("aosp_public.xml")!!.toXmlDocument().parsePublicXml(isSystem = true) + javaClass.classLoader.getResourceAsStream("aosp_public.xml")!!.toXmlDocument().parsePublicXml(isSystem = true) private fun InputStream.toXmlDocument(): Document { val factory = DocumentBuilderFactory.newInstance() diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/sourcefile/SourceFilePlugin.kt b/src/main/kotlin/com/yoavst/jeb/plugins/sourcefile/SourceFilePlugin.kt index f1f5037..4326cb2 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/sourcefile/SourceFilePlugin.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/sourcefile/SourceFilePlugin.kt @@ -49,18 +49,39 @@ class SourceFilePlugin : BasicEnginesPlugin(supportsClassFilter = true, defaultF if (!shouldAddComment && !shouldAddToTypeName) return - var seq = unit.classes.asSequence() - seq = if (isOperatingOnlyOnThisClass) { - seq.filter { it.classType == UIBridge.currentClass } + var i = 0 + if (isOperatingOnlyOnThisClass) { + unit.classes + .parallelStream() + .filter { it.classType == UIBridge.currentClass } + .filter { it.sourceStringIndex != -1 } + .forEach { + i++ + processClass(unit, it, renameEngine) + } } else { - seq.filter(classFilter::matches) + unit.classes + .parallelStream() + .filter(classFilter::matches) + .filter { it.sourceStringIndex != -1 } + .forEach { + i++ + processClass(unit, it, renameEngine) + } } - var i = 0 - seq.filter { it.sourceStringIndex != -1 }.forEach { - i++ - processClass(unit, it, renameEngine) - } + // var seq = unit.classes.asSequence() + // if (isOperatingOnlyOnThisClass) { + // seq.filter { it.classType == UIBridge.currentClass } + // } else { + // seq.filter(classFilter::matches) + // } + + // var i = 0 + // seq.filter { it.sourceStringIndex != -1 }.forEach { + // i++ + // processClass(unit, it, renameEngine) + // } logger.info("There are $i classes with source info") } diff --git a/src/main/kotlin/com/yoavst/jeb/plugins/tostring/ToStringRenamingPlugin.kt b/src/main/kotlin/com/yoavst/jeb/plugins/tostring/ToStringRenamingPlugin.kt index ef67397..b3c5627 100644 --- a/src/main/kotlin/com/yoavst/jeb/plugins/tostring/ToStringRenamingPlugin.kt +++ b/src/main/kotlin/com/yoavst/jeb/plugins/tostring/ToStringRenamingPlugin.kt @@ -16,12 +16,12 @@ import com.yoavst.jeb.utils.renaming.RenameRequest class ToStringRenamingPlugin : BasicEnginesPlugin(supportsClassFilter = true, defaultForScopeOnThisClass = false) { override fun getPluginInformation(): IPluginInformation = PluginInformation( - "ToString renaming", - "Fire the plugin to change obfuscated fields' name given a verbose toString implementation", - "Yoav Sternberg", - PLUGIN_VERSION, - JEB_VERSION, - null + "ToString renaming", + "Fire the plugin to change obfuscated fields' name given a verbose toString implementation", + "Yoav Sternberg", + PLUGIN_VERSION, + JEB_VERSION, + null ) override fun processUnit(unit: IDexUnit, renameEngine: RenameEngine) { @@ -30,8 +30,8 @@ class ToStringRenamingPlugin : BasicEnginesPlugin(supportsClassFilter = true, de val cls = UIBridge.focusedClass?.implementingClass ?: return processClass(cls, decompiler, renameEngine) } else { - unit.classes.asSequence().filter(classFilter::matches) - .forEach { processClass(it, decompiler, renameEngine) } + unit.classes.parallelStream().filter(classFilter::matches) + .forEach { processClass(it, decompiler, renameEngine) } } propagateRenameToGetterAndSetters(unit, renameEngine.stats.effectedClasses, renameEngine) @@ -104,6 +104,7 @@ class ToStringRenamingPlugin : BasicEnginesPlugin(supportsClassFilter = true, de break } + is IJavaOperation -> { /* 2. Left traversal to restore names @@ -127,6 +128,7 @@ class ToStringRenamingPlugin : BasicEnginesPlugin(supportsClassFilter = true, de expression = left.left } + else -> { logger.debug("Warning: The toString method for ${cls.name} has a complex expression ${left?.javaClass?.canonicalName}") return @@ -136,8 +138,8 @@ class ToStringRenamingPlugin : BasicEnginesPlugin(supportsClassFilter = true, de } private fun renamePossibleExpressionWithStr( - exp: IJavaExpression, newName: String, - cls: IDexClass, renameEngine: RenameEngine + exp: IJavaExpression, newName: String, + cls: IDexClass, renameEngine: RenameEngine ) { if (newName.isBlank()) { logger.debug("Warning: The toString method for ${cls.name} yielded a blank field name") diff --git a/src/main/kotlin/com/yoavst/jeb/utils/BasicAstTraversal.kt b/src/main/kotlin/com/yoavst/jeb/utils/BasicAstTraversal.kt index cf0d22f..d498880 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/BasicAstTraversal.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/BasicAstTraversal.kt @@ -1,7 +1,7 @@ package com.yoavst.jeb.utils -import com.pnfsoftware.jeb.core.units.code.java.IJavaCompound import com.pnfsoftware.jeb.core.units.code.java.IJavaBlock +import com.pnfsoftware.jeb.core.units.code.java.IJavaCompound import com.pnfsoftware.jeb.core.units.code.java.IJavaMethod import com.pnfsoftware.jeb.core.units.code.java.IJavaStatement import com.yoavst.jeb.utils.renaming.RenameEngine diff --git a/src/main/kotlin/com/yoavst/jeb/utils/BasicEnginesPlugin.kt b/src/main/kotlin/com/yoavst/jeb/utils/BasicEnginesPlugin.kt index abe4e0f..793b0a6 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/BasicEnginesPlugin.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/BasicEnginesPlugin.kt @@ -12,11 +12,11 @@ import com.yoavst.jeb.utils.renaming.RenameEngine import kotlin.properties.Delegates abstract class BasicEnginesPlugin( - private val supportsClassFilter: Boolean = false, - private val defaultForScopeOnThisClass: Boolean? = null, - private val defaultForScopeOnThisFunction: Boolean? = null, - private val usingSelectedMethod: Boolean = false, - private val usingSelectedClass: Boolean = false + private val supportsClassFilter: Boolean = false, + private val defaultForScopeOnThisClass: Boolean? = null, + private val defaultForScopeOnThisFunction: Boolean? = null, + private val usingSelectedMethod: Boolean = false, + private val usingSelectedClass: Boolean = false ) : AbstractEnginesPlugin() { protected lateinit var context: IEnginesContext protected val logger: ILogger = GlobalLog.getLogger(javaClass) @@ -63,13 +63,13 @@ abstract class BasicEnginesPlugin( out += ClassFilterOption if (defaultForScopeOnThisClass != null) out += scopeThisClass( - defaultForScopeOnThisClass, - UIBridge.currentClass?.currentSignature ?: "no class selected" + defaultForScopeOnThisClass, + UIBridge.currentClass?.currentSignature ?: "no class selected" ) if (defaultForScopeOnThisFunction != null) out += scopeThisMethod( - defaultForScopeOnThisFunction, - UIBridge.currentMethod?.currentSignature ?: "no method selected" + defaultForScopeOnThisFunction, + UIBridge.currentMethod?.currentSignature ?: "no method selected" ) return out } @@ -83,10 +83,10 @@ abstract class BasicEnginesPlugin( classFilter = Regex(executionOptions[ClassFilterOptionTag].orIfBlank(ClassFilterDefault)) if (defaultForScopeOnThisClass != null) isOperatingOnlyOnThisClass = - executionOptions[ScopeThisClassTag].orIfBlank(defaultForScopeOnThisClass.toString()).toBoolean() + executionOptions[ScopeThisClassTag].orIfBlank(defaultForScopeOnThisClass.toString()).toBoolean() if (defaultForScopeOnThisFunction != null) isOperatingOnlyOnThisMethod = - executionOptions[ScopeThisMethodTag].orIfBlank(defaultForScopeOnThisFunction.toString()).toBoolean() + executionOptions[ScopeThisMethodTag].orIfBlank(defaultForScopeOnThisFunction.toString()).toBoolean() return true } } \ No newline at end of file diff --git a/src/main/kotlin/com/yoavst/jeb/utils/ClassUtils.kt b/src/main/kotlin/com/yoavst/jeb/utils/ClassUtils.kt index 2a34275..36c1281 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/ClassUtils.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/ClassUtils.kt @@ -3,6 +3,7 @@ package com.yoavst.jeb.utils import com.pnfsoftware.jeb.core.units.code.android.IDexUnit import com.pnfsoftware.jeb.core.units.code.android.dex.IDexClass import com.pnfsoftware.jeb.util.logging.GlobalLog +import java.util.stream.Stream private object ClassUtils @@ -10,19 +11,20 @@ private val logger = GlobalLog.getLogger(ClassUtils::class.java) fun IDexUnit.classBySignature(classSignature: String) = classes.firstOrNull { it.currentSignature == classSignature } -fun IDexUnit.subclassesOf(classSignature: String): Sequence { +fun IDexUnit.subclassesOf(classSignature: String): Stream { val classesWithGivenName = types.filter { it.signature == classSignature } if (classesWithGivenName.isEmpty()) { logger.error("Failed to find class with the given signature: $classSignature") - return emptySequence() + return Stream.empty() } else if (classesWithGivenName.size != 1) { logger.error("Multiple class with the given signature: ${classesWithGivenName.joinToString()}") - return emptySequence() + return Stream.empty() } - return classes.asSequence().filter { classesWithGivenName == it.supertypes } + return classes.parallelStream().filter { classesWithGivenName == it.supertypes } } + fun IDexClass.isSubclassOf(unit: IDexUnit, classSignature: String): Boolean { val classesWithGivenName = unit.types.filter { it.signature == classSignature } if (classesWithGivenName.isEmpty()) { diff --git a/src/main/kotlin/com/yoavst/jeb/utils/ContextUtils.kt b/src/main/kotlin/com/yoavst/jeb/utils/ContextUtils.kt index f9daefc..4b55335 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/ContextUtils.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/ContextUtils.kt @@ -11,18 +11,18 @@ import com.pnfsoftware.jeb.core.units.code.android.IXApkUnit import org.w3c.dom.Document fun IEnginesContext.getDexUnits(): MutableList = - RuntimeProjectUtil.findUnitsByType(projects[0], IDexUnit::class.java, false) + RuntimeProjectUtil.findUnitsByType(projects[0], IDexUnit::class.java, false) fun IEnginesContext.getXmlResource(name: String): Document? = - RuntimeProjectUtil.findUnitsByType(projects[0], IXmlUnit::class.java, false).firstOrNull { it.name == name }?.let { - if (!it.isProcessed) - it.process() - it.document - } + RuntimeProjectUtil.findUnitsByType(projects[0], IXmlUnit::class.java, false).firstOrNull { it.name == name }?.let { + if (!it.isProcessed) + it.process() + it.document + } val IEnginesContext.apkPackage: String get() = RuntimeProjectUtil.findUnitsByType(projects[0], IApkUnit::class.java, false).firstOrNull()?.packageName - ?: RuntimeProjectUtil.findUnitsByType(projects[0], IXApkUnit::class.java, false).first().packageName + ?: RuntimeProjectUtil.findUnitsByType(projects[0], IXApkUnit::class.java, false).first().packageName fun IUnit.refresh() = UnitUtil.notifyGenericChange(this) \ No newline at end of file diff --git a/src/main/kotlin/com/yoavst/jeb/utils/FocusUtils.kt b/src/main/kotlin/com/yoavst/jeb/utils/FocusUtils.kt index b325239..898274d 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/FocusUtils.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/FocusUtils.kt @@ -64,10 +64,12 @@ fun IGraphicalClientContext.currentFocusedMethod(supportFocus: Boolean = true, v } return selectedMethod } + ItemClassIdentifiers.CLASS_NAME, ItemClassIdentifiers.EXTERNAL_CLASS_NAME -> { logger.error("Note: You cannot select a constructor from Java decompilation view. Switch to bytecode view and select the init function.") return null } + else -> {} } } @@ -146,6 +148,7 @@ fun IGraphicalClientContext.currentFocusedType(supportFocus: Boolean = true, ver logger.error("Cannot get selected class: $fragment") null } + is IDexClass -> selectedClass.classType as? IDexType else -> { logger.error("Selected class is not dex: $selectedClass") @@ -158,6 +161,7 @@ fun IGraphicalClientContext.currentFocusedType(supportFocus: Boolean = true, ver logger.error("Cannot get selected type: $fragment") null } + is IDexType -> selectedType else -> { logger.error("Selected type is not dex: $selectedType") diff --git a/src/main/kotlin/com/yoavst/jeb/utils/GetterSetterUtils.kt b/src/main/kotlin/com/yoavst/jeb/utils/GetterSetterUtils.kt index 3a4726a..6c6616c 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/GetterSetterUtils.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/GetterSetterUtils.kt @@ -17,10 +17,10 @@ private object GetterSetterUtils private val logger = GlobalLog.getLogger(GetterSetterUtils::class.java) fun propagateRenameToGetterAndSetters( - unit: IDexUnit, - classes: Iterable, - renameEngine: RenameEngine, - useOnlyModified: Boolean = true + unit: IDexUnit, + classes: Iterable, + renameEngine: RenameEngine, + useOnlyModified: Boolean = true ) { val decompiler = unit.decompilerRef @@ -38,11 +38,11 @@ fun propagateRenameToGetterAndSetters( /** rebuild getter and setters e.g void a(object o){this.a = o;} to void setA(object o); **/ private fun processPossibleGetterSetter( - method: IDexMethod, - decompiledMethod: IJavaMethod, - cls: IDexClass, - renameEngine: RenameEngine, - useOnlyModified: Boolean + method: IDexMethod, + decompiledMethod: IJavaMethod, + cls: IDexClass, + renameEngine: RenameEngine, + useOnlyModified: Boolean ) { if (decompiledMethod.body.size() != 1) return @@ -62,18 +62,21 @@ private fun processPossibleGetterSetter( val currentName = right.field.currentName(cls) val (actualCurrentName, renameReason) = when { currentName != null && useOnlyModified -> renameEngine.getModifiedInfo(currentName) ?: return - currentName != null && !useOnlyModified -> renameEngine.getModifiedInfo(currentName) ?: (name to RenameReason.FieldName) + currentName != null && !useOnlyModified -> renameEngine.getModifiedInfo(currentName) + ?: (name to RenameReason.FieldName) + currentName == null && useOnlyModified -> return else -> name to RenameReason.FieldName } renameEngine.renameGetter( - RenameRequest(actualCurrentName, renameReason ?: RenameReason.FieldName), - method, - cls + RenameRequest(actualCurrentName, renameReason ?: RenameReason.FieldName), + method, + cls ) } } + is IJavaAssignment -> { val left = statement.left if (left is IJavaInstanceField) { @@ -86,15 +89,17 @@ private fun processPossibleGetterSetter( val currentName = left.field.currentName(cls) val (actualCurrentName, renameReason) = when { currentName != null && useOnlyModified -> renameEngine.getModifiedInfo(currentName) ?: return - currentName != null && !useOnlyModified -> renameEngine.getModifiedInfo(currentName) ?: (name to RenameReason.FieldName) + currentName != null && !useOnlyModified -> renameEngine.getModifiedInfo(currentName) + ?: (name to RenameReason.FieldName) + currentName == null && useOnlyModified -> return else -> name to RenameReason.FieldName } renameEngine.renameSetter( - RenameRequest(actualCurrentName, renameReason ?: RenameReason.FieldName), - method, - cls + RenameRequest(actualCurrentName, renameReason ?: RenameReason.FieldName), + method, + cls ) } } diff --git a/src/main/kotlin/com/yoavst/jeb/utils/LogUtils.kt b/src/main/kotlin/com/yoavst/jeb/utils/LogUtils.kt index e62e942..13a8e3b 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/LogUtils.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/LogUtils.kt @@ -2,8 +2,6 @@ package com.yoavst.jeb.utils import com.pnfsoftware.jeb.util.logging.GlobalLog import com.pnfsoftware.jeb.util.logging.ILogger -import java.io.File -import java.io.PrintStream fun ILogger.enableAllLogs(): ILogger = apply { enabledLevel = GlobalLog.LEVEL_ALL diff --git a/src/main/kotlin/com/yoavst/jeb/utils/MetadataUtils.kt b/src/main/kotlin/com/yoavst/jeb/utils/MetadataUtils.kt index 2b5619b..5d2f0d3 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/MetadataUtils.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/MetadataUtils.kt @@ -27,6 +27,7 @@ fun KotlinClassHeader.toStringBlock(): String { |Properties:${klass.properties.joinToString(separator = INDENT, prefix = INDENT) { "${it.fieldSignature}" }} """.trimMargin() } + is KotlinClassMetadata.FileFacade -> { val klass = metadata.toKmPackage() """$KOTLIN_METADATA_COMMENT_PREFIX @@ -37,6 +38,7 @@ fun KotlinClassHeader.toStringBlock(): String { |Properties:${klass.properties.joinToString(separator = INDENT, prefix = INDENT) { "${it.fieldSignature}" }} """.trimMargin() } + is KotlinClassMetadata.SyntheticClass -> { if (metadata.isLambda) { val klass = metadata.toKmLambda() @@ -57,11 +59,13 @@ fun KotlinClassHeader.toStringBlock(): String { } } + is KotlinClassMetadata.MultiFileClassFacade -> """$KOTLIN_METADATA_COMMENT_PREFIX |Type: Multi-File Class Facade |This multi-file class combines: |${metadata.partClassNames.joinToString(separator = INDENT, prefix = INDENT) { "Class: $it" }} """.trimMargin() + is KotlinClassMetadata.MultiFileClassPart -> { val klass = metadata.toKmPackage() """$KOTLIN_METADATA_COMMENT_PREFIX @@ -73,6 +77,7 @@ fun KotlinClassHeader.toStringBlock(): String { |Properties:${klass.properties.joinToString(separator = INDENT, prefix = INDENT) { "${it.fieldSignature}" }} """.trimMargin() } + is KotlinClassMetadata.Unknown -> """$KOTLIN_METADATA_COMMENT_PREFIX Type: Unknown""" null -> "" } diff --git a/src/main/kotlin/com/yoavst/jeb/utils/OptionsUtils.kt b/src/main/kotlin/com/yoavst/jeb/utils/OptionsUtils.kt index db8e46d..1b9e5ff 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/OptionsUtils.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/OptionsUtils.kt @@ -8,9 +8,9 @@ const val ClassFilterDefault = ".*" const val ClassFilterOptionTag = "Class filter" val ClassFilterOption: IOptionDefinition = OptionDefinition( - ClassFilterOptionTag, - ClassFilterDefault, - """The operation you are about to perform may be costly, and cannot be interrupted. + ClassFilterOptionTag, + ClassFilterDefault, + """The operation you are about to perform may be costly, and cannot be interrupted. If you only want to run it only on specific classes, you can specify a regex. For example, if you only want to run it on default package, use "^L[^\/]*;$" Or if you want to run it on package com.test use "^Lcom\/test\/.*;$" @@ -21,13 +21,13 @@ const val ScopeThisClassTag = "Scope this class" fun scopeThisClass(default: Boolean, classSignature: String? = null): IOptionDefinition { return if (classSignature == null) { BooleanOptionDefinition( - ScopeThisClassTag, default, "Run the given operation only on the selected class. Default: $default" + ScopeThisClassTag, default, "Run the given operation only on the selected class. Default: $default" ) } else { BooleanOptionDefinition( - ScopeThisClassTag, - default, - "Run the given operation only on the selected class. Default: $default\nCurrent class: $classSignature" + ScopeThisClassTag, + default, + "Run the given operation only on the selected class. Default: $default\nCurrent class: $classSignature" ) } } @@ -36,13 +36,13 @@ const val ScopeThisMethodTag = "Scope this method" fun scopeThisMethod(default: Boolean = true, methodSignature: String? = null): IOptionDefinition { return if (methodSignature == null) { BooleanOptionDefinition( - ScopeThisMethodTag, default, "Run the given operation only on the selected method. Default: $default" + ScopeThisMethodTag, default, "Run the given operation only on the selected method. Default: $default" ) } else { BooleanOptionDefinition( - ScopeThisMethodTag, - default, - "Run the given operation only on the selected method. Default: $default\nCurrent method: $methodSignature" + ScopeThisMethodTag, + default, + "Run the given operation only on the selected method. Default: $default\nCurrent method: $methodSignature" ) } } diff --git a/src/main/kotlin/com/yoavst/jeb/utils/renaming/InternalRenameRequest.kt b/src/main/kotlin/com/yoavst/jeb/utils/renaming/InternalRenameRequest.kt index 5618137..1aaa052 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/renaming/InternalRenameRequest.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/renaming/InternalRenameRequest.kt @@ -1,26 +1,26 @@ package com.yoavst.jeb.utils.renaming data class InternalRenameRequest( - val type: RenameObjectType, - val currentName: String, - val newName: String, - val reason: RenameReason, - /** - * Do we try to rename just to provide the user extra info about the class, - * or we actually try to get the real name of the class **/ - val informationalRename: Boolean = false + val type: RenameObjectType, + val currentName: String, + val newName: String, + val reason: RenameReason, + /** + * Do we try to rename just to provide the user extra info about the class, + * or we actually try to get the real name of the class **/ + val informationalRename: Boolean = false ) { companion object { fun ofClass(currentName: String, newName: String, reason: RenameReason, informationalRename: Boolean = false) = - InternalRenameRequest(RenameObjectType.Class, currentName, newName, reason, informationalRename) + InternalRenameRequest(RenameObjectType.Class, currentName, newName, reason, informationalRename) fun ofMethod(currentName: String, newName: String, reason: RenameReason, informationalRename: Boolean = false) = - InternalRenameRequest(RenameObjectType.Method, currentName, newName, reason, informationalRename) + InternalRenameRequest(RenameObjectType.Method, currentName, newName, reason, informationalRename) fun ofField(currentName: String, newName: String, reason: RenameReason, informationalRename: Boolean = false) = - InternalRenameRequest(RenameObjectType.Field, currentName, newName, reason, informationalRename) + InternalRenameRequest(RenameObjectType.Field, currentName, newName, reason, informationalRename) fun ofIdentifier(currentName: String, newName: String, reason: RenameReason, informationalRename: Boolean = false) = - InternalRenameRequest(RenameObjectType.Identifier, currentName, newName, reason, informationalRename) + InternalRenameRequest(RenameObjectType.Identifier, currentName, newName, reason, informationalRename) } } diff --git a/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameBackendEngineImpl.kt b/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameBackendEngineImpl.kt index b8d1eae..6272dd5 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameBackendEngineImpl.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameBackendEngineImpl.kt @@ -58,7 +58,7 @@ object RenameBackendEngineImpl : RenameBackendEngine { } override fun renameField(renameRequest: InternalRenameRequest, field: IDexField): Boolean = - field.setName(renameRequest.newName) + field.setName(renameRequest.newName) override fun renameMethod(renameRequest: InternalRenameRequest, method: IDexMethod, cls: IDexClass): Boolean { if (method.setName(renameRequest.newName)) { @@ -69,9 +69,9 @@ object RenameBackendEngineImpl : RenameBackendEngine { } override fun renameIdentifier( - renameRequest: InternalRenameRequest, - identifier: IJavaIdentifier, - unit: IDexUnit + renameRequest: InternalRenameRequest, + identifier: IJavaIdentifier, + unit: IDexUnit ): Boolean { val decompiler = decompilerCache.getOrPut(unit, unit::decompilerRef) if (decompiler.setIdentifierName(identifier, renameRequest.newName)) { diff --git a/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameEngineImpl.kt b/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameEngineImpl.kt index 5c81d45..713bd4f 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameEngineImpl.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameEngineImpl.kt @@ -10,16 +10,16 @@ import com.yoavst.jeb.utils.currentName import java.util.* class RenameEngineImpl( - private val frontendEngine: RenameFrontendEngine, - private val backendEngine: RenameBackendEngine + private val frontendEngine: RenameFrontendEngine, + private val backendEngine: RenameBackendEngine ) : RenameEngine { override val stats: RenameStats = RenameStats() override fun renameClass(renameRequest: RenameRequest, cls: IDexClass) { val internalRenameRequest = InternalRenameRequest.ofClass( - cls.currentName, - renameRequest.newName, - renameRequest.reason, - renameRequest.informationalRename + cls.currentName, + renameRequest.newName, + renameRequest.reason, + renameRequest.informationalRename ) val finalRenameRequest = frontendEngine.applyRules(internalRenameRequest) ?: return if (backendEngine.renameClass(finalRenameRequest, cls)) { @@ -31,10 +31,10 @@ class RenameEngineImpl( override fun renameField(renameRequest: RenameRequest, field: IJavaField, cls: IDexClass) { val name = field.currentName(cls) ?: return val internalRenameRequest = InternalRenameRequest.ofField( - name, - renameRequest.newName, - renameRequest.reason, - renameRequest.informationalRename + name, + renameRequest.newName, + renameRequest.reason, + renameRequest.informationalRename ) val finalRenameRequest = frontendEngine.applyRules(internalRenameRequest) ?: return if (backendEngine.renameField(finalRenameRequest, field, cls)) { @@ -46,10 +46,10 @@ class RenameEngineImpl( override fun renameField(renameRequest: RenameRequest, field: IDexField, cls: IDexClass) { val name = field.currentName val internalRenameRequest = InternalRenameRequest.ofField( - name, - renameRequest.newName, - renameRequest.reason, - renameRequest.informationalRename + name, + renameRequest.newName, + renameRequest.reason, + renameRequest.informationalRename ) val finalRenameRequest = frontendEngine.applyRules(internalRenameRequest) ?: return if (backendEngine.renameField(finalRenameRequest, field)) { @@ -60,10 +60,10 @@ class RenameEngineImpl( override fun renameMethod(renameRequest: RenameRequest, method: IDexMethod, cls: IDexClass) { val internalRenameRequest = InternalRenameRequest.ofMethod( - method.currentName, - renameRequest.newName, - renameRequest.reason, - renameRequest.informationalRename + method.currentName, + renameRequest.newName, + renameRequest.reason, + renameRequest.informationalRename ) val finalRenameRequest = frontendEngine.applyRules(internalRenameRequest) ?: return if (backendEngine.renameMethod(finalRenameRequest, method, cls)) { @@ -74,10 +74,10 @@ class RenameEngineImpl( override fun renameIdentifier(renameRequest: RenameRequest, identifier: IJavaIdentifier, unit: IDexUnit) { val internalRenameRequest = InternalRenameRequest.ofIdentifier( - identifier.currentName(unit), - renameRequest.newName, - renameRequest.reason, - renameRequest.informationalRename + identifier.currentName(unit), + renameRequest.newName, + renameRequest.reason, + renameRequest.informationalRename ) val finalRenameRequest = frontendEngine.applyRules(internalRenameRequest) ?: return if (backendEngine.renameIdentifier(finalRenameRequest, identifier, unit)) { @@ -90,18 +90,18 @@ class RenameEngineImpl( override fun renameGetter(renameRequest: RenameRequest, method: IDexMethod, cls: IDexClass) { val preInternalNameRequest = InternalRenameRequest.ofIdentifier( - method.currentName, - renameRequest.newName, - renameRequest.reason, - renameRequest.informationalRename + method.currentName, + renameRequest.newName, + renameRequest.reason, + renameRequest.informationalRename ) frontendEngine.applyRules(preInternalNameRequest) ?: return // now for the real request: val internalNameRequest = InternalRenameRequest.ofIdentifier( - method.currentName, - "get" + renameRequest.newName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }, - renameRequest.reason, - renameRequest.informationalRename + method.currentName, + "get" + renameRequest.newName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }, + renameRequest.reason, + renameRequest.informationalRename ) val finalRequest = frontendEngine.applyRules(internalNameRequest) ?: return if (backendEngine.renameMethod(finalRequest, method, cls)) { @@ -112,18 +112,18 @@ class RenameEngineImpl( override fun renameSetter(renameRequest: RenameRequest, method: IDexMethod, cls: IDexClass) { val preInternalNameRequest = InternalRenameRequest.ofIdentifier( - method.currentName, - renameRequest.newName, - renameRequest.reason, - renameRequest.informationalRename + method.currentName, + renameRequest.newName, + renameRequest.reason, + renameRequest.informationalRename ) frontendEngine.applyRules(preInternalNameRequest) ?: return // now for the real request: val internalNameRequest = InternalRenameRequest.ofIdentifier( - method.currentName, - "set" + renameRequest.newName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }, - renameRequest.reason, - renameRequest.informationalRename + method.currentName, + "set" + renameRequest.newName.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }, + renameRequest.reason, + renameRequest.informationalRename ) val finalRequest = frontendEngine.applyRules(internalNameRequest) ?: return if (backendEngine.renameMethod(finalRequest, method, cls)) { diff --git a/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameFrontendEngineImpl.kt b/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameFrontendEngineImpl.kt index 197714e..74905d5 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameFrontendEngineImpl.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameFrontendEngineImpl.kt @@ -17,21 +17,24 @@ object RenameFrontendEngineImpl : RenameFrontendEngine { if (currentName[0].isUpperCase() && currentName.any(Char::isLowerCase)) return null } + RenameObjectType.Method -> { // is it getter or setter or constructor if (currentName.startsWith("get") || currentName.startsWith("set") || - currentName == "" || currentName == "" + currentName == "" || currentName == "" ) return null // is it a camelCase? if (currentName[0].isLowerCase() && currentName.any(Char::isUpperCase)) return null } + RenameObjectType.Field -> { // check for mVariable or sVariable if ((currentName[0] == 'm' || currentName[0] == 's') && currentName[1].isUpperCase()) return null } + RenameObjectType.Identifier -> { if (currentName[0].isLowerCase() && currentName.any(Char::isUpperCase)) return null @@ -45,28 +48,28 @@ object RenameFrontendEngineImpl : RenameFrontendEngine { if (informationalRename) { // we want to keep the current name return InternalRenameRequest( - type, - currentName, - "${currentModifiedName}__${reason.prefix}_${newNameSanitized}", - reason, - informationalRename + type, + currentName, + "${currentModifiedName}__${reason.prefix}_${newNameSanitized}", + reason, + informationalRename ) } else if (type == RenameObjectType.Identifier) { // We want to erase the previous name since it isn't a real name return InternalRenameRequest( - type, - currentName, - "__${reason.prefix}_${newNameSanitized}", - reason, - informationalRename + type, + currentName, + "__${reason.prefix}_${newNameSanitized}", + reason, + informationalRename ) } else { return InternalRenameRequest( - type, - currentName, - "${currentModifiedName.extractOriginalName()}__${reason.prefix}_${newNameSanitized}", - reason, - informationalRename + type, + currentName, + "${currentModifiedName.extractOriginalName()}__${reason.prefix}_${newNameSanitized}", + reason, + informationalRename ) } diff --git a/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameReason.kt b/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameReason.kt index 68917c9..9d830cf 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameReason.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameReason.kt @@ -14,11 +14,13 @@ enum class RenameReason(val prefix: String) : Comparable { ToString("TS"), EnumName("E"), KotlinName("KT"), + /** The user renamed the class. Should not be used as a reason */ User(""); companion object { const val MAX_PREFIX_LENGTH = 2 + /** Allocate it once for performance reason */ private val VALUES = values() diff --git a/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameRequest.kt b/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameRequest.kt index b82bb08..cdcdfc8 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameRequest.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/renaming/RenameRequest.kt @@ -1,10 +1,10 @@ package com.yoavst.jeb.utils.renaming data class RenameRequest( - val newName: String, - val reason: RenameReason, - /** - * Do we try to rename just to provide the user extra info about the class, - * or we actually try to get the real name of the class **/ - val informationalRename: Boolean = false + val newName: String, + val reason: RenameReason, + /** + * Do we try to rename just to provide the user extra info about the class, + * or we actually try to get the real name of the class **/ + val informationalRename: Boolean = false ) diff --git a/src/main/kotlin/com/yoavst/jeb/utils/utils.kt b/src/main/kotlin/com/yoavst/jeb/utils/utils.kt index 56c9e1d..8012bde 100644 --- a/src/main/kotlin/com/yoavst/jeb/utils/utils.kt +++ b/src/main/kotlin/com/yoavst/jeb/utils/utils.kt @@ -1,13 +1,43 @@ package com.yoavst.jeb.utils import org.apache.commons.text.StringEscapeUtils +import java.util.stream.Stream fun String?.orIfBlank(other: String) = if (isNullOrBlank()) other else this -inline fun Sequence.mapToPair(crossinline f: (T) -> U): Sequence> = map { it to f(it) } + +inline fun Sequence.mapToPair(crossinline f: (T) -> U): Sequence> = map { + it to f(it) +} + +inline fun Stream.mapToPair(crossinline f: (T) -> U): Stream> = map { + it to f(it) +} + +inline fun Stream.mapNotNull(crossinline f: (T) -> U): Stream = map { + val result = f(it) + result +} + + inline fun Sequence.mapToPairNotNull(crossinline f: (T) -> U?): Sequence> = mapNotNull { - val res = f(it) - if (res == null) null else it to res + val result = f(it) + if (result == null) { + null + } else { + it to result + } +} + + +inline fun Stream.mapToPairNotNull(crossinline f: (T) -> U?): Stream> = map { + val result = f(it) + if (result == null) { + null + } else { + it to result + } } + fun String.unescape(): String = StringEscapeUtils.unescapeJava(this) \ No newline at end of file