Skip to content
KitploitKITPLOIT
أدواتالمدونة
إرسال
أدواتالمدونة
إرسال

أدوات الاختراق واختبار الاختراق والأمن السيبراني لترسانتك الأمنية!

Kitploit هو دليل لأدوات الاختراق والأمن السيبراني واختبار الاختراق. اكتشف آخر تحديثات المشاريع للعثور على الثغرات وتحليل الأنظمة وأتمتة الاختبارات وتعزيز أمنك.

··الخلاصات·اتصال·الخصوصية·© 2026 Kitploit

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
gemma_crackme_tutorial — دليل تعليمي خطوة بخطوة حول استخدام نموذج الذكاء الاصطناعي المحلي Gemma 4 E4B من Google لهندسة عكسية لملف crackme على Windows باستخدام Ghidra، بما في ذلك إعداد الاستدلال المحلي وإعادة تسمية الدوال والمتغيرات تلقائيًا. | Kitploit
أدوات/GitHubGitHub/markoglasgow/gemma_crackme_tutorial
الهندسة العكسيةمصممي الأخطاءتحليل الملفات الثنائيةتعلم الآلةالتعلم والتعليمالهندسة العكسية بمساعدة الذكاء الاصطناعي
GitHubmarkoglasgow/gemma_crackme_tutorial

gemma_crackme_tutorial

دليل تعليمي خطوة بخطوة حول استخدام نموذج الذكاء الاصطناعي المحلي Gemma 4 E4B من Google لهندسة عكسية لملف crackme على Windows باستخدام Ghidra، بما في ذلك إعداد الاستدلال المحلي وإعادة تسمية الدوال والمتغيرات تلقائيًا.

الأكثر شعبية

عرض الكل →

اكتشف الأدوات الأكثر استخدامًا من قبل مجتمعنا.

استكشف جميع الأدوات

تصفح مجموعتنا من الأدوات

عرض جميع الأدوات →
مشاركة
عرض المستودع
35312منذ 4 أشهرتمت المراجعة من قبل Kitploit

استخدام نموذج الذكاء الاصطناعي المحلي Google Gemma 4 E4B في الهندسة العكسية لبرنامج Crackme بسيط

كنت أتجول مع نموذج Gemma E4B الجديد ذو الأوزان المفتوحة المحلي الذي أصدرته Google، ولدهشتي رأيت نجاحًا كبيرًا في استخدامه لسيناريوهات الهندسة العكسية المحلية دون اتصال بالإنترنت. أردت كتابة هذا البرنامج التعليمي لنشر الكلمة بأن الذكاء الاصطناعي المحلي أصبح الآن جيدًا بما يكفي للعديد من مهام الهندسة العكسية الأساسية، وأن الأمور ستتحسن على الأرجح بسرعة من هنا فصاعدًا.

الهندسة العكسية والذكاء الاصطناعي

أحد أكثر الأجزاء مملة في إجراء الهندسة العكسية لملف ثنائي جديد هو في البداية، عندما لا يكون لديك أي فكرة عن ماهية الدوال والمتغيرات المهمة. هناك العديد من الحيل التي يستخدمها المهندسون العكسيون للبدء، بما في ذلك النظر إلى مراجع السلاسل، المقارنة الثنائية، أو مطابقة الدوال المتشابهة.

الذكاء الاصطناعي جيد جدًا في المساعدة هنا، وقد حققت نجاحًا كبيرًا شخصيًا في استخدام واجهة برمجة تطبيقات OpenAI لتعليم ملف ثنائي أو تنظيف مخرجات مفكك الترجمة. ومع ذلك، هناك العديد من العيوب لاستخدام واجهات برمجة التطبيقات هذه:

  • التكلفة - تولد عمليات فك التجميع وفك التجميع أطنانًا من الرموز المميزة. تفرض واجهات برمجة التطبيقات رسومًا لكل رمز مميز، لذا يمكن أن تتطلب الملفات الثنائية الأكبر قدرًا كبيرًا من المال لتحليلها. إذا كنت تتعامل مع هدف كبير يحتوي على العديد من الملفات الثنائية التي يتم تحديثها كل أسبوع، يمكن أن تتراكم هذه التكاليف بسرعة، وتكون باهظة بالنسبة للمهندسين العكسيين الهواة.

  • الخصوصية - عندما تستخدم واجهة برمجة تطبيقات عن بُعد، يكون لدى مضيف الذكاء الاصطناعي إطلاع على ما تفعله. هذا أمر غير مقبول في بعض السيناريوهات المهنية.

  • التحكم - عندما تعتمد على واجهة برمجة تطبيقات عن بُعد، ليس لديك أي تحكم في النماذج التي يتم تقديمها لك، أو في جودة النماذج. إذا كنت تعتمد عليها لأشياء حساسة، يمكن أن تكون هذه مشكلة عندما تصبح بطيئة أو تتعطل عندما تحتاج إليها، أو عندما تتدهور جودة مخرجاتها إلى درجة أنها تصبح غير مفيدة.

تشغيل نماذج الذكاء الاصطناعي المحلية الخاصة بك يعالج بعض نقاط الألم هذه:

  • التكلفة - يمكن أن يكون أرخص بكثير تشغيل نموذج محلي بدلاً من الاعتماد على خدمة مستضافة. بينما من المحتمل أن يكون نموذجك المحلي أصغر وأبطأ من مزود جيد، إذا كان جيدًا بما يكفي ويعمل في وقت معقول، فقد يكون من المنطقي توفير المال عن طريق تشغيل نموذجك الخاص، خاصة إذا كنت تتعامل مع كميات كبيرة من البيانات لمهام بسيطة.

  • الخصوصية - عندما تقوم بتشغيل النموذج محليًا، لا تحدث أي استدعاءات شبكة، وأنت في سيطرة كاملة على خصوصيتك. لا يمكن لأي شخص رؤية ما تستخدم النموذج من أجله على جهازك الخاص.

  • التحكم - جمال النموذج ذو الأوزان المفتوحة هو أنه لا يمكن لأحد أن يأخذه منك. قد تجعل OpenAI أو Anthropic نماذجها المتطورة غير متاحة يومًا ما، إما من خلال زيادات الأسعار أو عن طريق إزالة واجهات برمجة التطبيقات الخاصة بها صراحةً. ولكن مع نموذج ذو أوزان مفتوحة، أنت مسيطر على مصيرك، سواء كان للأفضل أو للأسوأ.

