JNITrace EX: trace JNI in Android apps (Frida 16+/17+)
Project description
JNITrace EX
An updated JNITrace that works with the latest Frida (16.x and 17.x).
JNITrace EX is a maintained fork of chame1eon/jnitrace, modernized to run on current Frida and Python versions. It traces use of the JNI API in Android apps—similar to frida-trace or strace, but for the JNI.
Why JNITrace EX?
The original jnitrace targeted older Frida (e.g. 14.x) and Python 2/3.4–3.7. This version was created so you can use JNI tracing with:
- Frida 16 and 17 – Updated Python API usage and a patch for jnitrace-engine so
Module.findExportByName(null, …)is replaced withModule.findGlobalExportByName(…)(Frida 17 breaking change). - Python 3.7+ – Version and script loading use
importlib_metadata/importlib_resources(with fallbacks) instead of deprecatedpkg_resources. - Stable terminal output – Unbuffered stdout and flushes so trace output appears in the terminal without needing
adb logcat. - Easy run-from-source –
./scripts/jnitrace.shruns jnitrace from the repo using the project’s venv andPYTHONPATH, so you don’t depend on a globalpip install jnitrace.
Repository: https://github.com/L0WK3Y-IAAN/jnitrace-ex
Installation
From PyPI:
pip install jnitrace-ex
After install, run the CLI with jnitrace (same as before).
From source:
Use a Python that has Frida installed (e.g. 3.12). Create a venv with a stable Python path, then install:
cd /path/to/jnitrace-ex
/path/to/python3 -m venv .venv # e.g. /Library/Frameworks/Python.framework/Versions/3.12/bin/python3
.venv/bin/pip install -e .
.venv/bin/pip install -r requirements.txt
Run without installing the CLI (script uses repo + venv):
./scripts/jnitrace.sh -l libnative-lib.so -b none com.example.myapplication
Troubleshooting:
bad interpreter: ... python3.14: no such file or directory– Recreate the venv with a Python that stays in the same place, or run:.venv/bin/python3 -m jnitrace.jnitrace ...ModuleNotFoundError: No module named 'frida'– Install Frida for that interpreter:pip install frida, or use the same Python asfrida-tools.ModuleNotFoundError: No module named 'jnitrace'– From repo root run.venv/bin/pip install -e ., or use./scripts/jnitrace.sh(no install needed).TypeError: not a functionin jnitrace-engine – Try running from source so the Frida script is built with your environment: clone the repo, then./scripts/jnitrace.sh -l lib.so -b none com.example.app. If that works, reinstall from PyPI (pip install --force-reinstall jnitrace-ex) to get a fresh build, or use the run-from-source method.
Dependencies:
- Android device (arm, arm64, x86, or x64) with frida-server (16.x or 17.x)
- Host: Python 3.7+, Frida 16+, colorama, hexdump, importlib_resources
Running
Start frida-server on the device, then:
# Spawn app and trace (recommended)
./scripts/jnitrace.sh -l libnative-lib.so -b none com.example.myapplication
# Or after pip install
jnitrace -l libnative-lib.so com.example.myapplication
Required:
-l libnative-lib.so– Library to trace. Use multiple-lfor several libs, or-l *for all.com.example.myapplication– Package name (must be installed on the device).
Useful options:
-b none– Disable backtrace (reduces overhead; often needed so the app doesn’t crash under tracing).-m attach– Attach to an already running process by name (e.g.-m attach "AppName").-R [host:port]– Remote Frida server (default127.0.0.1:27042).-i <regex>– Include only JNI methods matching the regex (e.g.-i "NewStringUTF|CallIntMethod").-e <regex>– Exclude JNI methods matching the regex.-o path/output.json– Save trace to JSON.-p path/to/script.js– Load a Frida script before jnitrace (e.g. anti-Frida bypass).-a path/to/script.js– Load a Frida script after jnitrace.--hide-data– Hide hexdumps and extra data.--ignore-env/--ignore-vm– Skip JNIEnv or JavaVM calls.
Start frida-server:
adb shell /data/local/tmp/frida-server
Optional arguments (reference)
| Option | Description |
|---|---|
-b <fuzzy|accurate|none> |
Backtrace mode. Default accurate; use none to avoid crashes on some apps. |
-i <regex> |
Include only JNI methods whose names match (can be used multiple times). |
-e <regex> |
Exclude JNI methods whose names match. |
-I <string> |
Include only these library exports (e.g. Java_... or RegisterNatives methods). |
-E <string> |
Exclude these library exports. |
--aux name=(string|bool|int)value |
Aux options when spawning (e.g. --aux='uid=(int)10'). |
Building from source
Build the Frida script (needed for run-from-source or custom changes):
npm install
npm run build
npm run watch compiles on change. The built script is jnitrace/build/jnitrace.js. A patch is applied to jnitrace-engine (via patch-package) for Frida 17 compatibility.
Output
Output is colored by thread. Each JNI call shows:
- Thread ID
- Timestamp (ms)
- Method name (e.g.
JNIEnv->NewStringUTF) - Arguments (
|-) and return value (|=) - Optional backtrace when
-bis notnone
String arguments and return values (e.g. from NewStringUTF) are shown in braces. With -b none, trace is lighter and less likely to destabilize the app.
API (jnitrace-engine)
The engine is available as a separate npm package for custom Frida scripts:
import { JNIInterceptor } from "jnitrace-engine";
JNIInterceptor.attach("FindClass", {
onEnter(args) {
console.log("FindClass method called");
this.className = Memory.readCString(args[1]);
},
onLeave(retval) {
console.log("\tLoading Class:", this.className);
console.log("\tClass ID:", retval.get());
}
});
More: jnitrace-engine
How it works (summary)
jnitrace injects a Frida script that hooks library loading (dlopen/dlsym). For libraries you choose with -l, it builds a shadow JNIEnv and replaces the real one so only those libs’ JNI calls go through trampolines. Arguments and return values are captured and sent to the Python host, which formats and prints them. Variadic JNI calls are handled by tracking GetMethodID/GetStaticMethodID and building method signatures so the correct NativeCallbacks can be created per call.
Issues
For bugs or questions about JNITrace EX, open an issue at:
https://github.com/L0WK3Y-IAAN/jnitrace-ex/issues
Please include:
- Device and Android version
- Frida version (host and frida-server)
- Target app (package name)
- Exact command and any error output
For the original jnitrace or the engine, see chame1eon/jnitrace and chame1eon/jnitrace-engine.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jnitrace_ex-1.0.1.tar.gz.
File metadata
- Download URL: jnitrace_ex-1.0.1.tar.gz
- Upload date:
- Size: 33.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
efc5449db1927ea27761bb2f305930ea8d01be1b7b13bba7c34e31d6e2a59601
|
|
| MD5 |
1389b93547f1c2ffb88334bd704bfd38
|
|
| BLAKE2b-256 |
5e590d85d546207584f0622d626546c8bbd79134fcf76b1cd38b564b06868611
|
Provenance
The following attestation bundles were made for jnitrace_ex-1.0.1.tar.gz:
Publisher:
publish.yml on L0WK3Y-IAAN/jnitrace-ex
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jnitrace_ex-1.0.1.tar.gz -
Subject digest:
efc5449db1927ea27761bb2f305930ea8d01be1b7b13bba7c34e31d6e2a59601 - Sigstore transparency entry: 976259873
- Sigstore integration time:
-
Permalink:
L0WK3Y-IAAN/jnitrace-ex@3dc0ed795d5dfb475d73875b13d8f56087077b21 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/L0WK3Y-IAAN
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3dc0ed795d5dfb475d73875b13d8f56087077b21 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file jnitrace_ex-1.0.1-py3-none-any.whl.
File metadata
- Download URL: jnitrace_ex-1.0.1-py3-none-any.whl
- Upload date:
- Size: 30.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5fba3ced9e082d0908605187b8f0d44a48e1268c710cd1042bf0aa61c54ea4a3
|
|
| MD5 |
8c9b7661d542be5038a4a461e94b8c64
|
|
| BLAKE2b-256 |
e60bce7edb4f43ab25b03f79488f1b3a41da8bb5ea98d20c0ea0c00a1759505c
|
Provenance
The following attestation bundles were made for jnitrace_ex-1.0.1-py3-none-any.whl:
Publisher:
publish.yml on L0WK3Y-IAAN/jnitrace-ex
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
jnitrace_ex-1.0.1-py3-none-any.whl -
Subject digest:
5fba3ced9e082d0908605187b8f0d44a48e1268c710cd1042bf0aa61c54ea4a3 - Sigstore transparency entry: 976259880
- Sigstore integration time:
-
Permalink:
L0WK3Y-IAAN/jnitrace-ex@3dc0ed795d5dfb475d73875b13d8f56087077b21 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/L0WK3Y-IAAN
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3dc0ed795d5dfb475d73875b13d8f56087077b21 -
Trigger Event:
workflow_dispatch
-
Statement type: