
الهندسة العكسية بمساعدة الذكاء الاصطناعي مع Ghidra
ريف·ديك هي محطة عمل تحليل ثابت محلية لمستخدم واحد. تجمع بين واجهة مستخدم على الويب تعتمد على الأدلة أولاً ومساعد LLM على ثنائي يتم تحليله بواسطة خدمة Ghidra بدون واجهة رسومية: تصفح الأدلة الحتمية (الدوال، السلاسل النصية، الواردات، المراجع المتقاطعة، رسم بياني محدود للاستدعاءات) مباشرة، أو اطرح أسئلة محدودة على المساعد يجب أن تستند ادعاءاتها الواقعية إلى أدلة يمكن فحصها.
لا يتم تنفيذ الثنائيات المحللة أبدًا. يتواصل المتصفح فقط مع تطبيق Flask هذا؛ يقوم التطبيق بتوكيل الطلبات المُتحقق منها والمكتوبة إلى خدمة Ghidra.
https://github.com/user-attachments/assets/fba14dc5-7ad5-4137-9349-ed824da64fbe
cp .env.example .env # ضبط API_BASE و MODEL_NAME؛ اضبط API_KEY إذا لزم الأمر
docker compose up --build
يقرأ Docker Compose ملف .env تلقائيًا للاستيفاء. يفشل قبل البدء إذا كان API_BASE أو MODEL_NAME مفقودًا؛ يظل API_KEY=not-used صالحًا للمزودين المحليين/بدون مفتاح. يبدأ الـ stack كلتا الخدمتين. افتح http://127.0.0.1:5000.
لتشغيل خدمة Ghidra فقط:
docker pull biniamfd/ghidra-headless-rest:latest # تأكد من أحدث صورة
docker run --rm \
-p 127.0.0.1:9090:9090 \
-v "$(pwd)/data:/data/ghidra_projects" \
--security-opt no-new-privileges:true \
biniamfd/ghidra-headless-rest:latest
للحصول على pin قابل للتكرار، استخدم digest الإصدار المختبر بدلاً من latest:
docker run --rm \
-p 127.0.0.1:9090:9090 \
-v "$(pwd)/data:/data/ghidra_projects" \
--security-opt no-new-privileges:true \
biniamfd/ghidra-headless-rest:1.2.1@sha256:971591a3a8448d8ed969079b452306e806f36079c3ddd298f4a618d6e2f1442d
biniamfd/ghidra-headless-rest:latest.انسخ .env.example إلى .env واملأ هذه المتغيرات؛ راجع ذلك الملف للحصول على القائمة الكاملة والافتراضيات.
يتحدث ريف·ديك مع أي نقطة نهاية Chat Completions متوافقة مع OpenAI عبر OpenAI SDK، ويتم تكوينه بالكامل بواسطة API_BASE / API_KEY / MODEL_NAME. لا يوجد رأس أو معلمة أو منطق نموذج خاص بمزود معين: خادم Ollama محلي (API_BASE=http://127.0.0.1:11434/v1)، أو نقطة نهاية vLLM/llama.cpp/LM Studio مستضافة ذاتيًا، أو OpenAI نفسها، أو بوابة مثل OpenRouter تعمل جميعها بنفس الطريقة.
مثال إعدادات .env للمزود (استخدم العناصر النائبة، لا تلتزم بالمفاتيح الحقيقية أبدًا):
# Ollama
API_BASE=http://127.0.0.1:11434/v1
API_KEY=not-used
MODEL_NAME=qwen3:8b
# OpenRouter
API_BASE=https://openrouter.ai/api/v1
API_KEY=replace-with-your-key
MODEL_NAME=anthropic/claude-opus-4.8
# OpenAI
API_BASE=https://api.openai.com/v1
API_KEY=replace-with-your-key
MODEL_NAME=replace-with-a-supported-model-id
# LM Studio أو vLLM أو llama.cpp (اضبط المنفذ/النموذج حسب الخادم)
API_BASE=http://127.0.0.1:1234/v1
API_KEY=not-used
MODEL_NAME=replace-with-the-served-model-id
بشكل افتراضي (LLM_STREAM=auto)، يطلب المساعد استجابة متدفقة ويقوم بنقل الرموز إلى المتصفح فور وصولها. يمنح التدفق أيضًا ضمانًا أقوى للإلغاء: عندما توقف استجابة (أو تغلق التبويب)، يغلق ريف·ديك بسرعة دفق المزود الأساسي ولا يقوم بجولات أدوات أو نموذج إضافية، لذلك يتم إنهاء التوليد المنبع بدلاً من تركه يعمل حتى الاكتمال في الخلفية.
ملاحظات:
auto، إذا رفض المزود الطلب المتدفق بسبب خطأ توافق (HTTP 400/404/405/422) قبل أي محتوى أو إخراج استدعاء أداة، يتراجع ريف·ديك إلى استدعاء محظور واحد ويتذكر ذلك لبقية العملية. لا يتم التعامل مع أخطاء المصادقة (401/403)، أو تحديد المعدل (429)، أو الخادم (5xx) كمشكلات توافق ويتم عرضها كأخطاء بدلاً من إعادة المحاولة بصمت. اضبط LLM_STREAM=false لتخطي التدفق تمامًا، أو LLM_STREAM=true لفرضه (بدون تراجع).افتح التطبيق وقم برفع ثنائي لبدء مهمة تحليل. يطلب المحتوى النصي الواضح التأكيد قبل إرساله إلى Ghidra؛ استخدم تجاوز الثنائي الخام الصريح فقط عندما يكون المحتوى عمدًا برنامجًا ثابتًا/بيانات بدلاً من تنسيق قابل للتنفيذ. بمجرد اكتمال التحليل، قم بالتبديل بين علامتي تبويب مساحة العمل:
يأخذ كلا الوضعين ميزانية خطوة لكل مهمة، وخيار بدون حد للخطوات يعمل حتى تنتهي المهمة (لا يزال مقيدًا بـ MAX_STEP_BUDGET حتى لا يمكن لنموذج متكرر الهروب). إذا وصلت عملية إلى ميزانيتها، فإنها تبلغ عن نتائج جزئية وتقدم متابعة — والتي تستأنف نفس المحادثة باستخدام الأدلة المسترجعة بالفعل، دون إعادة استدعاءات الأدوات المكتملة. تنمو التكلفة مع عدد استدعاءات الأدوات/النموذج، لذا فإن الميزانيات الأعلى تكلف أكثر.
سير العمل المتاحة:
لكل مهمة تحليل محادثة رئيسية بالإضافة إلى سلاسل فرعية مركزة اختيارية. اختر تحقيق فرعي جديد، أدخل ملخصًا من سطر واحد، واعمل بسياق محادثة جديد على نفس الثنائي ونفس الأدوات للقراءة فقط. تظل تواريخ السلاسل معزولة، ويتم دفق سلسلة واحدة فقط في كل مرة.
عندما يكون العمل المركز جاهزًا، اختر إعادة النتيجة إلى الأصل. يقوم ريف·ديك باستدعاء نموذج محدود واحد على تلك السلسلة الفرعية فقط، ويتحقق من استشهادات الأدلة الخاصة بها، ويضيف بطاقة استنتاج واحدة مميزة بالإسناد إلى الأصل. يبقى الفرع بأكمله قابلاً لإعادة الفتح، بينما يتلقى سياق الأصل فقط الاستنتاج المدمج – وليس نص الفرع. يتم وضع علامة صريحة على البطاقة المرتجعة التي لا تحتوي على استشهادات مُتحقق منها على أنها غير مُتحقق منها.
تشير إجابات المساعد إلى الأدلة داخليًا كـ [function:0xADDR]، [string:0xADDR]، أو [import:name]. يتم التحقق من الاستشهادات مقابل ما تم استرجاعه فعليًا أثناء الجولة؛ يتم وضع علامة "(غير مُتحقق منه)" على الاستشهاد الذي لا يتطابق ويجب التعامل معه كادعاء غير مؤكد، وليس كحقيقة.
يتم عرض رسومات Mermaid في إخراج المساعد (مثل رسومات رسم بياني للاستدعاءات) في إطار معزول بدون وصول إلى الشبكة الخارجية.
يتواصل المتصفح فقط مع تطبيق الويب Rev·Deck. يقوم Rev·Deck بتنسيق LLM المكون وخدمة Ghidra بدون واجهة رسومية، ثم يعرض الأدلة الناتجة ونشاط الوكيل في مساحة عمل واحدة.
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
npm ci && npm run vendor # مرة واحدة: شراء تشغيل Mermaid المثبت
cp .env.example .env # قم بتحرير API_BASE / MODEL_NAME / API_KEY
set -a; source .env; set +a # Python العادي لا يقوم بتحميل .env تلقائيًا
# ابدأ خدمة Ghidra المنفصلة، ثم:
python webui/app.py
افتح http://127.0.0.1:5000. يقرأ Docker Compose ملف .env تلقائيًا؛ يتطلب التنفيذ من المصدر تصديره كما هو موضح أعلاه. خادم تطوير Flask مناسب للاستخدام المحلي؛ صورة Docker تشغل Gunicorn.
هذا مصمم لمحلل موثوق واحد على جهازه الخاص – وليس للاستخدام متعدد المستخدمين أو الاستضافة العامة. افتراضيًا، يربط التطبيق وخدمة Ghidra فقط بـ 127.0.0.1، وضع التصحيح مغلق، لا يتم تنفيذ الثنائيات المرفوعة أبدًا، ويبقى مفتاح مزود LLM على جانب الخادم.
pip install -r requirements.txt -r requirements-dev.txt
python -m pytest
node --test "webui/static/js/tests/**/*.test.mjs"
npm ci && npm run vendor:verify # يتحقق من سلامة حزمة Mermaid المشتراة
/readyz 503 — خدمة Ghidra غير قابلة للوصول على GHIDRA_API_BASE، أو API_BASE/MODEL_NAME غير مضبوطين.API_BASE/API_KEY/MODEL_NAME وأن المزود يمكن الوصول إليه خلال LLM_TIMEOUT.MAX_UPLOAD_BYTES.ANALYSIS_TIMEOUT لحاوية Ghidra (على سبيل المثال 5400 للثنائيات C++/Android التي تحتوي على أكثر من 10k دالة) وأعد الرفع. LLM_TIMEOUT غير مرتبط.| المتغير | الافتراضي | المعنى |
|---|
API_BASE | مطلوب | عنوان URL الأساسي المتوافق مع OpenAI (http/https). يتعطل Compose مبكرًا عند غيابه. |
API_KEY | not-used | مفتاح المزود. لا يتم تسجيله أو إرساله إلى المتصفح أبدًا؛ not-used صالح للمزودين المحليين بدون مفتاح. |
MODEL_NAME | مطلوب | معرف النموذج المتوقع بواسطة نقطة النهاية المكونة. يتعطل Compose مبكرًا عند غيابه. |
LLM_STREAM | auto | نقل التدفق: auto (تدفق، الرجوع إلى الاستدعاء المحظور مرة واحدة عند حدوث خطأ توافق قبل الإخراج)، true (تدفق دائمًا)، false (محظور دائمًا). |
GHIDRA_API_BASE | http://127.0.0.1:9090 | عنوان URL الأساسي لخدمة Ghidra. |
GHIDRA_IMAGE | biniamfd/ghidra-headless-rest:1.2.1@sha256:971591a3... | إصدار مُختبر مثبت بواسطة digest ثابت. :latest يحل أيضًا إلى هذا digest؛ تجاوز لتثبيت إصدار مختلف. |
HOST / PORT | 127.0.0.1 / 5000 | ربط خادم التطوير. |
MAX_UPLOAD_BYTES | 104857600 | حد حجم الرفع. |
CHATS_DIR | webui/chats | دليل سجل المحادثات. |
| سير العمل | الغرض | يتطلب عنوان دالة هدف |
|---|
program_triage | تلخيص الغرض المحتمل للبرنامج من البيانات الوصفية والواردات والسلاسل النصية والدوال. | لا |
suspicious_behavior | عرض المؤشرات الحتمية أولاً، ثم فرضيات محدودة ومُصنفة بوضوح. | لا |
selected_function | فك ترجمة دالة وشرحها مع المتصلين/المستدعين بها. | نعم |
call_chain | استكشاف حي واحد محدود من الرسم البياني للاستدعاءات الأصلي/المركب من دالة بداية. | نعم |
attack_surface_triage | قراءة تغطية/أفضل-K للنقاط الحتمية، ثم فحص ثلاثة مرشحين على الأكثر بعمق؛ النقاط هي أولويات وليست أحكامًا. | لا |
vulnerability_hypothesis | اختيار مرشح واحد محدود وعرض الأدلة والأدلة المضادة والأسئلة المفتوحة؛ لا يقوم بالتأكيد التلقائي أبدًا. | لا |