ومع ذلك، فإن نماذج الذكاء الاصطناعي المحلية لها عيوبها:

  • الحجم - كلما كان نموذجك أكبر، كان أكثر ذكاءً. ومع ذلك، لا يمكن لمعظم النماذج الكبيرة أن تتلاءم مع الأجهزة الاستهلاكية. وبسبب ذلك، إذا كنت تقوم بتشغيل نموذج محلي، فمن المحتمل أنك تقوم بتشغيل نموذج أصغر بحوالي 10-100 مرة من نموذج متطور. يؤدي هذا الانخفاض في الحجم مباشرةً إلى انخفاض في ذكاء النموذج، مما يجعله غير مناسب للعديد من المهام التي يعتبرها الناس أمرًا مفروغًا منه في النماذج المتطورة مثل ChatGPT/Codex أو Claude.

  • السرعة - من المحتمل أن تعمل النماذج المحلية بشكل أبطأ على جهازك مقارنة باستخدام واجهة برمجة تطبيقات الذكاء الاصطناعي. مرة أخرى، هذا بسبب قيود الأجهزة الاستهلاكية، وبعض الحيل التي يمكن لمقدمي واجهات برمجة التطبيقات القيام بها والتي لا تتوفر عمومًا لك.

  • التكوين - تشغيل النماذج المحلية يشبه محاولة تشغيل Linux على كمبيوتر محمول مجدد مقابل الدخول إلى متجر Apple وشراء Macbook Air جديد نظيف. تجربة Codex وClaude Code هي تجربة متجر Apple للذكاء الاصطناعي. تجربة النموذج المحلي للذكاء الاصطناعي هي تجربة الرجل الذي يرتدي قبعة في مرآبه وهو يضرب على جهاز كمبيوتر متعطل محاولاً جعله يعمل. على الأقل، عليك القلق بشأن الأمور التالية:

    • الحصول على الأجهزة المناسبة
    • برامج تشغيل GPU
    • اختيار وتكوين خادم الاستدلال المناسب
    • اختيار نموذج يتناسب مع أجهزتك، ويعمل بسرعة كافية ليكون عمليًا، وذكيًا بما يكفي للمهمة المطلوبة
    • استخدام قالب الدردشة الصحيح مع النموذج الخاص بك
    • استكشاف التكميمات المختلفة للنموذج لإيجاد التوازن الصحيح بين الحجم/السرعة مقابل ذكاء النموذج.
    • تلقين النموذج بشكل صحيح
    • اختيار الإطار المناسب، أو إنشاء الإطار الخاص بك إذا لم يعمل أي من الإطارات الحالية.

ليست رحلة سهلة، ويستسلم الكثير من الناس ويفترضون أن نماذج الذكاء الاصطناعي المحلية ليست على مستوى المهمة، لأنهم لم يجدوا أبدًا التركيبة الصحيحة من الأجهزة/النموذج/الإعدادات/التلقين/الإطار لجعلها تعمل لمهمتهم. بينما في كثير من الحالات هم على حق، آمل أن يسلط هذا البرنامج التعليمي الضوء على المدى الذي وصلت إليه النماذج المحلية، وكيف يمكنها المساعدة في الهندسة العكسية، ويلهم الناس لتجربة الذكاء الاصطناعي المحلي.

إعداد الهندسة العكسية

  • crackme هو برنامج crackme بسيط جدًا لنظام Windows تم تنزيله من Crackmes.one هنا: https://crackmes.one/crackme/69e13f938afd9d6c48b488fd

(كلمة مرور الأرشيف هي crackmes.one). لقد استضفت رابطًا بديلاً هنا في المستودع في حال تعطل الرابط الأصلي.

  • يستخدم Ghidra 12.04 لفك التجميع وفك الترجمة. ستحتاج إلى تثبيت OpenJDK 21 لاستخدامه: https://github.com/nationalsecurityagency/ghidra

  • أثناء العمل على هذا البرنامج التعليمي، قمت ببرمجة إضافة Ghidra باستخدام Claude Code لإعادة تسمية الدوال والمتغيرات باستخدام الذكاء الاصطناعي. يمكنك تنزيل الإضافة من هنا: https://github.com/markoglasgow/Ghidra_FastAIRenamer_Plugin

لتثبيتها، ببساطة انقل ملف zip ghidra_12.0.4_PUBLIC_20260427_FastAIRenamerPlugin.zip إلى ${GHIDRA_HOME}\Extensions\Ghidra، ثم قم بتشغيل Ghidra عن طريق تشغيل ${GHIDRA_HOME}\ghidraRun.bat. لتفعيل الإضافة، في شاشة Ghidra الأولية على القائمة العلوية حدد File -> Install Extensions، ثم في متصفح الإضافات حدد مربع الاختيار بجوار FastAIRenamerPlugin، ثم انقر Ok. سيطلب منك Ghidra إعادة التشغيل، لذا افعل ذلك فورًا.

لتكوين الإضافة، في المرة التالية التي يبدأ فيها Ghidra، على القائمة العلوية اذهب إلى Tools -> Run Tool -> CodeBrowser. سيقول Ghidra "New Extensions detected. Would you like to configure them?". انقر نعم، ثم مرة أخرى حدد مربع الاختيار بجوار FastAIRenamerPlugin، ثم انقر Ok. عندما يفتح CodeBrowser، في القائمة العلوية انقر Window -> Fast AI Renamer، ثم انقر الزر Config. هنا ستتمكن من تكوين نموذج الذكاء الاصطناعي الخاص بك. أغلق نافذة الإضافة ونافذة CodeBrowser الفارغة بعد الانتهاء.

ملاحظة: إذا واجهت أي مشاكل في تحميل الإضافة، قد تحتاج إلى تمكين وضع المطور في Ghidra (File -> Configure -> مربع الاختيار بجوار Developer)

ملاحظة: يمكنك دائمًا التحقق مما إذا كانت الإضافة محملة بالذهاب إلى CodeBrowser، والنقر على File -> Configure -> Ghidra Core -> النقر على زر التكوين الأزرق -> التصفية بواسطة "FastAIRenamer" -> تأكد من تحديد مربع الاختيار بجوار اسمها.

ملاحظة: لإلغاء تثبيت الإضافة، افتح أولاً CodeBrowser، File -> Configure -> Ghidra Core -> النقر على زر التكوين الأزرق -> التصفية بواسطة "FastAIRenamer" -> إلغاء التحديد -> ok. أغلق CodeBrowser، ثم في نافذة Ghidra الأولية، File -> Install Extensions -> إلغاء تحديد "FastAIRenamer". أخيرًا أغلق Ghidra واحذف ghidra_12.0.4_PUBLIC_20260427_FastAIRenamerPlugin.zip من ${GHIDRA_HOME}\Extensions\Ghidra. للتأكد من حذف الإضافة، في المرة التالية التي تقوم فيها بتشغيل Ghidra، في النافذة الأولية اذهب إلى Help -> Runtime Information -> Extension Points -> تصفية بواسطة "FastAIRenamer" وتأكد من عدم ظهور شيء. أوف.

أخيرًا، تأكد من أن لديك Visual Studio أو بعض بيئة تطوير C++ الأخرى مهيأة حتى تتمكن من برمجة حل لـ crackme عندما يحين الوقت.

إعداد الذكاء الاصطناعي المحلي

ستختلف إعدادات الذكاء الاصطناعي المحلي للأشخاص بشكل كبير اعتمادًا على الأجهزة والمال المتاح لديهم. بالنسبة لي:

  • أنا شخصيًا أقوم بتشغيل Nvidia GTX 3080، مع تثبيت Cuda 13.2. يمكنك التحقق من إصدار Cuda الخاص بك عن طريق تشغيل nvcc --version في محطة Windows الخاصة بك.

  • أقوم بتشغيل https://huggingface.co/bartowski/google_gemma-4-E4B-it-GGUF/blob/main/google_gemma-4-E4B-it-Q8_0.gguf quants من bartowski. قد يكون خرافة من جانبي، ولكن بالنسبة للمهام التحليلية أحاول التشغيل بأقل قدر ممكن من التكميم، وتجنبه تمامًا إذا استطعت.

  • أنا أستخدم llama.cpp كخادم استدلال خاص بي، مع هذا الإصدار المحدد: llama-b8893-bin-win-cuda-13.1-x64.

  • أقوم بتشغيل llama.cpp مع إعدادات CLI التالية:``` ..\llama-b8893-bin-win-cuda-13.1-x64\llama-server.exe ^ --port 8090 ^ --threads 12 ^ --n-gpu-layers 256 ^ --no-mmap ^ --model "google_gemma-4-E4B-it-Q8_0.gguf" ^ --ctx-size 32768 ^ --temp 1.0 ^ --top-k 64 ^ --top-p 0.95 ^ --offline

root@kitploit:~
ينتج هذا `75 رمزًا/ثانية`، وهو سريع جدًا للذكاء الاصطناعي المحلي.

## لا يوجد عتاد؟ لا مشكلة

إذا لم يكن لديك عتاد متاح أو تواجه مشكلة في التهيئة، ولكنك لا تزال ترغب في متابعة هذا الدليل، فيمكنك الحصول على حساب OpenRouter واستخدام إحدى واجهات API المجانية:
https://openrouter.ai/models/?q=free

تقدم Google تحديدًا `Gemma 4 31B` و `Gemma 4 26B-A4B` مجانًا الآن (لفترة محدودة):
https://openrouter.ai/google/gemma-4-31b-it:free
https://openrouter.ai/google/gemma-4-26b-a4b-it:free

... فقط ضع في اعتبارك أن واجهات API المجانية هذه بطيئة وغير موثوقة، وربما يتم تسجيل جميع البيانات التي ترسلها إليها في تحليلاتها الداخلية واستخدامها في جلسة التدريب التالية. ومع ذلك، لأغراض هذا الدليل، يجب أن تتيح لك واجهات API هذه متابعة الدليل.

لتكوين إضافة Ghidra لاستخدام حساب OpenRouter الخاص بك، افتح إعدادات الإضافة وأدخل ما يلي:```
Base URL: https://openrouter.ai/api/
API Key: <your OpenRouter API Key>
Model Name: qwen/qwen3-235b-a22b-2507

هذا المثال سيشغل الإضافة ضد نموذج qwen3 هنا

فحص ملف Crackme

