Skip to content

Commit

Permalink
✨ Add Getters & Setters support
Browse files Browse the repository at this point in the history
  • Loading branch information
yoavst committed Apr 6, 2021
1 parent 29e00a8 commit b8cd5fd
Show file tree
Hide file tree
Showing 15 changed files with 215 additions and 69 deletions.
56 changes: 52 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Extract the provided `offline.tar.xz` to the main project's folder. You are good

1. Copy the flat jar to `$JEB_HOME/coreplugins/`
2. Copy The following scripts from `src/main/python` to `$JEB_HOME/scripts`:
* FridaHook.py
* RenameFromConstArg.py
* UIBridge.py
* utils.py (not a script, but a dependency of both scripts)
Expand All @@ -36,6 +37,10 @@ Currently, the project has only one dynamic plugin: rename from constant arg. It
1. Use the shortcut "Ctrl+Alt+U" while selecting a method. Then, choose the plugin like you choose a one time plugin.
2. Use the shortcut "Ctrl+Alt+L" while selecting a method.

### Scripts

The project comes with some handy scripts you can use.

### Naming convention

The naming convention of the plugin is `original__Reason_NewName`.
Expand Down Expand Up @@ -304,7 +309,7 @@ Custom Landroid/os/Bundle;->getBinder(Ljava/lang/String;)Landroid/os/IBinder; 0
# ...
```
#### Example
##### Example
Running the mass renaming plugin, using built-in list, on Twitter apk using i7-4770 took 100s, yielding:
Expand Down Expand Up @@ -333,6 +338,51 @@ private static Float d(Intent arg3) {
return __A_Level == -1 || __A_Scale == -1 ? null : ((float)(((float)__A_Level) / ((float)__A_Scale)));
}
```
#### Getters and setters renamers
A common case not covered by the previous method is getters & setters. We provide a plugin that uses same infrastructure as the previous plugins,
but searches for methods of the form getX() and setY(param). It will rename the asignee and argument as expected.
##### Example
Running the GetX plugin, on Twitter apk using i7-4770 took 180s, yielding:
```
Stats:
Fields: 1067 Identifiers: 10261
```
Before:
```java
ComponentName v0_1 = MediaButtonReceiver.getServiceComponentByAction(arg4, "android.media.browse.MediaBrowserService");
if(v0_1 != null) {
BroadcastReceiver.PendingResult v1 = this.goAsync();
Context v3 = arg4.getApplicationContext();
MediaButtonConnectionCallback v2 = new MediaButtonConnectionCallback(v3, arg5, v1);
MediaBrowserCompat v4 = new MediaBrowserCompat(v3, v0_1, v2, null);
v2.setMediaBrowser(v4);
v4.connect();
return;
}
```
After:
```java
ComponentName v0_1 = MediaButtonReceiver.getServiceComponentByAction(arg4, "android.media.browse.MediaBrowserService");
if(v0_1 != null) {
BroadcastReceiver.PendingResult v1 = this.goAsync();
Context __A_applicationContext = arg4.getApplicationContext();
MediaButtonConnectionCallback v2 = new MediaButtonConnectionCallback(__A_applicationContext, arg5, v1);
MediaBrowserCompat __A_mediaBrowser = new MediaBrowserCompat(__A_applicationContext, v0_1, v2, null);
v2.setMediaBrowser(__A_mediaBrowser);
__A_mediaBrowser.connect();
return;
}
```
## Development
Expand All @@ -349,6 +399,4 @@ You can use the script `JarLoader.py` to run a plugin directly from a jar.
For that to work, you need to delete the current version from `coreplugins`, and have your code to have no reference after running.
## Wishlist
1. Use android api methods for naming. For example, `Intent::getAction` can be used to name a variable action
2. Create an informative name from short methods body
1. Create an informative name from short methods body
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ plugins {
}

group 'org.yoavst.jeb'
version '0.1.0'
version '0.2.0'

