Skip to content

Commit

Permalink
✨ Add source file support and kotlin comment
Browse files Browse the repository at this point in the history
  • Loading branch information
yoavst committed May 21, 2021
1 parent 934e448 commit e7150ca
Show file tree
Hide file tree
Showing 7 changed files with 349 additions and 51 deletions.
198 changes: 152 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ 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
### Scripts

The project comes with some handy scripts you can use.

### Naming convention

The naming convention of the plugin is `original__Reason_NewName`.
For example, it may convert `axu` to `axu__A_SettingsUtils`.
The naming convention of the plugin is `original__Reason_NewName`. For example, it may convert `axu`
to `axu__A_SettingsUtils`.

## Plugins

Expand Down Expand Up @@ -129,10 +129,21 @@ Before:
// PARTIAL FAILURE: ENUM SUGARING
// The enumeration is rendered as-is instead of being sugared into a Java 5 enum.
static final class b extends Enum {
public static final enum b U;
public static final enum b V;
public static final enum b W;
public static final enum b X;
public static final enum b U

;

public static final enum b V

;

public static final enum b W

;

public static final enum b X

;
private static final b[] Y;

static {
Expand All @@ -153,10 +164,21 @@ After:

```java
static final class b__T_Enum extends Enum {
public static final enum b__T_Enum U__E_NONE;
public static final enum b__T_Enum V__E_START;
public static final enum b__T_Enum W__E_END;
public static final enum b__T_Enum X__E_CENTER;
public static final enum b__T_Enum U__E_NONE

;

public static final enum b__T_Enum V__E_START

;

public static final enum b__T_Enum W__E_END

;

public static final enum b__T_Enum X__E_CENTER

;
private static final b__T_Enum[] Y;
}
```
Expand Down Expand Up @@ -298,7 +320,7 @@ Where:
* `renamer_bundle_get.py` - supports the case of `var = get("LONG.DOT.SEPARATED.NAME.NOTIFICATION_TITLE")`
* `renamer_bundle_set.py` - supports the case of `set("LONG.DOT.SEPARATED.NAME.NOTIFICATION_TITLE", var)`
* `renamer_log_renamer.py` - supports the case of `log("CLASSNAME MAYBE_METHODNAME", ...)`

The project comes with a builtin list, supporting: Intent, Bundle, ContentValues, org.json and shared preferences. It
doesn't support external libraries because they would probably be obfuscated.
Expand All @@ -322,27 +344,28 @@ Stats:
Before:
```java
private static Float d(Intent arg3) {
int v0 = arg3.getIntExtra("level", -1);
int v3 = arg3.getIntExtra("scale", -1);
return v0 == -1 || v3 == -1 ? null : ((float)(((float)v0) / ((float)v3)));
}
private static Float d(Intent arg3){
int v0=arg3.getIntExtra("level",-1);
int v3=arg3.getIntExtra("scale",-1);
return v0==-1||v3==-1?null:((float)(((float)v0)/((float)v3)));
}
```
After:
```java
private static Float d(Intent arg3) {
int __A_Level = arg3.getIntExtra("level", -1);
int __A_Scale = arg3.getIntExtra("scale", -1);
return __A_Level == -1 || __A_Scale == -1 ? null : ((float)(((float)__A_Level) / ((float)__A_Scale)));
}
private static Float d(Intent arg3){
int __A_Level=arg3.getIntExtra("level",-1);
int __A_Scale=arg3.getIntExtra("scale",-1);
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.
#### 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
Expand All @@ -356,33 +379,115 @@ Stats:
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;
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;
}
```
### Source file name
Sometimes the dex contains as a metadata, the source file name for some classes. JEB by default does not show it for
smali view, and never shows it for decompiler view. The plugin adds comment for every class that has this debug
attribute, with the source name. If enabled, it would also add the source file name as part of the class name.
**Warning:** Some obfuscator sets the source file to a specific string to ALL classes - Use with caution. You are
advised to enable debug directives, and search for ".source", to see if this is indeed the case.
We could not run it on Twitter, since the source file name is always `"TWTR"`
### Example
Before:
```java
class A {...
}
class B {...
}
```
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;
/* if comment only is enabled */
// Source name: RandomUtils.java
class A {...
}
/* if class name renaming is enabled */
class B__SF_UserInfo { ...
}
```
### Kotlin metadata
If the apk does not obfuscate the kotlin metadata information, We could get a lot of information about the class for
free. The plugin also supports obfuscated name for the metadata annotation.
#### Example
Before:
```java
@Metadata(bv = {1, 0, 3}, d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001A\u00020\u00042\b\u0010\u0005\u001A\u0004\u0018\u00010\u0006H\u0014\u00A8\u0006\u0007"}, d2 = {"Lcom/yoavst/testing/project/MainActivity;", "Landroidx/appcompat/app/AppCompatActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "app_debug"}, k = 1, mv = {1, 1, 15})
public final class A extends AppCompatActivity {
}
```
After:
```java
/*
Kotlin metadata:
Type: Class
Class Info:
Name: com/yoavst/testing/project/MainActivity
Supertypes: Class(name=androidx/appcompat/app/AppCompatActivity)
Module Name: app_debug
Type Aliases:
Companion Object:
Nested Classes:
Enum Entries:
Constructors:
<init>()V, Arguments:
Functions:
onCreate(Landroid/os/Bundle;)V, Arguments: savedInstanceState
Properties:
*/
@Metadata(bv = {1, 0, 3}, d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001A\u00020\u00042\b\u0010\u0005\u001A\u0004\u0018\u00010\u0006H\u0014\u00A8\u0006\u0007"}, d2 = {"Lcom/yoavst/testing/project/MainActivity;", "Landroidx/appcompat/app/AppCompatActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "app_debug"}, k = 1, mv = {1, 1, 15})
public final class A__KT_MainActivity extends AppCompatActivity {
}
```
## Development
Expand All @@ -392,11 +497,12 @@ The plugin relays on JEB internal api for some purposes. It was tested on JEB 3.
1. `UIUtils` depends on `OptionsForEnginesPluginDialog` for showing options dialog from script. As such, it also depends
on SWT.
### Utils
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.
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. Create an informative name from short methods body
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jar {
"com.yoavst.jeb.plugins.enumsupport.EnumRenamingPlugin",
"com.yoavst.jeb.plugins.resourcesname.ResourcesNamePlugin",
"com.yoavst.jeb.plugins.kotlin.KotlinPlugin",
"com.yoavst.jeb.plugins.sourcefile.SourceFilePlugin",
"com.yoavst.jeb.plugins.tostring.ToStringRenamingPlugin"].join(" "),
"jebPlugin-version": project.version
)
Expand Down
18 changes: 13 additions & 5 deletions src/main/kotlin/com/yoavst/jeb/plugins/kotlin/KotlinPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -155,18 +155,16 @@ class KotlinPlugin : BasicEnginesPlugin(supportsClassFilter = true, defaultForSc
seq.filter(classFilter::matches)
}

val tmp = unit.classBySignature("Lcom/yoavst/testing/project/MainActivity;")!!
val kotlinAnnotation = tmp.annotationsDirectory.classAnnotations[0].annotation
processClass(unit, tmp, kotlinAnnotation, renameEngine)


var i = 0
seq.filter { it.annotationsDirectory?.classAnnotations?.isNotEmpty() ?: false }.mapToPairNotNull { cls ->
cls.annotationsDirectory.classAnnotations.firstOrNull {
it.annotation.typeIndex == annotationTypeIndex
}?.annotation
}.forEach { (cls, annotation) ->
i++
processClass(unit, cls, annotation, renameEngine)
}
logger.info("There are $i classes with kotlin metadata annotation!")
}

private fun processClass(unit: IDexUnit, cls: IDexClass, annotation: IDexAnnotation, renameEngine: RenameEngine) {
Expand All @@ -181,6 +179,16 @@ class KotlinPlugin : BasicEnginesPlugin(supportsClassFilter = true, defaultForSc


val header = KotlinClassHeader(k, mv, d1, d2, xs, pn, xi)
val originalComment = unit.getComment(cls.currentSignature) ?: ""
if (KOTLIN_METADATA_COMMENT_PREFIX !in originalComment) {
val comment = header.toStringBlock()
if (originalComment.isBlank()) {
unit.setComment(cls.currentSignature, comment)
} else {
unit.setComment(cls.currentSignature, originalComment + "\n\n" + comment)
}
}

when (val metadata = KotlinClassMetadata.read(header)) {
is KotlinClassMetadata.Class -> {
val classInfo = metadata.toKmClass()
Expand Down
Loading

0 comments on commit e7150ca

Please sign in to comment.