قم بفك الضغط وتشغيل crackmepls.exe، ستظهر لك شاشة تسجيل دخول قياسية. اكتب أي كلمة مرور عشوائية، وستحصل على رسالة 'تم رفض الوصول':``` User: marko Pass: 123 Access denied

root@kitploit:~
افتح Ghidra عن طريق تشغيل `ghidraRun.bat`. في شريط الأدوات، اختر `File -> New Project`، واترك `Non-Shared Project` محددًا وانقر على `Next >>`، ثم اختر مجلد مشروع فارغًا وأعطِ المشروع اسمًا. ثم انقر على `Finish`. 

بعد ذلك، انقر على `File -> Import File`، ثم اختر `crackmepls.exe` لإضافته إلى المشروع. سيعرض Ghidra بعض التفاصيل حول الملف، ويخبرك أنه ملف `Portable Executable (PE)` لـ `x86:LE:64:default:windows`. فقط انقر على `OK` لقبول ذلك دون تغيير أي شيء. بعد تأخير بسيط، سيعرض المزيد من التفاصيل حول الملف، مرة أخرى انقر على `OK` لقبول ذلك. أخيرًا، انقر نقرًا مزدوجًا على `crackmepls.exe` في المشروع لفتح Code Browser، لبدء تفكيكه. 

في البداية، سيعرض Ghidra رسالة تقول `crackmepls.exe has not been analyzed. Would you like to analyze it now?`. انقر على `Yes` ثم زر `Analyze` في النافذة التالية، وانتظر حتى يقوم Ghidra بتحديد موقع وتفكيك وتحليل جميع الدوال في الملف الثنائي. قد تظهر لك رسالة خطأ أو اثنتان أثناء التحليل حول عدم العثور على ملفات PDB، فقط انقر على `Ok` وتجاهلها. 

بمجرد أن ينتهي Ghidra من تحليل الملف، يجب أن ترى شيئًا كهذا:

![alt text](https://assets.kitploit.com/production/public/readmes/13760/2be69c6e539fa74e1ac31eec4304bcd7e8952ba96b91fab51add36ed6c1440ee.png "Program Entry")

هذا هو مدخل MSVC القياسي. انقر نقرًا مزدوجًا على `FUN_14000200c` وانتقل لأسفل. 

يجب أن ترى مراجع في نافذة التحليل (decompiler) إلى `__p___argv` و `__p___argc`، ثم استدعاء دالة `FUN_140001290` التي تقبلهم كوسائط. هذه على الأرجح دالة `main()` للـ crackme، لذا انقر نقرًا مزدوجًا عليها.

![alt text](https://assets.kitploit.com/production/public/readmes/13760/577b01a818727ee43010a24d03236157d22a69f94599f4117420436a641745ac.png "Finding Main")

بمجرد الوصول إلى الدالة `FUN_140001290`، انتقل لأسفل قليلاً في نافذة التحليل. سترى إشارات إلى السلاسل النصية `User:` و `Pass:`، بالإضافة إلى إشارات إلى `basic_istream` (تيار الإدخال) و `basic_ostream` (تيار الإخراج). 

![alt text](https://assets.kitploit.com/production/public/readmes/13760/dda6baaa2d284402bd8ce61c776810a0c154c6861e0012a56391fccbeb71a0c5.png "Print and inputs")

تطابق هذه السلاسل عبارات الطباعة والإدخال التي رأيناها عندما قمنا بتشغيل الـ crackme لأول مرة، لذا نعلم أننا في دالة `main()` الخاصة بالـ crackme.

الآن في هذه المرحلة، كان سيبدأ العمل الممل حيث يتعين علينا بالفعل الجلوس وإعادة تسمية كل اسم متغير واستدعاء دالة إلى شيء ذي معنى، أثناء تحليل ما يفعله الملف الثنائي لحل الـ crackme. اعتاد مهندسو التفكيك العكسي فعل ذلك يدويًا في التجميع (disassembly)، لكن لحسن الحظ، أعطانا التقدم التكنولوجي وأموال الضرائب أداة التحليل الرائعة هذه التي يمكن تفعيلها عبر نصوص برمجية للعمل مع الذكاء الاصطناعي. 

لذا بدلاً من القيام بعمل حقيقي، دعنا نجلس، ونطفئ أدمغتنا، وندع الذكاء الاصطناعي المحلي يقوم بكل العمل نيابة عنا. 

أثناء الإعداد لهذا البرنامج التعليمي، كان يجب أن تكون قد قمت بتثبيت إضافة Ghidra للمساعدة في إعادة تسمية الدوال والمتغيرات. الآن هو وقت استخدامها. في Ghidra، في شريط الأدوات في الأعلى، انقر على `Window` ثم `Fast AI Renamer` لفتح الإضافة. 

![alt text](https://assets.kitploit.com/production/public/readmes/13760/7511b88d33bb13e45a830295f42d1fceda2eb5897beffbec6ca5e6a3bd52f46e.png "Open Fast AI Renamer")

يجب أن ترى واجهة الإضافة، وهي مجموعة من الأزرار ومنطقة نصية. ابدأ بالنقر على زر "Config"، ثم تأكد من أن كل شيء مُهيأ للتواصل مع الذكاء الاصطناعي المحلي لديك بشكل صحيح. إليك كيف يبدو على جهاز الكمبيوتر الخاص بي:

![alt text](https://assets.kitploit.com/production/public/readmes/13760/4807636497952b764fc6dad362d0defc2804df0d5ba353c6938e266864c676cc.png "Config")

يمكنك إغلاق نافذة الإعدادات بالنقر على "Save"، ثم انقر على زر `Rename Variables`. ستظهر نافذة تقدم مع تنين، وقد تسمع جهاز الكمبيوتر الخاص بك يبدأ في العمل بجهد أثناء تشغيل نموذج الذكاء الاصطناعي المحلي لإعادة تسمية جميع المتغيرات في نافذة التحليل:

![alt text](https://assets.kitploit.com/production/public/readmes/13760/1f9e6ac902cce19f63319621adaabef504ad5a94fa84db2ec39782b74e2f18f6.png "Rename Variables")

بمجرد أن ينتهي الذكاء الاصطناعي (يستغرق حوالي 10-20 ثانية على جهازي)، يجب أن ترى وصفًا للمتغيرات التي أعاد تسميتها في منطقة النص في الإضافة، وسيتم إعادة تسمية المتغيرات نفسها في نافذة التحليل:

![alt text](https://assets.kitploit.com/production/public/readmes/13760/3454e5e42d7a4d84dffffbc6343e9579d33b3241b42e0be06bdb068eb91ed1e3.png "Rename Results")

جميع نماذج الذكاء الاصطناعي، وخاصة الصغيرة منها محليًا، غير موثوقة بطبيعتها، لذا قد ترى بعض الأخطاء في هذه الخطوة الأخيرة. سنتطرق إلى هذا لاحقًا. في هذه الأثناء، يمكنك دائمًا إعادة تشغيل الذكاء الاصطناعي بالنقر على زر `Rename Variables` مرة أخرى، حتى تحصل على نتائج دون أخطاء تبدو مرضية لك. 

## حل الـ Crackme 

في هذه المرحلة، لدينا مخرجات تحليل مُعلَّمة بشكل جيد، حيث تمت إعادة تسمية جميع أسماء المتغيرات إلى شيء ذي معنى. تقليديًا، كان بإمكان مهندس التفكيك العكسي الآن قراءة هذه الشفرة والبدء في صياغة حل للـ Crackme. ومع ذلك، اعتقدت أنه سيكون من الجيد محاولة جعل الذكاء الاصطناعي المحلي يحل الـ crackme لنا. تذكر، لن نستخدم أدمغتنا اليوم. 

أولاً، جعلت الذكاء الاصطناعي يكمل تعليم الملف الثنائي بأكمله لنا بالنقر على زر `Rename ALL Functions and Variables`. يخدم هذا في تعيين أسماء لجميع استدعاءات الدوال المستخدمة في دالة الـ crackme هذه، مما ينظف مخرجات التحليل بشكل أكبر. على جهازي، يستغرق هذا حوالي 15 دقيقة. 

عند استخدام `llama.cpp` مع معاملات CLI التي ذكرتها تحت "إعداد الذكاء الاصطناعي المحلي"، يمكنك فتح واجهة دردشة لنموذجك المحلي بالانتقال في المتصفح إلى [http://localhost:8090/](http://localhost:8090/)

هنا، أدخلت مخرجات التحليل للدالة من الـ crackme (يمكنك الحصول عليها بالنقر على زر "Decompile Function" في واجهة الإضافة، أو ببساطة نسخها من نافذة التحليل على الجانب الأيمن)، وطلبت من الذكاء الاصطناعي كتابة حل لنا:

======================================================================
======================================================================
======================================================================


أريد منك المساعدة في كتابة حل لـ crackme. فيما يلي قائمة بالتحليل (decompile) لدالة من crackme، حيث يقوم المستخدم بإدخال اسم مستخدم وكلمة مرور، ثم يتم منح الوصول إذا قدم كلمة المرور الصحيحة. يتم حساب كلمة المرور في الدالة.```


