JEB2 offers extensive scripting API. It provides three different types of plugins that support various tasks: scripts, engines and parsers. In contrast to IDA Pro API, JEB offers high level, object oriented API, implemented in Java. The plugins are usually written in Python and Java.
This article was written with JEB 2.2.5 and Python 2.7.
The plugins come with complicated terminology:
Upon opening an APK file, JEB creates project (a database) that contains artifact (the APK file). The APK artifact typically contains multiple units:
The unit is an abstract object that usually represents a single file, such as the manifest file. It can represent multiple files that are logically connected. For example, multidex files are represented by one Bytecode unit.
Confusingly enough, the desktop application is referred to as “official RCP desktop client” in the official documentation. The other clients (automated client, command-line client) are set aside in this article.
Although a list of loaded projects can be enumerated by JEB2 API, JEB 2.2.5 client can handle only a single project per session.
Scripts are usually written in Python. They are intended for lightweight actions on loaded projects. In contrast to engines, JEB client doesn’t have to be restarted when the script has changed because scripts are not preloaded. Scripts can’t be easily debugged and there’s no general support for Python IDEs because the API library is available only as a JAR file, not a Python module.
Engines are similar to scripts but must be written in Java and preloaded upon JEB client initialization. They are intended for heavy lifting operations on loaded projects. Engines can be debugged.
Plugins (parsers) register to specific types of units. They are triggered by JEB core at project loading time. JEB2 uses its own internal plugins to process APK files. Internal plugins can be seen in menu File, Engines, Parsers. Plugins can be debugged.
The classification among scripts, engines, plugins and parsers can be confusing at first sight. When in doubt, take a look at the implementation:
Scripts implement the IScript interface and are usually written in Python.
Engines implement IEnginesPlugin interface and are written in Java.
Parsers usually occupy two or more Java files. The plugin itself extends AbstractUnitIdentifier class, usually located in <Name>Plugin.java file and instantiates unit class that extends AbstractUnit, usually located in <Name>Unit.java file.
Beware of the offical documentation that sometimes refers to parsers as to “units”.
To complete the list, so called headless clients are written in Java and contain the main() method.
Let’s look at the Python scripting capabilities for conquering the obstacles with Android malware analysis.
Quoting docs: “Scripts should be designed to achieve relatively short and to-the-point operations, within a client context. Scripts have access to parts of the the client context, exposed via IClientContext and sub-interfaces.
Unlike plugins, scripts should be started on the client’s main thread. In the case of the official RCP desktop client, they are executed on the main UI thread. Care should be taken to not block the main thread for extended periods of time. Long operations should take place on separately spawned pool threads.”
When a project is loaded, scripts can be executed from menu File, Scripts, Run Script. In order to run Python scripts, a copy of standalone Jython (Java Python runtime) must be present in <JEB2>/scripts folder.
Although scripts can be written in Java, most of the scripts that are already available are written in Python. Python scripts enable faster development but are harder to debug.
Scripts implement the IScript interface. Unlike engines and parsers, this interface is defined in com.pnfsoftware.jeb.client.api package.
Scripts access units in opened projects. The projects are accessible through IGraphicalClientContext.getEnginesContext() that returns IEnginesContext. This interface defines method getProjects() that returns IRuntimeProject that in turn provides access to loaded instance of project (artifact). The unit can be retrieved using static method RuntimeProjectUtil.findUnitsByType(). In the context of Android code it is the ICodeUnit or IDexUnit unit.
The simplest valid Python script can be seen in JEB2SampleScript.py in PNF software GitHub repository.
A minimal valid script follows. The file name must correspond to the name of the class.
# scripts use dedicated API package from com.pnfsoftware.jeb.client.api import IScript # the class name is the same as the file name # the class implements IScript class JEB2Script(IScript): # define the entry point # 'ctx' is the IGraphicalClientContext def run(self, ctx): # print to the Logger window print('Hello, this line was generated by a JEB2 Python script.')
Dex unit in loaded project can be accessed as follows.
from com.pnfsoftware.jeb.client.api import IScript, IGraphicalClientContext from com.pnfsoftware.jeb.core import RuntimeProjectUtil from com.pnfsoftware.jeb.core.units.code.android import IDexUnit class GetDexUnit(IScript): def run(self, ctx): engctx = ctx.getEnginesContext() if not engctx: print('Back-end engines not initialized') return projects = engctx.getProjects() if not projects: print('There is no opened project') return dexUnit = RuntimeProjectUtil.findUnitsByType(projects, IDexUnit, False) if not dexUnit: print('No dex unit found') return print(dexUnit.getDescription())
The scripts outputs text to the Logger window:
Unit: - Type: dex - Name: Bytecode - Parent unit: test.apk - Children units: 0 DEX pools: - Strings: 3314 - Types: 766 - Fields: 2205 - Methods: 4921 - Prototypes: 974 - Classes: 444
Time consuming operations should run asynchronously to prevent blocking UI thread. Methods IGraphicalClientContext.executeAsync() and executeAsyncWithReturn() provide this functionality. The two scripts that are shipped with JEB showcase them: JEB2AsyncTask.py and JEB2AsyncTaskWithReturn.py.