
Automatisierte Wiederherstellung von Android JNI RegisterNatives, Typrekonstruktion von gestrippten ARM64-.so-Dateien, OLLVM-BR-X8-Deobfuscator und universelle LLM/MCP-Bridge für Jadx, Ghidra und IDA Pro 9.0.
Überbrückt die Java-zu-Native-Kluft im Android-Reverse-Engineering.
Automatisierte RegisterNatives-Wiederherstellung, Typrekonstruktion gestrippter ARM64-.so-Dateien und ein einheitlicher Jadx-+-Ghidra- oder IDA-MCP-Server für KI-gestützte Malware-Analyse.
Schnellstart • Funktionsweise • MCP-Tools • Stealth-Dobby-Agent • Roadmap
Moderne Android-Malware, Banking-Trojaner, Spyware und kommerzielle Packer verstecken ihre Kernlogik (C2-Verschlüsselung, Schlüsselableitung, Umgebungsprüfungen und Exploit-Routinen) in gestrippten nativen C/C++-Bibliotheken (lib*.so).
Beim Reverse-Engineering über die JNI-Grenze hinweg stoßen Analysten auf drei Hürden:
classes.dex deklarierte native-Methoden verwenden keine standardmäßigen Java_com_pkg_Class_method-Symbole. Stattdessen werden sie zur Laufzeit innerhalb eines obfuskierten JNI_OnLoad über JNIEnv->RegisterNatives() dynamisch gebunden..so-Datei in Ghidra oder IDA zeigt Tausende unbenannter sub_XXXX-Funktionen, bei denen JNIEnv*-Vtable-Aufrufe wie reine Zeigerarithmetik aussehen ((**(code **)(*param_1 + 0x538))(param_1, param_2)).BroadcastReceiver in Java bis hin zu einer ARM64-Verschlüsselungsroutine in libnative-lib.so nicht nachverfolgen.JNISleuth-MCP beseitigt den Kontextwechsel. Es stellt statische und dynamische RegisterNatives-Bindungen automatisch wieder her, typisiert gestrippte ARM64-Funktionen in Ghidra neu und stellt einen einzigen einheitlichen Model Context Protocol (MCP)-Server bereit, der DEX-Bytecode mit nativem Assembler verbindet.
| Fähigkeit | Standard-Jadx-/Ghidra-MCPs | JNISleuth-MCP |
|---|---|---|
Standardmäßige Java_*-Exporte auflösen | ✅ | ✅ |
Gestrippte RegisterNatives-Struct-Arrays statisch wiederherstellen (.rodata / .data.rel.ro + JNI_OnLoad-CFG) | ❌ | ✅ |
Stealth-RegisterNatives-Erfassung auf dem Gerät via Dobby (umgeht Anti-Frida-Prüfungen) | ❌ | ✅ |
JNIEnv*-Vtable-Typen automatisch anwenden & Call*Method / GetStringUTFChars in Ghidra rekonstruieren | ❌ | ✅ |
Grenzüberschreitender Datenfluss mit einem einzigen Aufruf (Java Caller ➔ JNI Bridge ➔ Decompiled ARM64 C++) | ❌ | ✅ |
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
RegisterNatives-WiederherstellungFühren Sie jnisleuth scan gegen eine APK mit entfernten nativen Bibliotheken aus:
$ 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
Vorher 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;
}
Nach 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;
}
JNISleuth-MCP ist 100 % modellagnostisch. Sie können es über das standardmäßige Model Context Protocol (stdio JSON-RPC 2.0) mit jedem Cloud- oder lokalen LLM verbinden oder native OpenAI / Gemini / Ollama Function-Calling-Schemas exportieren:
jnisleuth llm-tools --format openai|gemini|mcp) für LangChain, LlamaIndex, LiteLLM, OpenAI SDK oder google-genai.