/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */
/* **Reasoning:**
   The function takes user input (a username/input and a password) via standard input. It processes
   the user input by calculating a complex, custom checksum/hash. It then compares the provided
   password input against a target buffer (likely a stored hash or secret). Finally, it determines
   and outputs whether "Access granted" or "Access denied," indicating the function serves as an
   authentication routine. */

undefined8 authenticate_user(undefined8 param_1,undefined8 param_2,undefined8 max_len)

{
  uint user_checksum;
  int iVar1;
  undefined8 ****temp_free_ptr;
  ulonglong user_input_byte_index;
  char *result_message;
  undefined1 *status_buffer;
  undefined8 ****data_buffer_ptr;
  ulonglong pass_input_length_1;
  bool access_granted;
  undefined8 uStack_d0;
  undefined1 status_buffer_small [8];
  undefined1 status_buffer_large [32];
  undefined8 ***allocated_ptrs [2];
  size_t compare_length;
  ulonglong ptr_metadata_size;
  undefined8 ***user_input_buffer;
  undefined8 uStack_78;
  ulonglong user_input_length;
  ulonglong user_input_length_param;
  undefined8 ***pass_input_buffer;
  undefined8 uStack_58;
  size_t pass_input_length;
  ulonglong pass_input_length_param;
  ulonglong checksum_seed;
  code *code_pointer;
  undefined8 ***pass_input_buffer_ptr;
  undefined8 ***user_input_buffer_ptr;
  ulonglong user_input_length_1;
  
  status_buffer = status_buffer_small;
  checksum_seed = MAGIC_VALUE_1 ^ (ulonglong)status_buffer_small;
  user_input_byte_index = 0;
  uStack_78 = 0;
  user_input_length = 0;
  user_input_length_param = 0xf;
  user_input_buffer = (undefined8 ****)0x0;
  uStack_58 = 0;
  pass_input_length = 0;
  pass_input_length_param = 0xf;
  pass_input_buffer = (undefined8 ****)0x0;
  formatted_string_output((basic_ostream<char,struct_std::char_traits<char>_> *)cout_exref,"User: ");
  extract_token_from_stream((basic_istream<char,struct_std::char_traits<char>_> *)cin_exref,
                (longlong *)&user_input_buffer,max_len);
  formatted_string_output((basic_ostream<char,struct_std::char_traits<char>_> *)cout_exref,"Pass: ");
  extract_token_from_stream((basic_istream<char,struct_std::char_traits<char>_> *)cin_exref,
                (longlong *)&pass_input_buffer,max_len);
  user_input_length_1 = user_input_length_param;
  user_input_buffer_ptr = user_input_buffer;
  user_checksum = 0;
  pass_input_length_1 = user_input_byte_index;
  if (user_input_length != 0) {
    do {
      temp_free_ptr = &user_input_buffer;
      if (0xf < user_input_length_param) {
        temp_free_ptr = (undefined8 ****)user_input_buffer;
      }
      user_checksum =
           ((int)user_input_byte_index + 1) *
           (int)*(char *)((longlong)temp_free_ptr + user_input_byte_index) +
           (int)pass_input_length_1;
      user_checksum = user_checksum * 8 ^ user_checksum;
      user_input_byte_index = user_input_byte_index + 1;
      pass_input_length_1 = (ulonglong)user_checksum;
    } while (user_input_byte_index < user_input_length);
  }
  int_to_string_dynamic(allocated_ptrs,user_checksum * 0x539 ^ 0x5a5a);
  pass_input_length_1 = pass_input_length_param;
  pass_input_buffer_ptr = pass_input_buffer;
  temp_free_ptr = &pass_input_buffer;
  if (0xf < pass_input_length_param) {
    temp_free_ptr = (undefined8 ****)pass_input_buffer;
  }
  data_buffer_ptr = allocated_ptrs;
  if (0xf < ptr_metadata_size) {
    data_buffer_ptr = (undefined8 ****)allocated_ptrs[0];
  }
  if (compare_length == pass_input_length) {
    if (compare_length == 0) {
      access_granted = true;
    }
    else {
      iVar1 = memcmp(data_buffer_ptr,temp_free_ptr,compare_length);
      access_granted = iVar1 == 0;
    }
  }
  else {
    access_granted = false;
  }
  if (0xf < ptr_metadata_size) {
    temp_free_ptr = (undefined8 ****)allocated_ptrs[0];
    status_buffer = status_buffer_small;
    if (0xfff < ptr_metadata_size + 1) {
      temp_free_ptr = (undefined8 ****)allocated_ptrs[0][-1];
      data_buffer_ptr =
           (undefined8 ****)((longlong)allocated_ptrs[0] + (-8 - (longlong)temp_free_ptr));
      status_buffer = status_buffer_small;
      if ((undefined8 ****)0x1f < data_buffer_ptr) {
        code_pointer = (code *)swi(0x29);
        (*code_pointer)(5);
        temp_free_ptr = data_buffer_ptr;
        status_buffer = status_buffer_large;
      }
    }
    *(undefined8 *)(status_buffer + -8) = 0x140001424;
    free(temp_free_ptr);
  }
  result_message = "Access granted\n";
  if (!access_granted) {
    result_message = "Access denied\n";
  }
  *(undefined8 *)(status_buffer + -8) = 0x140001443;
  formatted_string_output((basic_ostream<char,struct_std::char_traits<char>_> *)cout_exref,result_message);
  if (0xf < pass_input_length_1) {
    temp_free_ptr = (undefined8 ****)pass_input_buffer_ptr;
    if (0xfff < pass_input_length_1 + 1) {
      temp_free_ptr = (undefined8 ****)pass_input_buffer_ptr[-1];
      data_buffer_ptr =
           (undefined8 ****)((longlong)pass_input_buffer_ptr + (-8 - (longlong)temp_free_ptr));
      if ((undefined8 ****)0x1f < data_buffer_ptr) {
        code_pointer = (code *)swi(0x29);
        (*code_pointer)(5);
        status_buffer = status_buffer + 8;
        temp_free_ptr = data_buffer_ptr;
      }
    }
    *(undefined8 *)(status_buffer + -8) = 0x14000147d;
    free(temp_free_ptr);
  }
  if (0xf < user_input_length_1) {
    temp_free_ptr = (undefined8 ****)user_input_buffer_ptr;
    if (0xfff < user_input_length_1 + 1) {
      temp_free_ptr = (undefined8 ****)user_input_buffer_ptr[-1];
      data_buffer_ptr =
           (undefined8 ****)((longlong)user_input_buffer_ptr + (-8 - (longlong)temp_free_ptr));
      if ((undefined8 ****)0x1f < data_buffer_ptr) {
        code_pointer = (code *)swi(0x29);
        (*code_pointer)(5);
        status_buffer = status_buffer + 8;
        temp_free_ptr = data_buffer_ptr;
      }
    }
    *(undefined8 *)(status_buffer + -8) = 0x1400014b7;
    free(temp_free_ptr);
  }
  *(undefined8 *)(status_buffer + -8) = 0x1400014c9;
  return 0;
}


