
استرداد RegisterNatives في Android JNI بشكل آلي، وإعادة بناء الأنواع لملفات .so المجرّدة من الرموز على ARM64، ومزيل تشويش OLLVM BR X8، وجسر LLM/MCP عالمي لـ Jadx وGhidra وIDA Pro 9.0.
جسر الهوة بين Java والكود الأصلي في الهندسة العكسية لتطبيقات Android.
استرداد آلي لـ RegisterNatives، وإعادة بناء الأنواع في ملفات ARM64 .so المجرّدة، وخادم MCP موحّد يجمع Jadx مع Ghidra أو IDA لتحليل البرمجيات الخبيثة بمساعدة الذكاء الاصطناعي.
البدء السريع • كيف يعمل • أدوات MCP • وكيل Dobby الخفي • خارطة الطريق
تُخفي البرمجيات الخبيثة الحديثة على Android — من أحصنة طروادة المصرفية وبرامج التجسس وحزم الحماية التجارية — منطقها الأساسي (تشفير C2، واشتقاق المفاتيح، وفحوصات البيئة، وروتينات الاستغلال) داخل مكتبات C/C++ أصلية مجرّدة (lib*.so).
عند إجراء الهندسة العكسية عبر حدود JNI، يصطدم المحللون بثلاثة عوائق:
native المعلنة في classes.dex الرموز القياسية Java_com_pkg_Class_method. بل تُربط ديناميكيًا وقت التشغيل داخل JNI_OnLoad مُشوَّش عبر JNIEnv->RegisterNatives()..so مجرّد في Ghidra أو IDA آلاف الدوال غير المسماة sub_XXXX حيث تبدو استدعاءات جدول الدوال الافتراضية (vtable) لـ JNIEnv* كحسابات مؤشرات خام ((**(code **)(*param_1 + 0x538))(param_1, param_2)).BroadcastReceiver في Java على Android مباشرةً إلى روتين تشفير ARM64 في libnative-lib.so.يُزيل JNISleuth-MCP تبديل السياق. فهو يسترد تلقائيًا ارتباطات RegisterNatives الساكنة والديناميكية، ويعيد كتابة أنواع الدوال المجرّدة في ARM64 داخل Ghidra، ويكشف خادمًا موحّدًا واحدًا لـ بروتوكول سياق النموذج (MCP) يربط بايت كود DEX بلغة التجميع الأصلية.
| القدرة | خوادم Jadx / Ghidra MCP القياسية | JNISleuth-MCP |
|---|---|---|
حل تصديرات Java_* القياسية | ✅ | ✅ |
استرداد مصفوفات بنى RegisterNatives المجرّدة ساكنًا (.rodata / .data.rel.ro + CFG الخاص بـ JNI_OnLoad) | ❌ | ✅ |
حصاد RegisterNatives خفيًا على الجهاز عبر Dobby (يتجاوز فحوصات مكافحة Frida) | ❌ | ✅ |
تطبيق أنواع جدول الدوال الافتراضية (vtable) لـ JNIEnv* تلقائيًا وإعادة بناء Call*Method / GetStringUTFChars في Ghidra | ❌ | ✅ |
تدفق بيانات عابر للحدود باستدعاء واحد (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 الساكن والديناميكي عبر CLIشغّل jnisleuth scan على ملف APK يحتوي على مكتبات أصلية مجرّدة:
$ 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
قبل 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;
}
بعد 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 محايد تمامًا للنموذج. يمكنك ربطه بـ أي LLM سحابي أو محلي عبر بروتوكول سياق النموذج القياسي (stdio JSON-RPC 2.0) أو تصدير مخططات استدعاء الدوال الأصلية OpenAI / Gemini / Ollama:
jnisleuth llm-tools --format openai|gemini|mcp) لـ LangChain، LlamaIndex، LiteLLM، OpenAI SDK، أو google-genai.