
محمّل Mach-O dylib داخل الذاكرة لبيئة بايثون القياسية في macOS؛ يفك تشفير الحمولات، ويعيّنها في الذاكرة، ويشغّلها دون استخدام dlopen أو الكتابة إلى القرص، مع دعم اختياري لتضمين ملف غلاف مشفّر.
dyen يقوم بتحميل مكتبة Mach-O dylib في الذاكرة باستخدام بايثون macOS الأصلية ويستدعي نقطة دخول مُصدَّرة دون dlopen على تلك المكتبة. الهدف هو ألا تكون الصورة ملفًا مستقلًا على الجهاز الهدف وألا تُسجَّل لدى dyld.
بقلم cenobyte [email protected] 2026
https://github.com/cenobyte-vincit/dyen
dyen هو مُحمّل Mach-O في الذاكرة يعتمد على المكتبة القياسية فقط (loader.py) لـ /usr/bin/python3 الأصلي على macOS. يقرأ مكتبة dylib عادية، أو كتلة DYEN v1 مستقلة (الغلاف المشفَّر)، أو ملف غلاف ملحقة به تلك الكتلة في نهايته، ويعيّن شريحة المضيف عبر mmap مجهول، ويطبّق إصلاحات rebase/bind التقليدية، ويستدعي dyld_main عبر مؤشر دالة ctypes.
encrypt_dyld.py هو أداة التحضير: يغلّف dylib ككتلة DYEN على stdout، أو ينسخ ملف غلاف إلى dyld_<basename> مع إلحاق الكتلة. الحمولة المرفقة dyld/dyld.c تطبع hello world فقط. loader.py خاص بـ macOS فقط؛ بينما يعمل المُشفِّر على Linux أو macOS.
يقرأ المُحمّل المضمّن مسارًا محليًا أو stdin. يمكن للموجّه (dropper) أو المرحلة الأولى جلب ملف الغلاف (أو البايتات الخام) عبر الشبكة وتمريرها إلى read_dyld_bytes()، بحيث لا تصل مكتبة dylib المشفَّرة أبدًا كملف مستقل على الجهاز الهدف.
.dyen، أو ملف غلاف.hello world). التشخيصات تذهب إلى stderr./usr/bin/python3 الأصلي (المكتبة القياسية فقط)loader.py خاص بـ macOS فقط. تجميع مكتبة dylib التجريبية يتطلب Apple clang على Mac. encrypt_dyld.py يعمل على Linux أو macOS ضد مكتبة dylib مبنية مسبقًا.
clang و make (لتجميع dyld/dyld.c)encrypt_dyld.pyruff, pylint, cppcheck (brew install ruff pylint cppcheck)make
يقوم هذا بتجميع build/libdyld.dylib (عالمي x86_64 + arm64)، ويكتب build/libdyld.dyen، ويدمج نفس مكتبة dylib المشفَّرة في pdf/dyld_c4611_sample_explain.pdf. ملف PDF الناتج غير مُلتزم به في المستودع؛ make ينشئه و make clean يحذفه. ملف الغلاف المصدر هو pdf/c4611_sample_explain.pdf.
مكتبة dylib العادية هي نتاج وسيط. في وقت التشغيل، يفك المُحمّل تشفير كتلة DYEN قبل التعيين، إما من ملف .dyen مستقل أو من ملف غلاف مع dyld المشفَّر الملحق في نهايته.
# Standalone DYEN blob to stdout (Makefile redirects to build/libdyld.dyen)
python3 encrypt_dyld.py build/libdyld.dylib CHANGEMEASAP > build/libdyld.dyen
# Hide encrypted dyld inside a cover file -> pdf/dyld_c4611_sample_explain.pdf
python3 encrypt_dyld.py build/libdyld.dylib CHANGEMEASAP pdf/c4611_sample_explain.pdf
# Use this Mac's serial number as the encryption key instead of a password (local testing)
python3 encrypt_dyld.py build/libdyld.dylib "$(python3 loader.py --serial)" > build/libdyld.dyen
# Use the target Mac's serial number as the encryption key (fictitious serial)
python3 encrypt_dyld.py build/libdyld.dylib C07XH4V2MD6T > build/libdyld.dyen
encrypt_dyld.py يعمل على Linux أو macOS. loader.py يعمل على macOS فقط.
واجهة سطر الأوامر للمُحمّل تأخذ مسار ملف واحد، أو تقرأ من stdin. لا يوجد بها خيار لكلمة مرور. فك التشفير يحاول أولًا استخدام الرقم التسلسلي لعتاد هذا Mac، ثم DEFAULT_DYLD_KEY (CHANGEMEASAP). إذا تعذّرت قراءة الرقم التسلسلي، يُجرَّب المفتاح المضمّن فقط. سطر الأوامر دائمًا يحمّل المدخلات ويستدعي dyld_main. انسخ loader.py إلى الجهاز الهدف.
/usr/bin/python3 loader.py build/libdyld.dylib
/usr/bin/python3 loader.py pdf/dyld_c4611_sample_explain.pdf
/usr/bin/python3 loader.py build/libdyld.dyen
cat build/libdyld.dylib | /usr/bin/python3 loader.py
cat pdf/dyld_c4611_sample_explain.pdf | /usr/bin/python3 loader.py
cat build/libdyld.dyen | /usr/bin/python3 loader.py
ملف غلاف بدون كتلة ملحقة يفشل:
/usr/bin/python3 loader.py pdf/c4611_sample_explain.pdf
يطبع loader.py --serial الرقم التسلسلي لعتاد هذا Mac (خاص بـ macOS فقط):
python3 loader.py --serial
اضبط LOADER_DEBUG على أي قيمة للحصول على تتبعات مفصلة إلى stderr:
LOADER_DEBUG=1 /usr/bin/python3 loader.py build/libdyld.dyen
loader.py و encrypt_dyld.py:
| الرمز | المعنى |
|---|---|
| 0 | نجاح |
| 1 | خطأ في الاستخدام، أو فشل فك التشفير، أو الرقم التسلسلي مفقود، أو خطأ إدخال/إخراج |
LOADER_DEBUG (أي قيمة) يطبع تتبعات مفصلة إلى stderr: مصدر الإدخال، فحوصات الترويسة/الـ magic، اكتشاف كتلة DYEN، فك التشفير، map/bind، حل الرموز. make يمرر LOADER_DEBUG=1 إلى encrypt_dyld.py افتراضيًا؛ make LOADER_DEBUG= يكتم تتبعات خطوة التشفير.
DEFAULT_DYLD_KEY في loader.py هو CHANGEMEASAP. التشفير يتطلب كلمة مرور صريحة أو وسيط الرقم التسلسلي للـ Mac إلى encrypt_dyld.py.
عدد التكرارات (PBKDF2_ITERATIONS في loader.py) يتبع التوصية الحالية في OWASP Password Storage Cheat Sheet لـ PBKDF2-HMAC-SHA256 (600,000 تكرار). PKCS #5 / RFC 8018 يعرّف PBKDF2 لكنه يترك عدد التكرارات للمنفِّذ؛ NIST SP 800-63B يسمح بـ PBKDF2 دون فرض رقم محدد. OWASP ينشر أهدافًا عملية ترتفع بمرور الوقت مع تسارع العتاد، لذا يجب مراجعة هذه القيمة دوريًا. إنها إرشادات لتمديد كلمات المرور طُبّقت هنا على المفتاح الاختباري، وليست ثابت بروتوكول دائم.
مضيف البناء:
make test
make lint # cppcheck (C) + ruff + pylint (Python)
make check # cppcheck only
make lint-python # ruff + pylint
make test يشغّل أدوات الفحص، ويبني مكتبة dylib التجريبية، ويشفّرها (.dyen مستقلة وتضمين PDF)، ويشغّل اختبارات التكامل تحت /usr/bin/python3. يتحقق كل اختبار تحميل من أن stdout يحتوي على hello world. تُشغَّل هذه الاختبارات على Mac في نفس الموقع. وهي ليست إثباتًا لبيئة تشغيل نظيفة.
يكتشف CrowdStrike Falcon على macOS هذه التقنية. أدوات EDR الأخرى غير مختبرة.
يطبّق المُحمّل rebase/bind التقليدي لـ LC_DYLD_INFO فقط. لا يتعامل مع الإصلاحات المتسلسلة (chained fixups) أو Objective-C أو Swift. يُستخدم dlopen فقط للاعتماديات النظامية الموقّعة من Apple (مثل /usr/lib/libSystem.B.dylib). الحمولة لا تُسجَّل أبدًا لدى dyld.
الإخفاء عبر الإلحاق في نهاية الملف (Append-at-EOF) مرئي في تفريغ سداسي عشري.
التسليح: غيّر DYENCRYPT قبل الاستخدام العملي. إن magic التجريبي في DYLD_BLOB_MAGIC (loader.py) هو توقيع ثابت. يمكن لقواعد YARA، وفاحصات الملفات، وطرق rfind البسيطة أن تستند إليه. استبدله بقيمة فريدة من اختيارك (أبقِ التشفير والمُحمّل متزامنين؛ الطول غير ثابت، لكن الأطول أفضل لملفات الغلاف المضمّنة).
مكتبة dylib المضمّنة في المستودع تطبع hello world فقط.