يرجى قراءة decompiler listing، ثم أكمل رمز C++ الخاص بي أدناه لحساب كلمة المرور الصحيحة لاسم مستخدم تم إدخاله:``` #include #include #include #include

int main() { std::string username; std::cout << "User: "; std::cin >> username;

root@kitploit:~
uint32_t password_value = 0;

std::cout << "Pass: " << password_value << "\n";
return 0;

}

root@kitploit:~
======================================================================
======================================================================
======================================================================

## الحل المُولَّد

في المرة الأولى التي أنشأ فيها حلاً، كان غير صحيح. لقد أدرجته في [tut1.txt](https://github.com/markoglasgow/gemma_crackme_tutorial/blob/master/tut1.txt) في هذا المستودع. للوهلة الأولى، يبدو الحل _صحيحاً_، لكنه يفشل عندما نجربه. أنظمة الذكاء الاصطناعي غير موثوقة بشكل أساسي، وهذا مثال مثالي على ذلك.

لا بأس، يمكننا ببساطة فتح محادثة جديدة، ونسخ ولصق المطالبة أعلاه، ومحاولة جعل الذكاء الاصطناعي يولد لنا حلاً جديداً. في حالتي، حصلت على حل يعمل في المحاولة الثانية:```
#include <cstdio>
#include <cstdint>
#include <string>
#include <iostream>
#include <sstream>
#include <iomanip>

/**
 * @brief Simulates the custom checksum calculation defined in the crackme function.
 *
 * @param username The input string (username).
 * @return int The final calculated user_checksum.
 */
