JebOps
Plugins for reversing android apps in JEB
Install / Use
/learn @yoavst/JebOpsREADME
JebOps
Set of JEB plugins and scripts for better Android reversing experience.
Build
Compilation
- Copy
jeb.jarandswt.jarfrom$JEB_HOME/bin/apptolibs/runtime/ - Use
./gradlew jarto build a flat jar. It will be stored onbuild/libs/JebOps-VERSION.jar
Installation
- Copy the flat jar to
$JEB_HOME/coreplugins/ - Copy The following scripts from
src/main/pythonto$JEB_HOME/scripts:- FridaHook.py
- RenameFromConstArg.py
- UIBridge.py
- utils.py (not a script, but a dependency of both scripts)
- Restart JEB
Usage
One time plugins
This kind of plugin are supposed to run only once per project. In order to run them, Go to "File" menu, select "Plugins" , Then "Execute an engine plugin". Now, choose the plugin you want to run.
Dynamic plugins
Currently, the project has only one dynamic plugin: rename from constant arg. It can be launched in two ways:
- Use the shortcut "Ctrl+Alt+U" while selecting a method. Then, choose the plugin like you choose a one time plugin.
- 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. For example, it may convert axu
to axu__A_SettingsUtils.
Plugins
If the plugin supports running on a specific class/method, you can use "Ctrl+Alt+U" to select the method, before launching the plugin.
ToString
Obfuscation will rename private fields, getters and setters. However, if the class has an informative toString()
method, those names could be restored. The one time plugin ToString renaming could be used to automate the task.
Running the plugin on Twitter apk using i7-4770 took 30s, yielding:
Stats:
Classes: 826 Methods: 1531
Fields: 2421
Example
Before:
public final class a01 {
private final TextView a;
private final CharSequence b;
private final int c;
private final int d;
private final int e;
public int a() {
return this.e;
}
@Override
public String toString() {
return "TextViewTextChangeEvent(view=" + this.a + ", text=" + this.b + ", start=" + this.c + ", before=" + this.d + ", count=" + this.e + ")";
}
}
After:
public final class a01__TS_TextViewTextChangeEvent {
private final TextView a__TS_view;
private final CharSequence b__TS_text;
private final int c__TS_start;
private final int d__TS_before;
private final int e__TS_count;
public int a__TS_getCount() {
return this.e__TS_count;
}
@Override
public String toString() {
return "TextViewTextChangeEvent(view=" + this.a__TS_view + ", text=" + this.b__TS_text + ", start=" + this.c__TS_start + ", before=" + this.d__TS_before + ", count=" + this.e__TS_count + ")";
}
}
Enums
Enum's constructor signature is <init>(String name, int index). All the possible enum values are public static final
fields of the enum class. However, after obfuscation, the field's name might change, and JEB sometimes fail to use the
name from the constructor. The one time plugin Enum field renaming could be used to automate the task.
Running the plugin on Twitter apk using i7-4770 took 15s, yielding:
Stats:
Classes: 805 Fields: 3719
Example
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 final enum b U
public final enum b V
public final enum b W
public final enum b X
private static final b[] Y;
static {
b v0 = new b("NONE", 0);
b.U = v0;
b v1 = new b("START", 1);
b.V = v1;
b v3 = new b("END", 2);
b.W = v3;
b v5 = new b("CENTER", 3);
b.X = v5;
b.Y = new b[]{v0, v1, v3, v5};
}
}
After:
static final class b__T_Enum extends Enum {
public final enum b__T_Enum U__E_NONE
public final enum b__T_Enum V__E_START
public final enum b__T_Enum W__E_END
public final enum b__T_Enum X__E_CENTER
private static final b__T_Enum[] Y;
}
Resources
Android app use R.type.resourceName to reference resources from Java code. Obfuscation may:
- inline the const ints into the code
- Rename the classes and fields
The one time plugin Resources name restore could be used to restore the names. It does that by:
- Create a fake R class, and replace constant in the decompiled view with reference to fake field.
- Rename the static fields.
Running the plugin on Twitter apk using i7-4770 took 15s, yielding:
Stats:
Fields: 9454
Example
Before:
public final class p8 {
public static final int A = 0x7F0A00B4; // id:advertiser_avatar
public static final int A0 = 0x7F0A0154; // id:birdwatch_indicator
public static final int A1 = 0x7F0A022A; // id:caret
public static final int A2 = 0x7F0A0335; // id:crop
public static final int A3 = 0x7F0A03D3; // id:dock
public static final int A4 = 0x7F0A0493; // id:favorite
public static final int A5 = 0x7F0A0560; // id:gallery_header_trash
public static final int A6 = 0x7F0A0623; // id:interstitial_view_stub
public static final int A7 = 0x7F0A073D; // id:menu_ads_companion
public static final int A8 = 0x7F0A07CD; // id:namespace_text
public static final int A9 = 0x7F0A0891; // id:pin_list_button
public static final int Aa = 0x7F0A093D; // id:ptr_overlay_bg
public static final int Ab = 0x7F0A09F8; // id:scroll_view
// ...
}
After:
public final class p8 {
public static final int A__R_advertiser_avatar = 0x7F0A00B4; // id:advertiser_avatar
public static final int A0__R_birdwatch_indicator = 0x7F0A0154; // id:birdwatch_indicator
public static final int A1__R_caret = 0x7F0A022A; // id:caret
public static final int A2__R_crop = 0x7F0A0335; // id:crop
public static final int A3__R_dock = 0x7F0A03D3; // id:dock
public static final int A4__R_favorite = 0x7F0A0493; // id:favorite
public static final int A5__R_gallery_header_trash = 0x7F0A0560; // id:gallery_header_trash
public static final int A6__R_interstitial_view_stub = 0x7F0A0623; // id:interstitial_view_stub
public static final int A7__R_menu_ads_companion = 0x7F0A073D; // id:menu_ads_companion
public static final int A8__R_namespace_text = 0x7F0A07CD; // id:namespace_text
public static final int A9__R_pin_list_button = 0x7F0A0891; // id:pin_list_button
public static final int Aa__R_ptr_overlay_bg = 0x7F0A093D; // id:ptr_overlay_bg
public static final int Ab__R_scroll_view = 0x7F0A09F8; // id:scroll_view
Const arg renaming
Modern code contains a lot of references to the real variable name. Few examples are:
- Call to log:
Log.e("ResUtils", "Failed to run") - Assertions:
Preconditions.notNull(activity, "activity") - Dict retrieve:
conditions = jsonObject.get("conditions")
Const arg renaming plugin can be used to automate the renaming task. select the method you want to use as source for
renaming, and press "Ctrl+Alt+L". You will have to supply the following details:
- What is going to get renamed: class, method, asignee, another argument or custom target
- What is the index of the const argument that is the name. (0-based)
- (Optional) if you choose argument, what is the index of the other argument you want to rename. (0-based)
Then, the plugin will go over all the xrefs of the method, and if the name argument is constant, it will be used as name.
Custom
One can have a custom scheme to convert the constant argument to names. You will be asked to supply a python file.
Input:
# tag will be the constant argument from the call
tag = "..."
Output:
# Initialize one of the following variables
cls = "New class name"
method = "New method name"
assignee = "New assignee name"
argument = "New argument name"
Note that if you use argument, you should supply the argument index in the dialog.
There are 2 predefined functions you can use:
def split2(s, sep, at_least):
'''Split the string using separator. Result array will be at least the given length.'''
arr = s.split(sep)
return arr + [""] * (at_least - len(arr))
def underscore_to_camelcase(word):
return ''.join(x.capitalize() or '_' for x in word.split('_'))
For example, for Intent::getExtra you can use: assignee = underscore_to_camelcase(tag.split(".")[-1])
Mass renaming
In order to automate the task, you can use the plugin Const arg mass renaming plugin to run the renaming on multiple
function at the same time. You need to create a file with the following format:
# You can use "#" for comments
TARGET SIGNATURE CONST_ARG_INDEX [custom script path] [renamed argument index]
Where:
- target in
{Class, Method, Argument, Assignee, Custom} - signature is a dex method signature. for example: "Lcom/yoavst/test/TestClass;->doStuff(Ljava/lang/String;) Ljava/lang/String;"
- custom script path is a path relative to the signatures file, or a resource from jar, written
as
jar:resource_name.py. Currently, the available built-in renamers are:renamer_bundle_get.py- supports the case ofvar = get("LONG.DOT.SEPARATED.NAME.NOTIFICATION_TITLE")renamer_bundle_set.py- supports the case ofset("LONG.DOT.SEPARATED.NAME.NOTIFICATION_TITLE", var)renamer_log_renamer.py- supports the case oflog("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.
# ...
Custom Landroid/os/Bundle;->get(Ljava/lang/String;)Ljava/lang/Object; 0 jar:renamer_bundle_get.py
Custom Landroid/os/Bundle;->
Related Skills
node-connect
337.7kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
83.3kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
337.7kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
83.3kCommit, push, and open a PR