repositories {
maven { url './offline' }
Expand All @@ -21,6 +21,7 @@ jar {
attributes(
"JebPlugin-entryclass": ["com.yoavst.jeb.plugins.constarg.ConstArgRenamingPlugin",
"com.yoavst.jeb.plugins.constarg.ConstArgMassRenamingPlugin",
"com.yoavst.jeb.plugins.constarg.GetXPlugin",
"com.yoavst.jeb.plugins.enumsupport.EnumRenamingPlugin",
"com.yoavst.jeb.plugins.resourcesname.ResourcesNamePlugin",
"com.yoavst.jeb.plugins.tostring.ToStringRenamingPlugin"].join(" "),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ 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.lang.Exception
import java.lang.IllegalArgumentException
import kotlin.math.log

class ConstArgMassRenaming(
private val renamers: Map<String, ExtendedRenamer>,
private val isOperatingOnlyOnThisClass: Boolean,
private var classFilter: Regex
private val renamers: Map<String, ExtendedRenamer>,
private val isOperatingOnlyOnThisClass: Boolean,
private var classFilter: Regex,
private var recursive: Boolean = true
) {
private val logger: ILogger = GlobalLog.getLogger(javaClass)
private var effectedMethods: MutableMap<IJavaMethod, IDexClass> = mutableMapOf()
Expand Down Expand Up @@ -51,22 +49,22 @@ class ConstArgMassRenaming(
return
}
ConstArgRenamingTraversal(
method,
decompiledMethod,
method.classType.implementingClass!!,
unit,
renameEngine
method,
decompiledMethod,
method.classType.implementingClass!!,
unit,
renameEngine
).traverse(decompiledMethod.body)
}

private inner class ConstArgRenamingTraversal(
private val method: IDexMethod,
private val javaMethod: IJavaMethod,
private val cls: IDexClass,
private val unit: IDexUnit,
renameEngine: RenameEngine
private val method: IDexMethod,
private val javaMethod: IJavaMethod,
private val cls: IDexClass,
private val unit: IDexUnit,
renameEngine: RenameEngine
) :
BasicAstTraversal(renameEngine) {
BasicAstTraversal(renameEngine) {
override fun traverseNonCompound(statement: IStatement) {
if (statement is IJavaAssignment) {
// Don't crash on: "Object x;"
Expand All @@ -92,40 +90,51 @@ class ConstArgMassRenaming(
// the method we were looking for was a constructor
processMatchedMethod(assignee, renamers[element.constructorSignature]!!) { element.arguments[it] }
}
else -> {
recursive -> {
// Try sub elements
element.subElements.forEach { traverseElement(it, assignee) }
}
else -> {
}
}

private inline fun processMatchedMethod(assignee: IJavaLeftExpression?, match: ExtendedRenamer, getArg: (Int) -> IJavaElement) {
val nameArg = getArg(match.constArgIndex)
if (nameArg is IJavaConstant && nameArg.isString) {
val result = match(nameArg.string)
if (!result.className.isNullOrEmpty()) {
var result: RenameResult? = null
if (match.constArgIndex < 0) {
// case of method match unrelated to args
result = match("")
} else {
val nameArg = getArg(match.constArgIndex)
if (nameArg is IJavaConstant && nameArg.isString) {
result = match(nameArg.string)
}
}

result?.let { res ->
if (!res.className.isNullOrEmpty()) {
renameEngine.renameClass(
RenameRequest(
result.className,
RenameReason.MethodStringArgument
), cls
RenameRequest(
res.className,
RenameReason.MethodStringArgument
), cls
)
}
if (!result.methodName.isNullOrEmpty()) {
if (!res.methodName.isNullOrEmpty()) {
renameEngine.renameMethod(
RenameRequest(
result.methodName,
RenameReason.MethodStringArgument
), method, cls
RenameRequest(
res.methodName,
RenameReason.MethodStringArgument
), method, cls
)
}
if (!result.argumentName.isNullOrEmpty()) {
if (!res.argumentName.isNullOrEmpty()) {
if (match.renamedArgumentIndex == null) {
throw IllegalArgumentException("Forget to put renamed argument index")
}
renameElement(getArg(match.renamedArgumentIndex), result.argumentName)
renameElement(getArg(match.renamedArgumentIndex), res.argumentName)
}
if (!result.assigneeName.isNullOrEmpty() && assignee != null) {
renameElement(assignee, result.assigneeName)
if (!res.assigneeName.isNullOrEmpty() && assignee != null) {
renameElement(assignee, res.assigneeName)
}
}
}
Expand Down Expand Up @@ -174,7 +183,7 @@ class ConstArgMassRenaming(
this.a = anIdentifierIRecovered
*/
private inner class SimpleIdentifierPropagationTraversal(private val cls: IDexClass, renameEngine: RenameEngine) :
BasicAstTraversal(renameEngine) {
BasicAstTraversal(renameEngine) {
override fun traverseNonCompound(statement: IStatement) {
if (statement is IJavaAssignment) {
val left = statement.left
Expand All @@ -188,21 +197,21 @@ class ConstArgMassRenaming(
return
}
renameEngine.renameField(
RenameRequest(
renameRequest.newName,
RenameReason.MethodStringArgument
), field, cls
RenameRequest(
renameRequest.newName,
RenameReason.MethodStringArgument
), field, cls
)
} else if (left is IJavaStaticField) {
val field = left.field ?: run {
logger.warning("Failed to get field: $left")
return
}
renameEngine.renameField(
RenameRequest(
renameRequest.newName,
RenameReason.MethodStringArgument
), field, cls
RenameRequest(
renameRequest.newName,
RenameReason.MethodStringArgument
), field, cls
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package com.yoavst.jeb.plugins.constarg
import com.pnfsoftware.jeb.core.*
import com.pnfsoftware.jeb.core.units.code.android.IDexUnit
import com.yoavst.jeb.bridge.UIBridge
import com.yoavst.jeb.plugins.JEB_VERSION
import com.yoavst.jeb.plugins.PLUGIN_VERSION
import com.yoavst.jeb.utils.BasicEnginesPlugin
import com.yoavst.jeb.utils.decompiler
import com.yoavst.jeb.utils.displayFileOpenSelector
Expand All @@ -20,8 +22,8 @@ class ConstArgMassRenamingPlugin :
"Const arg mass renaming plugin",
"Fire the plugin to change names using information from a constant string argument to function",
"Yoav Sternberg",
Version.create(0, 1, 0),
Version.create(3, 0, 16),
PLUGIN_VERSION,
JEB_VERSION,
null
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import com.pnfsoftware.jeb.core.*
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
import com.yoavst.jeb.plugins.JEB_VERSION
import com.yoavst.jeb.plugins.PLUGIN_VERSION
import com.yoavst.jeb.utils.BasicEnginesPlugin
import com.yoavst.jeb.utils.decompiler
import com.yoavst.jeb.utils.displayFileOpenSelector
Expand All @@ -29,8 +31,8 @@ class ConstArgRenamingPlugin :
"Const arg renaming plugin",
"Fire the plugin to change names using information from a constant string argument to function",
"Yoav Sternberg",
Version.create(0, 1, 0),
Version.create(3, 0, 16),
PLUGIN_VERSION,
JEB_VERSION,
null
)

Expand Down
59 changes: 59 additions & 0 deletions src/main/kotlin/com/yoavst/jeb/plugins/constarg/GetXPlugin.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.yoavst.jeb.plugins.constarg

import com.pnfsoftware.jeb.core.IPluginInformation
import com.pnfsoftware.jeb.core.PluginInformation
import com.pnfsoftware.jeb.core.units.code.android.IDexUnit
import com.pnfsoftware.jeb.core.units.code.android.dex.IDexMethod
import com.yoavst.jeb.plugins.JEB_VERSION
import com.yoavst.jeb.plugins.PLUGIN_VERSION
import com.yoavst.jeb.utils.*
import com.yoavst.jeb.utils.renaming.RenameEngine

class GetXPlugin : BasicEnginesPlugin(supportsClassFilter = true, defaultForScopeOnThisClass = false) {
override fun getPluginInformation(): IPluginInformation = PluginInformation(
"GetX plugin",
"Fire the plugin to scan the apk for getX methods and use that for naming",
"Yoav Sternberg",
PLUGIN_VERSION,
JEB_VERSION,
null
)

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
}
ConstArgMassRenaming(renamers, isOperatingOnlyOnThisClass, classFilter, recursive = false).processUnit(unit, renameEngine)
}


private fun simpleNameMethodVisitor(renameEngine: RenameEngine): (IDexMethod) -> ExtendedRenamer? = { method ->
val currentName = method.currentName
val name = renameEngine.getModifiedInfo(currentName)?.let { (realName, _) -> realName } ?: currentName
if (name.startsWith("get") && name.length >= 4) {
val fieldName = name.substring(3).decapitalize()
when {
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) {
val fieldName = name.substring(3).decapitalize()
when {
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
}

}
6 changes: 6 additions & 0 deletions src/main/kotlin/com/yoavst/jeb/plugins/consts.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.yoavst.jeb.plugins

import com.pnfsoftware.jeb.core.Version

val PLUGIN_VERSION: Version = Version.create(0, 2, 0)
val JEB_VERSION: Version = Version.create(3, 0, 16)
Loading

0 comments on commit b8cd5fd

Please sign in to comment.