int calculate_username_checksum(const std::string& username) {
    // user_checksum is treated as an 'int' (32-bit signed integer) during the core calculation.
    int user_checksum = 0;

    // pass_input_length_1 acts as the state variable and is a ulonglong (64-bit).
    // It starts at 0, as pass_input_length_1 = user_input_byte_index (which is 0) initially.
    uint64_t state_pass_length_1 = 0;

    for (size_t i = 0; i < username.length(); ++i) {
        // i = user_input_byte_index

        // Step 1: Calculate the intermediate value T1.
        // The decompiler implies this calculation happens using standard int math 
        // (truncating/wrapping if intermediate values exceed 32 bits).
        // (int)user_input_byte_index + 1
        int index_term = (int)(i + 1);

        // (int)*(char *)((longlong)temp_free_ptr + user_input_byte_index)
        // This is just the ASCII value of the character.
        int char_term = (int)username[i];

        // (int)pass_input_length_1
        // The state is cast down to an int for this arithmetic operation.
        int state_term = (int)state_pass_length_1;

        // T1 = ((int)user_input_byte_index + 1) * (int)char + (int)state
        int intermediate_t1 = index_term * char_term + state_term;

        // Step 2: Apply the XOR/Multiplication hash function.
        // user_checksum = user_checksum * 8 ^ user_checksum;
        // Note: The original code structure suggests the update happens here, 
        // but since we are recalculating the hash step-by-step, we use the definition:
        // user_checksum = T1 * 8 ^ T1; 

        // Since the original code overwrites user_checksum completely with the hash 
        // of the current character, we use the T1 derived above.

        int current_checksum = intermediate_t1;
        current_checksum = current_checksum * 8 ^ current_checksum;

        // Update the final running checksum
        user_checksum = current_checksum;

        // Step 3: Update the state variable for the next iteration.
        state_pass_length_1 = (uint64_t)user_checksum;
    }

    return user_checksum;
}


int main()
{
    std::string username;
    std::cout << "User: ";
    std::cin >> username;

    // 1. Calculate the final checksum using the username
    int final_user_checksum = calculate_username_checksum(username);

    // 2. Calculate the final hash/secret value
    // FinalHashValue = user_checksum * 0x539 ^ 0x5a5a
    // Note: The arithmetic here is done on the final 32-bit 'int' checksum.
    uint32_t final_hash_value = (uint32_t)final_user_checksum * 0x539 ^ 0x5a5a;

    // 3. Convert the final hash value into a string (as implied by int_to_string_dynamic)
    // Since the password needs to match the memory contents (memcmp), it must be the string representation.
    std::stringstream ss;
    ss << final_hash_value;
    std::string required_password = ss.str();


    // We output the required password string.
    std::cout << "Pass: " << required_password << "\n";
    return 0;
}

لقد قمت بتضمين الدردشة التي أنتجت الحل كـ solution_chat.html في هذا المستودع. وقد تضمنت تيار الاستدلال، والذي اعتقدت أنه رائع جدًا لمثل هذا النموذج الصغير الذي يعمل محليًا.

على أي حال، يمكننا تجميع الحل باستخدام Visual Studio ثم تشغيله لتوليد تركيبة صالحة لاسم المستخدم/كلمة المرور. ثم نقوم بإدخال اسم المستخدم/كلمة المرور في برنامج الكراك (crackme) والتحقق من أننا قد حللناه:

alt text

gg

القيود، الهلوسات، والأخطاء

كما رأينا مرارًا وتكرارًا خلال هذا البرنامج التعليمي، فإن الذكاء الاصطناعي غير موثوق به بطبيعته. تأمل لقطتي الشاشة أدناه:

alt text

alt text

لقطة الشاشة الأولى من جزء سابق في البرنامج التعليمي، بينما تم التقاط لقطة الشاشة الثانية في نفس الخطوة، ولكن مع تشغيل مختلف لإعادة تسمية الذكاء الاصطناعي. لاحظ في نافذة المترجم العكسي، كيف أن في لقطة الشاشة العلوية، يتم تسمية مخزن إدخال كلمة المرور بـ &pass_input_buffer، بينما في لقطة الشاشة السفلية، يتم تسمية نفس المخزن بـ &username_buffer_1. في لقطة الشاشة السفلية، يكذب الذكاء الاصطناعي علينا، ونطلق على تلك الأكاذيب اسم "الهلوسات".

جميع نماذج الذكاء الاصطناعي ستكذب عليك وتهلوس. كلما كان النموذج أصغر، زادت وتيرة الهلوسات. كلما كان النموذج أكبر، كلما كنت أكثر حظًا وقلت هلوساتهم. كلما دفعت أكثر، كلما زاد حظك. ومع ذلك، لا يوجد نموذج ذكاء اصطناعي محصن ضد الهلوسات، ويجب ألا تنسى هذا أبدًا أثناء العمل معهم.

الذكاء الاصطناعي هو الأنسب للمهام التي يمكنك فيها التحقق من المخرجات، أو عندما تكون المخرجات منخفضة المخاطر بحيث لا تكون الدقة المثالية ضرورية. في حالة الهندسة العكسية، يمكن للذكاء الاصطناعي مساعدتنا في إلقاء نظرة سريعة على ملف ثنائي غير معروف وتوفير الكثير من العمل اليدوي الشاق. ولكن كما رأينا مع إعادة التسمية السيئة التي قام بها، يمكن أن يقودنا أيضًا إلى الطريق الخطأ. تعامل دائمًا مع النتائج المولدة بواسطة الذكاء الاصطناعي بحذر، وتحقق منها دائمًا إذا استطعت.

