JNISleuth-MCP
Automated Android JNI RegisterNatives recovery, stripped ARM64 .so type reconstruction, OLLVM BR X8 deobfuscator, and universal LLM/MCP bridge for Jadx, Ghidra & IDA Pro 9.0.
Install / Use
claude mcp add nighthawkk -- npx -y github:nighthawkk/JNISleuth-MCPIf the server publishes to npm under a different name, use that package instead — check the repo README.
MCP Server
Model Context Protocol server
Quality Score
Category
SecuritySupported Platforms
Our assessment of JNISleuth-MCP
JNISleuth-MCP scores 84/100 on our quality scale, 834th of 1,105 Security skills we index.
Its MCP Server is 12 KB long, well organised into 18 sections with 7 code examples: a thorough specification that gives an agent plenty to work with.
It has 10 GitHub stars, so there is little community track record yet; judge it on its content.
Maintenance, license and trust
- The repository was last updated 7 days ago, so JNISleuth-MCP is actively maintained.
- No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
- Its trust signals score 85/100, with 1 caution from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
JNISleuth-MCP compared with similar skills
All 4 of these similar skills score higher than JNISleuth-MCP; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| JNISleuth-MCP (this skill)by nighthawkk | 84 | 10 | 7d ago | MCP Server |
| Agent-Reachby Panniantong | 100 | 108.6k | today | CLAUDE.md |
| headroomby headroomlabs-ai | 100 | 78.8k | today | CLAUDE.md |
| CowAgentby zhayujie | 100 | 47.8k | today | CLAUDE.md |
| Scraplingby D4Vinci | 100 | 96.0k | today | MCP Server |
Frequently asked questions
- How do I install JNISleuth-MCP?
- Run
claude mcp add nighthawkk -- npx -y github:nighthawkk/JNISleuth-MCP. The install tabs above show the steps for each supported agent. - Which AI agents does JNISleuth-MCP work with?
- It is written for Claude Code and Claude Desktop, as a MCP Server file. Other agents that read the same format can often use it too.
- Is JNISleuth-MCP safe to use?
- It declares no license and scores 85/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is JNISleuth-MCP still maintained?
- The repository was last updated 7 days ago, so JNISleuth-MCP is actively maintained.
Skill content
View source on GitHub🕵️♂️ JNISleuth-MCP
Bridge the Java-to-Native Chasm in Android Reverse Engineering.
Automated RegisterNatives Recovery, Stripped ARM64 .so Type Reconstruction, and a Unified Jadx + Ghidra or IDA MCP Server for AI-Assisted Malware Analysis.
Quickstart • How It Works • MCP Tools • Stealth Dobby Agent • Roadmap
</div>⚡ Why JNISleuth-MCP?
Modern Android malware, banking trojans, spyware, and commercial packers hide their core logic (C2 encryption, key derivation, environment checks, and exploit routines) inside stripped C/C++ native libraries (lib*.so).
When reversing across the JNI boundary, analysts hit three walls:
- Broken Cross-References:
nativemethods declared inclasses.dexdo not use standardJava_com_pkg_Class_methodsymbols. Instead, they are dynamically bound at runtime inside an obfuscatedJNI_OnLoadviaJNIEnv->RegisterNatives(). - Blind Decompilation: Opening a stripped
.soin Ghidra or IDA shows thousands of unnamedsub_XXXXfunctions whereJNIEnv*vtable calls look like raw pointer arithmetic ((**(code **)(*param_1 + 0x538))(param_1, param_2)). - Disconnected AI Context: Existing Ghidra MCP and Jadx MCP servers live in silos—an LLM cannot trace data flow from an Android
BroadcastReceiverin Java straight into an ARM64 encryption routine inlibnative-lib.so.
JNISleuth-MCP eliminates the context switch. It automatically recovers static and dynamic RegisterNatives bindings, re-types stripped ARM64 functions in Ghidra, and exposes a single unified Model Context Protocol (MCP) server connecting DEX bytecode to native assembly.
| Capability | Standard Jadx / Ghidra MCPs | JNISleuth-MCP |
| :--- | :---: | :---: |
| Resolve standard Java_* exports | ✅ | ✅ |
| Recover stripped RegisterNatives struct arrays statically (.rodata / .data.rel.ro + JNI_OnLoad CFG) | ❌ | ✅ |
| Stealth on-device RegisterNatives harvesting via Dobby (bypasses anti-Frida checks) | ❌ | ✅ |
| Auto-apply JNIEnv* vtable types & reconstruct Call*Method / GetStringUTFChars in Ghidra | ❌ | ✅ |
| Single-call cross-boundary dataflow (Java Caller ➔ JNI Bridge ➔ Decompiled ARM64 C++) | ❌ | ✅ |
🏗️ Architecture & How It Works
flowchart LR
subgraph Input["📦 Target APK / Malware"]
DEX["classes*.dex"]
ELF["Stripped lib/*.so (ARM64)"]
end
subgraph Harvester["🔍 Stage 1: JNI Bridge Harvester"]
StaticScan["Static ELF & DEX Scanner\n(LIEF + Capstone + Heuristic Triplets)"]
DobbyAgent["Stealth Native Agent\n(C++ / NDK / Dobby Inline Hook)"]
end
subgraph GhidraEngine["⚙️ Stage 2: Ghidra Headless Reconstructor"]
Renamer["Function Renamer & Signature Applier"]
VTable["JNIEnv* VTable & Struct Propagator"]
end
subgraph MCPServer["🤖 Stage 3: Unified FastMCP Server"]
CrossGraph["Cross-Boundary Callgraph\n(Jadx Java + Ghidra C/ARM64)"]
CallbackTrace["Upcall Tracer\n(Native -> Java Reflection)"]
end
DEX --> StaticScan
ELF --> StaticScan
ELF -. "Packed / OLLVM?" .-> DobbyAgent
StaticScan --> Renamer
DobbyAgent -- "register_natives.json" --> Renamer
Renamer --> VTable
VTable --> CrossGraph
VTable --> CallbackTrace
🎬 See It In Action
1. CLI Static + Dynamic RegisterNatives Recovery
Run jnisleuth scan against an APK with stripped native libraries:
$ jnisleuth scan --apk samples/banker_dropper.apk --arch arm64-v8a
[*] Parsing 3 DEX files... Found 14 declared `native` methods across 4 classes.
[*] Inspecting lib/arm64-v8a/libcore-crypto.so (Stripped ELF, 0 exported Java_* symbols)
[*] Running static JNINativeMethod triplet scanner on .rodata / .data.rel.ro...
[+] Resolved 11/14 methods via static struct table @ 0x0004d210
[!] 3 methods unresolved (constructed dynamically on stack in JNI_OnLoad).
[*] Launching headless Dobby collector on device (emulator-5554)...
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┓
┃ Java Class & Method ┃ JNI Signature ┃ .so Offset (ARM64) ┃ Resolution Mode ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━┩
│ com.dropper.Core#initEngine │ (Landroid/content/.. │ libcore.so+0x001a40 │ Static (.rodata) │
│ com.dropper.Core#decryptPayload │ ([BLjava/lang/Stri.. │ libcore.so+0x0023c8 │ Static (.rodata) │
│ com.dropper.Core#signC2Beacon │ (Ljava/lang/String.. │ libcore.so+0x003f10 │ Dynamic (Dobby) │
│ com.dropper.net.SocketBridge#connectRaw │ (Ljava/lang/String.. │ libcore.so+0x004890 │ Dynamic (Dobby) │
└──────────────────────────────────────────┴──────────────────────┴─────────────────────┴──────────────────┘
[+] Synced 14 function signatures and JNIEnv* types to Ghidra project: banker_dropper.gpr
2. Before vs. After in Ghidra
Before JNISleuth-MCP (FUN_000123c8):
long FUN_000123c8(long *param_1, undefined8 param_2, undefined8 param_3, undefined8 param_4) {
char *pcVar1;
long lVar2;
pcVar1 = (char *)(**(code **)(*param_1 + 0x548))(param_1, param_4, 0);
lVar2 = (**(code **)(*param_1 + 0x5c0))(param_1, param_3, 0);
sub_00014a00(lVar2, pcVar1);
(**(code **)(*param_1 + 0x550))(param_1, param_4, pcVar1);
return param_3;
}
After JNISleuth-MCP (Java_com_dropper_Core_decryptPayload):
jbyteArray Java_com_dropper_Core_decryptPayload(
JNIEnv *env, jobject thiz, jbyteArray encryptedData, jstring rc4KeyStr)
{
const char *key_utf = (*env)->GetStringUTFChars(env, rc4KeyStr, NULL);
jbyte *payload_bytes = (*env)->GetByteArrayElements(env, encryptedData, NULL);
rc4_crypt_buffer(payload_bytes, key_utf);
(*env)->ReleaseStringUTFChars(env, rc4KeyStr, key_utf);
return encryptedData;
}
🤖 Universal LLM & MCP Support (Works With Any Model)
JNISleuth-MCP is 100% model-agnostic. You can connect it to any cloud or local LLM via the standard Model Context Protocol (stdio JSON-RPC 2.0) or export native OpenAI / Gemini / Ollama function-calling schemas:
- Cloud LLMs & Agents: Google Gemini CLI, OpenAI / ChatGPT Desktop / Codex CLI, Cursor, Windsurf, Cline, Roo Code, Claude Desktop / Claude Code
- Local / Self-Hosted LLMs: Ollama, vLLM, LM Studio, OpenWebUI, Continue.dev (DeepSeek-R1, Qwen-2.5-Coder, Llama-3.3)
- Custom Python Agent SDKs: Direct tool-schema export (
jnisleuth llm-tools --format openai|gemini|mcp) for LangChain, LlamaIndex, LiteLLM, OpenAI SDK, orgoogle-genai.
| Tool Name | Description |
| :--- | :--- |
| resolve_apk_jni_map(apk_path) | Extracts all native declarations from DEX and maps them to exact ELF file offsets in lib/*.so (via export symbols, static .rodata triplets, or dynamic trace import). |
| get_cross_boundary_slice(java_method) | Returns the decompiled Java caller (via Jadx) and the typed, decompiled C/ARM64 implementation (via Ghidra/IDA) in a single unified response. |
| trace_jni_upcalls(so_name, fn_offset) | Lists every Java reflection callback (FindClass, GetMethodID, CallObjectMethod, SetByteArrayRegion) invoked inside a native ARM64 function and its callees. |
| sync_ghidra_jni_types(trace_json) | Applies JNIEnv*, JavaVM*, JNINativeMethod structs, and recovered function signatures directly into an active or headless Ghidra project. |
| sync_ida_jni_types(trace_json) | Applies JNIEnv*, JavaVM*, JNINativeMethod C headers, recovered function prototypes, and OLLVM fl_JN code cross-references into IDA Pro 9.0 (idalib / idapyswitch / idat64). |
| resolve_ollvm_indirect_branches(so_name, fn_offset) | Resolves OLLVM CSEL + BR X8 state-machine dispatchers and .rodata jump tables in JNI_OnLoad, and optionally patches the ELF binary with direct B.<cond> / B instructions. |
| extract_jni_onload_strings(so_name) | Dumps stack-constructed and XOR-decrypted strings inside .init_array and JNI_OnLoad. |
🥷 Stealth Dobby Agent (Anti-RASP & Packer Mode)
Many wild Android malware families and commercial protectors immediately crash or exit if they detect frida-agent.so in /proc/self/maps, gum-js-loop threads, or modified function prologues in libc.so.
JNISleuth-MCP includes an optional zero-Frida native collector (agent-native/) written in C++20 and ARM64 assembly using Dobby:
- Mid-Function Hooking: Places hooks inside the
ArtMethod::RegisterNative/JNIEnvExt::RegisterNativesdispatch path rather than the first instruction of exported stubs, evading basic prologue integrity scanners. - Base-Address Normalization: Uses
dladdr()and linkersoinfotraversal to automatically subtract ASLR slide and record deterministic file offsets (libfoo.so + 0x23c8), even if the packer unmaps ELF section headers afterJNI_OnLoad.
🚀 2-Minute Quickstart
Prerequisites
- Python 3.10+ (
pip install uv) - Ghidra 11.0+ (
GHIDRA_INSTALL_DIR) or IDA Pro 9.0+ (IDA_INSTALL_DIR/idapyswitch) - Jadx CLI installed in
$PATH - (Optional) Android NDK r26+ & ADB for dynamic Dobby collection
Installation
git clone https://github.com/<your-username>/JNISleuth-MCP.git
cd JNISleuth-MCP
uv sync
# 1. Scan the included obfuscated NDK crackme sample & sync with Ghidra / IDA Pro 9.0:
uv run jnisleuth scan --apk examples/obfuscated_jni_sample.apk --ghidra-headless --ida-headless
# 2. Resolve & patch OLLVM CSEL + BR X8 indirect branches in JNI_OnLoad:
uv run jnisleuth deobfuscate --so examples/libcore-crypto.so --offset 0x5200 --patch-out examples/libcore-crypto.deobf.so
Connect to Any LLM / IDE Agent (mcp.json / settings.json)
Works out of the box with Gemini CLI (~/.gemini/settings.json), Cursor (.cursor/mcp.json), Windsurf, Cline, Continue.dev (Ollama), ChatGPT Desktop, and Claude:
{
"mcpServers": {
"jnisleuth-mcp": {
"command": "uv",
"args": ["run", "--directory", "/path/to/JNISleuth-MCP", "jnisleuth-mcp"],
"env": {
"GHIDRA_INSTALL_DIR": "/opt/ghidra",
"IDA_INSTALL_DIR": "/opt/idapro-9.0"
}
}
}
}
Use with OpenAI / Gemini / Ollama Python SDKs Directly (No MCP Client Needed)
# Export function-calling tool schemas for OpenAI / Ollama / vLLM or Google Gemini:
uv run jnisleuth llm-tools --format openai > openai_tools.json
uv run jnisleuth llm-tools --format gemini > gemini_tools.json
🗺️ Roadmap
- [x] Static DEX
nativemethod parser + ELF.rodata/.data.rel.roJNINativeMethodtriplet scanner - [x] Headless Ghidra
JNIEnv*vtable auto-typing script for ARM64 & x86_64 - [x] Unified FastMCP server bridging Jadx, Ghidra & IDA Pro 9.0
- [x] On-device Dobby mid-function
RegisterNatives+DexFile::OpenCommonunpacker module - [x] IDA Pro 9.0 (
idapyswitch/idalib/ headlessidat64) backend support alongside Ghidra - [x] Automated OLLVM indirect-branch (
BR X8) resolver & ELF patcher forJNI_OnLoadstate machines
⚠️ Disclaimer
JNISleuth-MCP is developed for defensive malware analysis, mobile application security assessments, and reverse-engineering research. All
Truncated for display — read the full file on GitHub.
Related Skills
Agent-Reach
108.6kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
headroom
78.8kCompress tool outputs, logs, files, and RAG chunks before they reach the LLM. 20% fewer tokens for coding agents, 60-95% fewer tokens for JSON, same answers. Library, proxy, MCP server.
CowAgent
47.8kOpen-source personal AI assistant & Agent Harness. Plans tasks, runs tools and skills, self-evolves with memory and knowledge. Multi-agent, multi-model, multi-channel. Lightweight, extensible, one-line install.
Scrapling
96.0k🕷️ The BEST Web Scraping framework that handles everything from a single request to a full-scale crawl! Don't be shy, join here: https://discord.gg/EMgGbDceNQ and follow here for daily tips and tricks: https://x.com/Scrapling_dev
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