أفضل طريقة لاستخدام الذكاء الاصطناعي هي التحقق من نتائجه تلقائيًا. على سبيل المثال، إذا كنت تطلب من الذكاء الاصطناعي كتابة كود، فسيكون من الرائع وجود سكريبت يختبر مخرجات الذكاء الاصطناعي مقابل مجموعة اختبارات كتبتها يدويًا. في حالة قيام الذكاء الاصطناعي بتوليد حل لبرنامج الكراك الخاص بنا، كان بإمكاننا كتابة مجموعة اختبارات تختبر كل حل يولد بواسطة الذكاء الاصطناعي مقابل برنامج الكراك تلقائيًا، ثم قبول الحل الذي يولد كلمات مرور صالحة لآلاف من أسماء المستخدمين المختلفة. ثم يمكننا تشغيل الذكاء الاصطناعي في حلقة، حتى يكتب حلاً يجتاز اختباراتنا.

قيود النماذج الصغيرة

لقد حللنا هذا الكراك باستخدام نموذج صغير نسبيًا (Gemma 4 E4B). يحتوي هذا النموذج على 8 مليارات معامل، على عكس النماذج المتطورة (SotA) الموجودة التي تحتوي على مئات المليارات إن لم يكن تريليونات المعاملات. لن يتمكن النموذج الأصغر من التفكير بشكل جيد، وسيهلوس بشكل متكرر، وسيصبح عديم الفائدة بشكل أسرع مع امتلاء نافذة السياق الخاصة به.

في تمرين هندسة عكسية حقيقي، قد نضطر إلى إجراء هندسة عكسية لدوال أكبر من نافذة السياق لنموذجنا الصغير، أو التي تحتوي على منطق معقد جدًا بحيث لا يستطيع ذكاؤه المحدود التفكير فيه. نحتاج إلى أن نكون على دراية بقيود نماذجنا عند استخدامها، حتى نفهم مسبقًا أين وكيف ستفشلنا.

مع تقدم الأجهزة الاستهلاكية جنبًا إلى جنب مع بنية النماذج، أعتقد أننا سنكون قادرين على تشغيل نماذج أكبر وأفضل محليًا، مما سيجعل مهام الهندسة العكسية الأكثر تقدمًا في متناول نماذجنا المحلية.

العمل المستقبلي

يقوم ملحق Ghidra 'Fast AI Renamer' الذي كتبته حاليًا بكتابة ملخص لكل دالة يحللها، ويمتلك القدرة على تصدير قوائم C و asm الخاصة بالمشروع الذي يعمل عليه بشكل جماعي. أرغب في كتابة أداة تحسب تضمينًا لكل ملخص دالة ثم تفهرسه في قاعدة بيانات متجهة أو SQLite، ومن ثم يمكننا الاستعلام عن قاعدة البيانات للعثور على الدوال وفقًا لاستعلامات اللغة الطبيعية.

يستخدم ملحق Ghidra حاليًا سير عمل بسيطًا بدون محادثة متعددة الأدوار لإعادة التسمية، ويعتمد على تحليل ملف CSV من مخرجات الذكاء الاصطناعي لأداء مهمته. لقد نفذت الأمور بهذه الطريقة لأنني لم أعتقد أن نموذج ذكاء اصطناعي صغير سيكون قادرًا على التعامل مع المحادثات الطويلة واستدعاءات الأدوات المتعددة المتأصلة في سير العمل الوكيل. قد أكون مخطئًا، ويجب استكشاف طرق بديلة لتحفيز الذكاء الاصطناعي. ومع ذلك، بالنسبة للنماذج المحلية الصغيرة، أعتقد أنه من الأفضل تقليل حجم نافذة السياق قدر الإمكان.

لست متأكدًا من مدى ضعف ملحق Ghidra تجاه حقن الاستعلامات (prompt injection)، ويجب على أي متخصص في الأمن أن يكون حذرًا من استخدامه لتحليل البرامج الضارة أو الثنائيات العدائية. بشكل عام، ينطبق هذا على جميع أدوات الهندسة العكسية المدعومة بالذكاء الاصطناعي، والتي قد تحتوي على خوادم MCP غير مصادقة على المضيف المحلي، أو أدوات متاحة لوظائف حساسة يمكنها تحقيق تنفيذ الأكواد. كن حذرًا.

نظرًا لأننا نستخدم نموذجًا صغيرًا للأغراض العامة في الهندسة العكسية، أتساءل إلى أي مدى يمكن تحسين أدائه عن طريق ضبطه بدقة على المحادثات المقطرة من نموذج أكبر وأكثر ذكاءً؟

أخيرًا، تعمل وظيفة تصدير C/asm لملحق Ghidra بشكل جيد مع Claude Code، وقد تمكنت من استخدام Claude Code + Sonnet لكتابة حل صحيح لهذا الكراك من المرة الأولى، فقط عن طريق تغذيته بفك التجميع المعلق (decompilation) للكراك وإخباره بإيجاد حل. أتساءل عما إذا كانت هناك أي مهارات يمكننا كتابتها لـ Claude Code قد تجعله أفضل في تحليل ثنائي من خلال ملفات نصية لمخرجات المترجم العكسي، دون جعله يمر عبر خادم MCP إلى Ghidra لإجراء التحليل. على سبيل المثال، يمكننا تقديم مهارة <dump bytes> لـ Claude Code والتي تسمح له بفحص البايتات في الذاكرة الافتراضية للثنائي، بحيث إذا رأى بعض البيانات المشار إليها من قوائم التفكيك/فك التجميع، يمكنه رؤية ما تحتويه تلك الذاكرة دون الحاجة إلى المرور عبر Ghidra.

الشكر والتقدير

شكرًا لـ Kryptos على صنع برنامج الكراك، ولـ Crackmes.one على استضافته. أقدر لهم استمرارهم من حيث توقف crackmes.de (رحمه الله).

شكرًا لـ Google على إطلاق نموذج محلي مذهل وأنيق.

شكرًا لوكالة الأمن القومي (NSA) لاستخدامها أموال ضرائبي لصنع أداة هندسة عكسية رائعة، وشكرًا لـ Ryan Kurtz على كل الدعم الذي يقدمه على Ghidra GitHub.

تنزيل الأداة