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

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

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

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

دليل الأدوات

الفئات

عرض جميع الفئات
Loading categories
amazon-mustang-hack — أبحاث استغلال نواة تحقق صلاحيات root مؤقتة على Amazon Fire 7 (Fire OS 7.3.3.1) عبر ثغرة use-after-free في Mali kbase JIT بالمعرّف CVE-2022-38181، مع سلسلة استغلال تعتمد على الكتابة فوق modprobe_path. | Kitploit
أدوات/GitHubGitHub/artur9010/amazon-mustang-hack
أمان أندرويدتصعيد الامتيازاتتحليل الثغرات الأمنيةالاستغلالالهندسة العكسيةأمن الجوالالأوراق والأبحاثتطوير الحمولاتاستغلال الملفات الثنائية

الأكثر شعبية

عرض الكل →

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

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

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

عرض جميع الأدوات →
مشاركة
GitHubartur9010/amazon-mustang-hack

amazon-mustang-hack

أبحاث استغلال نواة تحقق صلاحيات root مؤقتة على Amazon Fire 7 (Fire OS 7.3.3.1) عبر ثغرة use-after-free في Mali kbase JIT بالمعرّف CVE-2022-38181، مع سلسلة استغلال تعتمد على الكتابة فوق modprobe_path.

عرض المستودعالموقع الإلكتروني
منذ 13س 56دلم تتم المراجعة بعد

مشروع بمساعدة الذكاء الاصطناعي. تم إنتاج هذا البحث وتطوير الاستغلال والتوثيق بمساعدة الذكاء الاصطناعي باستخدام النموذجين GLM-5.3 و DeepSeek V4.1 Flash.

amazon-mustang-hack

بحث استغلال الجذر (root exploit) لجهاز Amazon Fire 7 الجيل التاسع (mustang, MT8163, Mali-T720) على البرنامج الثابت النهائي — Fire OS 7.3.3.1, PS7331.4463N, kernel 4.9.117 (built 2025-05-03, SPL 2024-08-01).

الهدف: LineageOS. مسار الـ bootloader ميت على هذه الوحدة (bootrom مُرقّع — preloader فقط عبر CMD short)، لذا المسار الوحيد المتبقي هو استغلال برمجي للنواة (kernel exploit).

بداية سريعة```

one-shot: build, run the exploit (retries across the probabilistic reclaim),

install a setuid-root su and verify it as an unprivileged user

nix-shell -p android-tools --run './run.sh' # add -p zig too if no zig

root@kitploit:~
عند النجاح:```
/data/metrics/su id     # run a command as root
/data/metrics/su        # interactive root shell

يفوز reclaim بنحو إقلاع واحد من كل 3، والخسارة تؤدي إلى panic/إعادة تشغيل الجهاز اللوحي؛ run.sh ينتظر فقط إعادة التشغيل ويعيد المحاولة. يتم فرض SELinux إلى Permissive كجزء من الاستغلال، لذا فإن root يعمل وقت التشغيل فقط — إعادة التشغيل تستعيد الوضع الأصلي وتعيد تشغيل run.sh.

يتم تضمين st3 و su المبنيين مسبقًا (armv7 static)، لذا لا حاجة إلى toolchain للتشغيل. ./run.sh --build يعيد بناءهما من poc/*.c إذا كان لديك zig.

كل ما هو أدناه مجرد سجل للعمل الذي قام به النموذج، لا يوجد إدخال بشري أدناه.

الهدف الأساسي (منذ الجلسة 5): kbase CVE-2022-38181 — المرحلة 2 مُثبتة

GhostLock (أدناه) متوقف مؤقتًا: متغير rtmutex BUG_ON الخاص بـ MTK + عدم الكشف عن عنوان kernel من shell = طريق مسدود معماري على هذا البناء (الجلسات 2-4). تمت إعادة تشخيص kbase JIT UAF (كان "panic غير المشروط" في destroy-worker هو JIT_FREE deref، وفُقد السجل بسبب موت adbd في منتصف الـ panic) والمرحلة 2 الآن مُثبتة بالأوراكل — انظر قسم الجلسة 5.

متوقف مؤقتًا: GhostLock، CVE-2026-43499

rtmutex remove_waiter() futex-PI stack-UAF (إفصاح NebuSec 2026-07، الإصلاح 3bfdc63936dd وصل 2026-04). النطاق المعرّض 2.6.39–7.1 → إصدارنا 4.9.117 (مايو 2025) متأثر.

تم التحقق على بنائنا الدقيق:

  • CONFIG_FUTEX=y، rtmutex مُجمّع، الخلل موجود حرفيًا: rtmutex.c:1108-1111 يستخدم current->pi_lock/current->pi_blocked_on (يجب أن يكون waiter->task)؛ موقع الاستدعاء المعطوب rtmutex.c:1723 (مسار خطأ rt_mutex_start_proxy_lock)
  • سطح التشغيل = استدعاءات futex خالصة (WAIT_REQUEUE_PI/CMP_REQUEUE_PI)، لا device node، لا شيء محمي بـ SELinux — العوائق القاتلة في مسار kbase غير موجودة هنا
  • المستهلك: sched_setattr → __sched_setscheduler → rt_mutex_adjust_pi(p) عند sched/core.c:4706 — يعمل deref لـ القديم ✓

TODO (خطة المنفذ)

  1. كتابة trigger (3-thread requeue-PI deadlock، الأنوية 0-3) — منفذ من exp32/main.c
  2. هندسة الطبع: إزاحة إطار rt_waiter مقابل منطقة do_sys_select fd_set — فك تجميع vmlinux الخاص بنا (do_sys_select stack_fds مقابل إطار futex_wait_requeue_pi)، كشف STAMP_NFDS/STAMP_WAITER_OFF كمعاملات قابلة للضبط
  3. ترميز fake-writer لـ arm32 48-byte waiter → فتحات "write V to ADDR"
  4. 2 slots → modprobe_path، تشغيل، root script
  5. بدائل إذا لم يصل select-stamp: setsockopt(MCAST_JOIN_SOURCE_GROUP) stamp

الحالة

  • Bootrom (طريقة amonet العتادية) — مُرقّع على هذه الوحدة، طريق مسدود
  • mtk-su (CVE-2020-0069) — مُرقّع، Failed critical init step 3
  • مسح سطح الهجوم — /dev/mali0 world-RW + SELinux gpu_device، kbase r26p0-01rel0
  • CVE-2022-38181 مؤكد في مصدر البناء الدقيق؛ trigger المرحلة 1 يعمل
  • CVE-2026-43499 (GhostLock) تم التحقق منه لكنه محظور: متغير rtmutex BUG_ON الخاص بـ MTK + عدم الكشف عن عنوان kernel من shell (الجلسات 2-4)
  • CVE-2022-38181 المرحلة 2 مُثبتة (الجلسة 5): panic في destroy-worker كان تشخيصًا خاطئًا؛ إعادة توجيه UAF إلى المنطقة المرشوشة، مُتحقق بالأوراكل
  • المرحلة 1: trigger + stamp + consumer (crash = السلسلة حية)
  • المرحلة 2 (مسار kbase): إعادة توجيه UAF إلى المنطقة المرشوشة — مُثبتة الجلسة 5
  • المرحلة 2b: تحكم raw-byte slot (تقلب xattr stamp) → unlink write
  • المرحلة 3: استدعاء دالة kernel عشوائية → ROOT (الجلسة 10) — اختطاف nf LOCAL_OUT hook، سلسلة المكونة من حزمتين: تصفير ، إعادة كتابة fake entry إلى . ، SELinux Permissive.

النتائج الرئيسية

الجهاز / البرنامج الثابت

  • الطراز KFMUWI، الجهاز mustang، Fire OS 7.3.3.1 PS7331.4463N/0031575863040
  • Kernel 4.9.117-g08fe75b-dirty، بُني السبت 3 مايو 01:25:15 UTC 2025 (Linaro GCC 6.3-2017.05)
  • أعادت Amazon بصمت إصدار 7.3.3.1 في مايو 2025 (incremental جديد، نفس سلسلة الإصدار)
  • مراجعة Bootrom بعد 2020: قصر إلى GND على eMMC CMD يعطي preloader فقط (مُرقّع)
  • /dev/kb، /dev/dkb (أقسام النسخ الاحتياطي لنواة Amazon) root:drmrpc 0660 — مقفلة

لماذا ينطبق CVE-2022-38181

  • Driver: mali_kbase r26p0-01rel0 (Midgard، Mali-T720)، داخل نطاق NVD المتأثر r4p0–r31p0
  • إعادة بناء Amazon في مايو 2025 شحنت خلل 2018 حرفيًا — لا backport
  • الكود المعرّض الدقيق، مُتحقق من المصدر:
    • mali_kbase_mem.c:2721 kbase_jit_destroy_worker يحرر المنطقة، لا يمسح kctx->jit_alloc[id] أبدًا
    • mali_kbase_softjobs.c:1270 kbase_jit_free_finish يعمل deref لـ jit_alloc[ids[j]] القديم
    • mali_kbase_mem.c:3138 kbase_jit_backing_lost → مسار destroy (يُفعّل أثناء reclaim)

مناخ الاستغلال (كل شيء مُتحقق من config dump الحي + OTA vmlinux)

  • armv7 32-bit، غير LPAE → لا KASLR (kernel عند 0xc0008000 VA ثابت / 0x40080000 PA)
  • لا ARM_SW_DOMAIN_PAN → ret2usr قابل للتطبيق؛ CONFIG_PANIC_ON_OOPS=y (المحاولات الفاشلة = إعادة تشغيل)
  • لا SLAB_FREELIST_RANDOM/HARDENED، لا CONFIG_USER_NS/USERFAULTFD/NF_TABLES
  • CONFIG_MODULES=y، لا STATIC_USERMODEHELPER → الكتابة فوق modprobe_path = root
  • 1 GB RAM → direct reclaim (المطلوب للإخلاء) يمكن الوصول إليه بسهولة؛ الضغط >~1 GB يؤدي إلى panic في kernel من تلقاء نفسه (خلل lowmem/OOM غير ذي صلة) — أبقِ spray ≤ 900 MB، استخدم ~700 MB

غرائب UAPI التي واجهناها أثناء تطوير PoC (r26p0، _IOC_TYPE 0x80)

  • اتحاد MEM_ALLOC بحجم 32 بايت (in يحتوي على 4 × u64 بما في ذلك extent)
  • يجب أن تتضمن flags BASE_MEM_PROT_GPU_RD|WR (بتات 2|3)، وليس R|W القديمة
  • mmap لصفحة التتبع مطلوب قبل أي alloc: mmap(fd, offset=3<<12, PROT_NONE)
  • يجب أن تساوي stride في JOB_SUBMIT قيمة sizeof(base_jd_atom_v2) = 48 (base_jd_prio/base_jd_dep_type هي typedefs من نوع u8)
  • JIT: MEM_JIT_INIT (nr 14، بنية v2)، alloc/free هي soft jobs عبر JOB_SUBMIT (BASE_JD_REQ_SOFT_JIT_ALLOC=0x209، ...FREE=0x20a؛ =مؤشر مستخدم، =العدد)

تفاضل المرحلة 1 (إثبات أن الخلل يُفعّل)

يحدث panic أثناء الإخلاء نفسه (evictable_reclaim_scan_objects → backing_lost → destroy worker) — يتم اجتياز المراجع المعلقة (jit_alloc[]، قائمة evict) قبل أن نرسل JIT_FREE أبدًا. يجب أن تفوز المرحلة 2 بالسباق: إعادة تخصيص kbase_va_region المحرر بـ spray MEM_ALLOC الخاص بنا بينما الضغط لا يزال جاريًا.

المخرجات

  • poc/stage2.c — استغلال المرحلة 2 (الأوضاع: step/uaf/spstep/spfree/spray/keys) — spray 700 = تشغيل أوراكل كامل؛ ينجو ويتوقف مؤقتًا (اقتل للتنظيف)
  • poc/mustang_jit_uaf.c — PoC المرحلة 1 (الأوضاع: jit N / control N / pressure N)
  • poc/build.sh — بناء متقاطع بـ zig (static musl armv7)
  • kernel/vmlinux — الرموز المستعادة من بناء OTA الدقيق (vmlinux-to-elf)
  • — تفريغ من الجهاز قيد التشغيل

البناء والتشغيل```

nix-shell -p zig --run 'zig cc -target arm-linux-musleabihf -static -O2 -o juaf poc/mustang_jit_uaf.c' adb push juaf /data/local/tmp/juaf && adb shell chmod 755 /data/local/tmp/juaf adb shell /data/local/tmp/juaf jit 700 # full trigger (~reboots device) adb shell /data/local/tmp/juaf control 700 # no-JIT control adb shell /data/local/tmp/juaf pressure 700 # raw memory-pressure control

root@kitploit:~
## المراجع

- إرشاد GHSL-2022-054: https://securitylab.github.com/advisories/GHSL-2022-054_Arm_Mali/
- تدوينة Mo عن استغلال Pixel 6: https://github.blog/2023-01-23-pwning-the-all-google-phone-with-a-non-google-bug/
- سابقة Fire HD 10 (trona)، نفس عائلة العلّة: ericpardee.github.io/fire-hd-ownership
- بوابة Amazon للمصادر المفتوحة: amazon.com gp/help/customer/display.html nodeId=200203720
- خيط فتح XDA (ميت لهذه المراجعة العتادية): xdaforums.com/t/fire-7-2019-mustang-unbrick-downgrade-unlock-root.3944365/


## ملحق الجلسة 3 (مسح معمّق لطوابع نداءات النظام)

أعماق النسخ المصدرية المقيسة (مطلقة مقابل sp0 عند مدخل نداء النظام؛ يمتد المُنتظِر من -0x1d8 إلى -0x1a8):
- sendto sockaddr @ -0xc4 | process_vm iov @ -0x104 | recvmsg iov @ -0x11c
- sendmsg iov @ -0x12c | recvmmsg iov @ -0x154 | select fds @ -0x174
- pselect6 fds @ -0x19c | **sendmmsg iov @ -0x1bc (الأفضل — أقصر بـ 0x1c من waiter+0x00)**
- poll entries @ -0x3e0 (بالكامل أدناه؛ الجانب الخاطئ)

استُبعدت في هذه الجلسة:
- سلسلة io_submit ضحلة جدًا (~-0x130) | semtimedop: CONFIG_SYSVIPC=n (stub)
- configfs مُثبّت لكن بصفر أنظمة فرعية مسجّلة (لا أهداف mkdir)
- /sys/kernel/debug، /config: مرفوضة من SELinux للصدفة
- /proc/sys/kernel: getdents يعمل (29 مدخلًا مُدرجًا)، فقط pid_max قابل للفتح؛
  قراءات kptr_restrict/hotplug/hostname/domainname كلها مرفوضة
- قيمة الكتابة دائمًا waiter+0 (عنوان مكدّس النواة، قابل للتنفيذ، shellcode عند
  +0x1c): rb_link_node *link = node، insert_color يكتب parent-color — لا
  يوجد متغيّر بقيمة متحكَّم بها دون ختم حقول الشجرة (فجوة -0x1d8..-0x1bc)
- حقول الإرسال ذات الإشارة المزدوجة تقرأ *(waiter+0)=1، *(waiter+4/+8)=0 — BLX 1/0
  (nf_hooks، net_families، inet[6]_protos، seq_file->op كلها ميتة)
- timer_list.function@+0xc و work_struct.func@+0xc ستقرأ *(waiter+0xc) =
  pi_tree self-ptr = waiter+0xc قابل للتنفيذ — لكن لا مسار يضع waiter+0 كـ
  timer/work (تلف الروابط / لا مصادر طابور غير مباشرة)
- جذر الشجرة غير صفري (فتحة معالج sysctl) = تتبّع مؤشر حتمي عبر
  .text كشجرة rb؛ ينتهي عند كلمة صفرية — قابل للمحاكاة دون اتصال، لكن الهبوط
  في فتحة قابلة للكتابة مفيدة غير معقول

خيوط متبقية للجلسة 4:
1. مسارات ioctl العميقة: نسخ dev_ioctl ifreq (40B بيانات مستخدم) — قِس
   عمق سلسلة SyS_ioctl→sock_ioctl→dev_ioctl مقابل -0x1d8
2. أي نسخ آخر أعمق بـ 0x1c من sendmmsg (لم يُعثر على شيء بعد)
3. إذا فشل البحث عن سطح الختم: أعد النظر في البنى المتسلسلة بالمشي أو
   ابحث عن أصناف فتحات قابلة للكتابة-الصفرية-المُستدعاة لم تُعدّد بعد


## الجلسة 4 — الاختراقان

### 1. ملف rtmutex_common.h من MTK هو اللغز بأكمله
استبدلت MTK النسخة الأصلية الآمنة ضد NULL rt_mutex_top_waiter بـ:```c
w = rb_entry(lock->waiters_leftmost, struct rt_mutex_waiter, tree_entry);
BUG_ON(w->lock != lock);   // compiled to: ldr sb,[lock+8]; ldr r3,[sb+0x1c]; cmp; bne→udf#0x12

NO NULL CHECK + BUG_ON. كل مرساة صفرية بالكامل تموت عند *(NULL+0x1c)؛ المراسي العشوائية تموت عند udf. THE WALK REQUIRES: lock->waiters_leftmost (lock+8) يجب أن يشير إلى waiter وهمي W (قابل للكتابة) مع W->lock (+0x1c) == lock.

تم رسم تدفق المشي بالكامل (rt_mutex_adjust_prio_chain @ 0xc0189a58):

  • 9b44-9b54: retry head؛ pi_blocked_on==NULL → خروج نظيف ret 0
  • 9abc-9adc: orig_waiter==NULL → تخطي فحوصات pi_waiters (adjust_pi يمرر NULL دائمًا)
  • 9b10-9b28: فحص prio (prio==task->prio + MIN → خروج 9b58)
  • 9b2c-9b38: trylock(lock+0) — ticket؛ يفشل → حلقة إعادة المحاولة مع bailout العداد (9a90-9aa8، الحد @ *(0xc11189c8))
  • 9ba4-9bc0: فحوصات الجمود
  • 9bcc-9bd8: THE BUG_ON (leftmost→W→W->lock==lock أو الموت)
  • 9bdc-9c04: dequeue (شجرة متبقية RB_CLEAR_NODE'd = EMPTY → تخطي آمن)، كتابة prio/deadline
  • 9c04 bl: rt_mutex_enqueue → *link = waiter+0 عند lock+4 ← THE WRITE
  • 9c3c+: owner==NULL → مسار خروج نظيف

2. تسريب عنوان مكدس النواة (يقتل متطلب عدم وجود عنوان)

/proc/self/task//stat الحقل 28 (kstkesp) يُرجع SP حقيقي للنواة للخيوط المحجوبة بـ syscall من سياق SHELL (تم التحقق: قيم غير صفرية ملاحظة).

  • waiter يحجب في read(blocking_pipe) → stat → kstkesp
  • قاعدة المكدس = kstkesp & ~0x1fff (arm32 THREAD_SIZE=8192)
  • rt_waiter abs addr = base + delta ثابت (قابل للحساب: sp0 = base+0x2000-0x48 pt_regs؛ waiter = sp0-0x1d8)
  • جميع قيم الطابع المرجعية الذاتية تصبح قابلة للحساب!

طابع ذاتي الاتساق كامل (بعد التسريب):

  • L = waiter+0x24 (قفل وهمي IN THE WINDOW — جميع الكلمات الأربع قابلة للتحكم)
  • iov[0].base (waiter+0x1c lock) = L
  • iov[0].len (waiter+0x20 prio) = 1 (≠139)
  • W = waiter+0x1c؛ stamp *(W+0x1c) = *(waiter+0x38) = L (BUG_ON يمر)
  • lock+0 (waiter+0x24) = 0؛ lock+4 (waiter+0x28) = 0 (الكتابة تهبط هنا)؛ lock+8 (waiter+0x2c) = W؛ lock+0xc (waiter+0x30) = 0 (owner NULL)

حالة Oracle

  • crash أثناء المشي = المشي عمل (dead-lock probe: crash حتمي، كود نظيف)
  • مشي نظيف + لا كتابة = trylock-fail retry-bailout (kptr anchor: كلمة وقت التشغيل غير صفرية)
  • كل شيء الآن حتمي بعد إصلاح التلوث.

TODO للجلسة القادمة

  1. تنفيذ التسريب: waiter يحجب على pipe، main يقرأ stat، يحسب base
  2. ختم نافذة ذاتية الاتساق، إطلاق المشي → إكمال بدون crash = كتابة مُثبتة
  3. تسليح: الكتابة تهبط دائمًا عند lock+4 (rb_link_node) — يجب أن يعيش lock في النافذة (الذاكرة الوحيدة المتحكم بها بالكامل)، لذا بحث اختيار الهدف: إما إيجاد حيلة called-slot-in-window، أو بناء على مرحلتين.

SESSION 4 FINAL STATE — THE WALL (مُوصَّف بدقة)

الصورة الكاملة

المشي يُطلق بشكل حتمي (dead-lock probe: crash في كل مرة، كود نظيف). الكتابة لا يمكن أن تهبط بسبب مصادفة تصلب النواة ثلاثية الاتجاه:

  1. متغير MTK rtmutex BUG_ON: lock+8 (leftmost) يجب أن يشير إلى W مع *(W+0x1c)==lock. المراسي الصفرية/العشوائية تموت. لا يوجد نمط مرجعي ذاتي ثابت (6571 مرشحًا تم فحصهم، 0 إصابة). مؤشرات وقت التشغيل غير معروفة.
  2. لا إفصاح عن عنوان النواة من shell:
    • kstkesp على arm32 = USER SP (task_pt_regs->ARM_sp) — ليس مكدس النواة. DEAD.
    • dmesg/pstore/pagetypeinfo/kallsyms/stack — كلها مرفوضة.
    • kptr_restrict=1 في وقت التشغيل (كلمات fops anchor أيضًا غير صفرية في وقت التشغيل — تشغيل kptr anchor خرج عبر trylock-fail retry-bailout، وليس نجاح trylock)
  3. جداول fops في rodata: trylock strex يُجهض (انهيارات مسح الجلسة 3).

نافذة الطابع (waiter+0x1c..0x5b) هي الذاكرة الوحيدة المتحكم بها ومعروفة المحتوى، لكن عنوانها هو المجهول الذي نحتاجه. البنى المرجعية الذاتية كلها تتطلب ختم عنوان نواة كثابت — دائري بدون تسريب.

مقارنة gitchw (لماذا نجحت كتابتهم على ARM32، كتابتنا لا يمكنها بعد)

نواة 5.4 الخاصة بهم لديها UPSTREAM rtmutex_top_waiter (آمن من NULL: if (!leftmost) return NULL) — مراسي الشجرة الفارغة تنجو، كتابتهم هبطت على null_fops (قابل للكتابة على نواتهم). حتى هم عالقون عند الإرسال ("ioctl reboot"). شجرة Mustang 4.9.117 MTK لديها متغير BUG_ON — أجهزة Fire OS 8 اللوحية (GhostLock-5.10) نجحت لأن نوى 5.10 الخاصة بهم بأسلوب upstream.

حقائق الجلسة 4 المُتحقق منها

  • حلقة إعادة محاولة المشي لديها bailout عداد (الحد @ *(0xc11189c8))؛ trylock-fail على كلمات anchor غير صفرية في وقت التشغيل → خروج retry-bailout نظيف (تشغيلات kptr anchor)
  • مسار No-requeue (9ce4، FULL walk) أيضًا يُشير إلى leftmost عند 9d64 — لا مفر
  • مؤشرات RB_CLEAR_NODE الذاتية موجودة كمخلفات في النافذة (waiter+0 و +0xc تحتوي على عناوينها الخاصة) لكن لا يوجد فحص-مقارنة يستخدمها بطريقة تتجنب ختم عناوين معروفة
  • مرشحو mutex الحقيقي (chain mutex لديه waiter حي = BUG_ON سيمر) — لكن &chain_mutex عنوان heap، غير قابل للوصول بدون تسريب

خيارات الجلسة القادمة (مرتبة)

  1. مطاردة مؤشر النواة في logcat: Amazon HALs/daemons ثرثارة؛ أي مؤشر نواة مسجل (حتى قديم) يفتح البناء. رخيص للاختبار.
  2. سلوك /proc/net %pK على هذا البناء: بعض أشجار 4.9 تطبع مؤشرات غير مُجزأة في /proc/net/tcp,udp,unix للقراء غير المميزين. اختبر مباشرة.
  3. مسارات خروج الخيط على pi_blocked_on المتدلي (لمرة واحدة، derefs مختلفة).
  4. إعادة النظر في خطأ kbase JIT المؤجل مع المعرفة المتراكمة عن 4.9.

SESSION 4 ADDENDUM — LEAK HUNT: EXHAUSTED (نهائي)

تم اختباره وميت من نطاق shell:

  • /proc/net/{tcp,unix,packet,netlink,ptype}: %pK-hashed إلى 00000000 (kptr_restrict=1)
  • /proc/timer_list: قابل للقراءة لكن المؤشرات %pK-zeroed (الرموز مرئية، لا عناوين)
  • logcat: لا مؤشرات نواة في ثرثرة Amazon/wpa
  • kstkesp (stat f28): USER SP على arm32 (task_pt_regs->ARM_sp)
  • عقد MTK (/proc/ged, mtk_cmdq_debug, mtktz, ptp, chip, aed, driver/*): كلها مرفوضة من SELinux
  • /proc/{iomem,vmstat,kmsg,keys,crypto,slabs...}: مرفوضة
  • /sys/kernel/notes: مرفوضة
  • CONFIG_VECTORS_BASE=0xffff0000 (متجهات عالية — NULL+0x1c يُخطئ)
  • CONFIG_KUSER_HELPERS=y (kuser عند 0xffff0000، ليس الصفحة 0)

الخلاصة: GhostLock على mustang يتطلب إفصاحًا عن عنوان النواة لا تكشفه هذه النواة لنطاق shell. القفل الوهمي المرجعي الذاتي لا يمكن بناؤه بدونه.

DECISION POINT

(a) طحن حتمي عند الإقلاع: إعادة تشغيل → معايرة عنوان المكدس عبر crash oracle (~20-30 إعادة تشغيل)، التحقق من قابلية التكرار. فرصة بعيدة — تخصيص مكدس الخيط في الإقلاع المتأخر غير مرجح أن يكون مستقرًا. (b) PIVOT مرة أخرى إلى kbase CVE-2022-38181 مع الأصول المتراكمة: vmlinux للبناء الدقيق + المصدر الكامل + toolchain + انضباط تتبع O_SYNC + معرفة عميقة بـ 4.9. العائق الأصلي (panic destroy-worker أثناء إخلاء JIT) هو مشكلة توقيت spray، مفهومة الآن بشكل أفضل. (c) التوقف عند ~45% الصادقة: trigger مُثبت، المشي مُرسم حتى التعليمة، الكتابة محجوبة بواسطة MTK BUG_ON + لا تسريب.

موصى به: (b) — خطأ kbase مُتحقق من وجوده في هذا المصدر الدقيق، كان لديه trigger عامل، وعائقه ميكانيكي، وليس معماريًا.

SESSION 5 — STAGE 2 PROVEN (تم تنفيذ الخيار b)

إعادة التشخيص: "panic destroy غير المشروط" لم يكن موجودًا قط

وضع step (alloc id=1 → DONT_NEED → ضغط 700MB → MEM_QUERY، بدون free) ينجو: query=-1 (المنطقة حُررت بواسطة destroy worker، rbtree-clean). مسار worker مطابق بايت ببايت لتدفق JIT_FREE-تحت-الضغط القانوني. انهيار الجلسة 1 كان دائمًا deref المتدلي JIT_FREE؛ سطر السجل الخاص به ضاع لأن panic يقتل adbd أثناء flush. تم التحقق مرتين إضافيتين مع وضع uaf (free عاري → panic، نفس قطع السجل). تدفق GHSL-2022-054 حي بالكامل على هذا البناء.

جرد البدائيات الكامل (تفكيك vmlinux الدقيق)

kbase_jit_free(kctx, reg) @ 0xc058495c مع reg وهمي متحكم به بالكامل:

  • reg->cpu_alloc NULL → حجم مدعوم 0 → كتلة trim متخطاة (0xc0584978)
  • إنقاص bin: kctx+0x147dd (بايت) + kctx+0x147de+bin_id (بايت)
  • mark_reclaim(reg->gpu_alloc) @ 0xc059b158: سلسلة K=*(gpu_alloc+0x38) → *(K+0x1429c)==0 يتخطى mm-atomics → atomic_sub nents@K+0x141c8، D=*(K+4) → atomic_sub nents@D+0x538. مع nents=0 جميع الكتابات هي مخازن no-op (strex لنفس القيمة).
  • reg->flags |= 0x100000 (كتابة في الوهمي، حميدة)
  • shrink_cpu_mapping يخرج مبكرًا عندما new==old (nents=0 → return)
  • list_add(gpu_alloc->evict_node, &kctx->evict_list): رأس evict_list @ kctx+0x1427c؛ كتابات في gpu_alloc+0x18/0x1c (يجب أن تكون قابلة للكتابة)
  • مسار WARN (0xc0584bd4) غير قاتل (لا panic_on_warn) ويستمر
  • UNLINK @ 0xc0584b08/b0c: r3=*(reg+0x3c) prev, r2=*(reg+0x38) next → *(next+4)=prev; *(prev+0)=next — كتابتان تعسفيتان write-what-where، ثم إعادة ربط reg+0x38 في jit_pool_head @ kctx+0x148e8

سلسلة fake-gpu_alloc الثابتة (مسح vmlinux دون اتصال، /tmp/opencode/scan_s.py)

9 مرشحين؛ S=0xc118b7ec (بيانات xfrm، خاملة على هذا الجهاز): *(S+8)=0 (nents)، *(S+0x18)=S+0x18 (evict_node فارغ → لا WARN)، K=*(S+0x38)=0xc118b820 → *(K+0x1429c)=0، K/D+0x141c8/+0x538 كلها في بيانات قابلة للكتابة. أهداف Oracle مُجهزة: init_uts_ns.name.nodename=0xc110d561 ("(none)"، قابلة للقراءة عبر uname)، scratch P=0xc118bd58 (أصفار xfrm). تجنب S=0xc111cba4 (ملاصق لـ tracepoint). CONFIG_DEBUG_RODATA=y → جميع أهداف الكتابة يجب أن تكون في .data/.bss (bss 0xc11d9000-0xc12d9000).

هندسة Spray (ما نجح، وما لم ينجح)

  • add_key (CONFIG_KEYS=y): مرفوض من SELinux لـ shell. ميت.
  • مخزن قيمة setxattr: kvmalloc(96)+copy_from_user يحدث قبل فحص SELinux → رقصة alloc محصنة من SELinux حتى عندما يفشل الاستدعاء؛ عابر (يُحرر عند نهاية syscall)، البايتات تستمر عند +4..95 (مؤشر freelist يطمس +0..3 = rblink، غير مستخدم بواسطة kbase_jit_free)
  • kbase_va_region نفسه: kzalloc(72) → kmalloc-96! MEM_ALLOC(va=0x40, commit=0x10) يضع فقط المنطقة في kmalloc-96 (phy alloc → 384) → نوع استرجاع حتمي. ضحية منطقة حقيقية تجعل kbase_jit_free يكتمل عبر حالة قانونية بالكامل (jit_node فارغ → self-unlink).
  • spray متسلسل بعد الضغط: يخطئ دائمًا — worker يحرر الفتحة في منتصف الضغط إلى slabs جزئية (SLUB: free إلى slab غير نشطة ≠ cpu freelist)؛ تحت الضغط تفشل تخصيصاتنا → حجم صافي صفري → لا دوران
  • Sprayers مثبتة + caps: لا تزال تخطئ (512-cap مستنفد قبل الإخلاء؛ worker قد يعمل على أي cpu)
  • الفائز: spray commit_pages=0 — لا صفحات فيزيائية → MEM_ALLOCs تنجح خلال عاصفة الضغط بأكملها → ~6000 تخصيص صافي → دوران القائمة الجزئية مضمون. 8 خيوط (2/cpu، cpus 0-3 مثبتة — /proc/cpuinfo مُرشح لـ shell إلى نواة واحدة، استخدم Cpus_allowed_list) + طفل ضغط مثبت على cpu0 + دفعة احتفاظ بـ 16 تخصيصًا بعد join.
  • فخ query(jit_va): بعد الاسترجاع، مناطق spray تعيد استخدام VA المحرر في المنطقة المخصصة → query=0 غامض (حي-أصلي مقابل spray-يغطي-VA)

ORACLE HIT — إعادة توجيه مُتحقق منها آليًا

تشغيل spray 700 2026-09-11: 5895 منطقة مرشوشة أثناء الضغط، JIT_FREE على id=1 المتدلي اكتمل على منطقة مُسترجعَة، ثم JIT_ALLOC(0x40, bin 0) مشى jit_pool_head وأرجع VA المنطقة المرشوشة #4251 (0x142701000) — المنطقة الدقيقة التي استهلكها المؤشر المتدلي. قتل العملية بعد: تفكيك kctx نظيف، لا crash. Stage 2 مكتمل: إعادة توجيه UAF حتمية مع نوع كائن ومحتويات متحكم بها.

خطة Stage 3 (unlink بالبايت الخام)

استرجاع نوع المنطقة يعطي نجاة الرقصة القانونية لكن jit_node مُهيأ ذاتيًا → لا بدائية unlink. نحتاج بايتات خام عند +0x38/+0x3c:

  1. ختم xattr: تبادل دفعات تخصيص المنطقة (الحجم الصافي → دوران slab) مع عواصف xattr (اختم كل فتحة رأس، البايتات تستمر بعد free) → نافذة هادئة → deref
  2. أو sendmsg cmsgs مثبتة (optmem_max=10240 → ~106 × 96B محتفظ بها)
  3. ثم: W1 *(N+4)=P مع P=صفحة shellcode في userland (لا PAN!) — المرشحون: const fops هي .rodata (DEBUG_RODATA) → استهدف fn ptr غير const في .data، أو رأس قائمة binfmt formats، أو sysctl proc_handler (تحقق من قابلية كتابة الجدول). احتياطي: modprobe_path عبر كتابات مسلسلة بالبايت (القيم يجب أن تكون عناوين قابلة للكتابة — استخدم أهدافًا بشكل مؤشر)
  4. لا KASLR + vmlinux دقيق: prepare_kernel_cred 0xc0149e3c، commit_creds 0xc014993c

SESSION 5B — STAGE 3: السلاح مبني، سباق الاسترجاع لم يُكسب بعد

تم

  • سلاح Stage-3 مكتمل ومُجهز (poc/stage3.c):
    • الهدف: kern_table[pid_max].proc_handler @ 0xc1113f40 (قابل للكتابة .data، تم التحقق عبر مسح مؤشر السلسلة + handler == proc_dointvec_minmax)
    • N = مدخل shellcode 0x11111112 (mmap 0x11111000؛ W1 يطمس entry+4، متخطى بواسطة b +8؛ W2 يكتب N عند P = حقل handler)
    • shellcode arm32 ring0 مُشفَّر يدويًا: prepare_kernel_cred(0) + commit_creds + ret 0؛ trigger = read /proc/sys/kernel/pid_max (قابل للقراءة من shell)؛ يعمل في سياق مهمته الخاصة → creds تنطبق علينا
    • وضع oracle الحميد يكتب uts nodename (0xc110d561، غير محاذي مقبول)
  • اختيار بدائية Spray:
    • دبابيس NETLINK_USERSOCK sendmsg (نسخة msg_control kmalloc-96، محتفظ بها أثناء الحجب، لا تُحلل أبدًا): مرفوضة من SELinux (إنشاء socket EACCES)
    • unix/UDP sendmsg: تحليل cmsg يسمم بايتات الحمولة ✗
    • أحداث inotify: inotify_handle_event يخصص kmalloc name_len+0x1d، بايتات الاسم (متحكم بها بالكامل، قيد خالٍ من NUL/slash) عند event+0x1c؛ name_len=60 → kmalloc-96؛ في قائمة الانتظار → محتفظ بها؛ 4 نسخ → 4 أحداث لكل rename؛ SELinux-OK من shell. الوهمي أُعيد تصميمه خاليًا من NUL: cpu_alloc يشير إلى S (nents@S+8 = 0 → نفس دلالات NULL)
  • السلسلة الميكانيكية تم التحقق منها من البداية إلى النهاية (drain4، تشغيل بدون إخلاء): 20K حدث مُستنزف + 13.5K renames متعددة النوى زائدة + JIT_FREE + oracle + pause، كلها نظيفة. سجل O_SYNC (/data/local/tmp/s3.log) ينجو من panics — تحقيق جنائي دقيق لنقطة الانهيار.

محاولات الاسترجاع على الفتحة المحررة (كلها أخطأت حتى الآن)

variantresult
rename sprayers متزامنة أثناء العاصفة

الفرضية العاملة: سباق storm-junk — بين تحرير destroy worker للفتحة (في منتصف العاصفة) وقتل الطفل/الهدوء، نشاط استرجاع متبقٍ يأخذ الفتحة الحرة الوحيدة في slab الضحية ببايتات غير حمولة. spray المنطقة (stage 2) يفوز لأنه يخصص باستمرار أثناء العاصفة؛ renames لا تستطيع.

المزالق التي واجهناها

  • خطأ مفتاح spray: مصدر rename يجب أن يكون اسم الحمولة (كان اسمًا مؤقتًا → ENOENT بعد موجتين → 512 حدث فقط على الإطلاق)
  • اسم مضيف الجهاز هو "localhost"/يتغير — oracle يقارن قبل/بعد
  • st3 متوقف + pkill → الجهاز WEDGE (تفكيك مع 33K حدث؟!) — اقتل العمليات المتوقفة فقط عبر إعادة التشغيل؛ ثاني hard-wedge في الجلسة
  • /proc/cpuinfo يُظهر 1 cpu لـ shell؛ استخدم Cpus_allowed_list

الخطوات التالية (مرتبة)

  1. drain4 @ 500MB (عتبة الإخلاء مؤكدة هناك)، استطلاع 1ms، قتل فوري، ذيل 4-cpu × 3200 — قلص نافذة العاصفة
  2. churn ختم xattr (تخصيص-نسخ setxattr يحدث قبل فحص SELinux — محصن من SELinux) متزامن مع العاصفة + دوران المنطقة، ينتهي بختم
  3. اقبل استرجاع المنطقة (مُثبت) + ابحث عن بدائية مرحلة ثانية على حالة ضحية المنطقة (تحليل double jit_free سلبي حتى الآن)

SESSION 5C — العائق، مُوصَّف بدقة

النتائج التجريبية هذه الجلسة

  • drain4@500 (استطلاع 1ms، قتل فوري، +4.5K renames): لا يزال crash عند deref
  • تداخل الذيل مع القتل (drain5): crash أبكر (renames أثناء عاصفة استرداد القتل تصطدم بخطأ على مستوى النظام) — تم التخلي عن التداخل
  • تجربة العزل (وضع iso): توقيت مطابق، ذيل مع مناطق commit-0
    • oracle إعادة استخدام pool من stage-2 → ORACLE HIT → التوقيت/إمكانية الوصول جيدة؛ الأحداث هي المشكلة
  • أحداث mask-cycled (دوران MOVED_TO/CREATE/DELETE لهزيمة inotify_merge): لا يزال crash عند deref
  • CONFIG_MEMCG=n → الأحداث والمناطق تتشارك kmalloc-96 واحد (نظرية memcg ميتة)؛ inotify_merge يقارن الأسماء أيضًا (نظرية merge ميتة — أسماء التبديل لدينا لم تندمج أبدًا؛ الأحداث كانت في قائمة الانتظار ومحتفظ بها طوال الوقت)
  • /proc/slabinfo غائب؛ /proc/self/pagemap قابل للقراءة لكن PFN-zeroed (إخفاء ما بعد 4.0، لا CAP_SYS_ADMIN)

العائق الفعلي (جزآن، كلاهما مُثبت)

  1. إنهاء soft-job يعمل في سياق kbase job-scheduler WORKER (jd_run_atom ← إرسال js، mali_kbase_jd.c:81-112/677)، وليس مضمّنًا في ioctl الإرسال → current->mm هو لخيط نواة → التصميم الأنيق "وجّه مؤشرات الوهمي إلى mmap الخاص بـ userland" (لا PAN!) يُخطئ بشكل غير حتمي. 5/5 انهيارات مع وهمي مثالي بخلاف ذلك.
  2. لذلك يجب أن يشير gpu_alloc إلى ذاكرة النواة مع سلسلة وقت تشغيل قابلة للنجاة: K=*(S+0x38) قابلة للقراءة، *(K+0x1429c)==0 في وقت التشغيل (يتخطى mm-atomics)، K+0x141c8 قابلة للكتابة، D=*(K+4) → D+0x538 قابلة للكتابة، nents *(S+8) يفضل أن تكون 0. المرشحون التسعة S دون اتصال تم التحقق منهم مقابل بايتات الملف — الانحراف في وقت التشغيل (تهيئة xfrm/tracepoint) يجعلهم غير مُتحققين. سلسلة خاطئة = crash = إعادة تشغيل (~3 دقائق دورة).

خطط الجلسة 6 (كلاهما محدد بالكامل)

A. physmap-spray fake (ret2dir، arm32 الكلاسيكي بدون PAN): رش ~450MB من صفحات المستخدم كل منها يحتوي على النمط الوهمي المخبوز لعنوان واحد مُخمَّن G (G&0xfff = 0x141 لبايتات اسم خالية من NUL؛ S=G؛ K=G-0x141b4 بحيث K+4 يهبط في الصفحة؛ K+0x141c8/+0x1429c → G+0x10/+0xd4 في الصفحة؛ D=G+0x300؛ مخازن sub-0 الشاردة تصطدم بـ RAM مُعيّن عشوائيًا - غير ضارة مع nents=0). spray يضاعف كضغط الإخلاء (anon قذر = غير قابل للإخلاء → فقط ~100-200MB إضافية مطلوبة). الاحتمالات ≈ 45% (إصابة الصفحة) × ~50% (كلمة K+0x1429c الغريبة صفرية... إذا بقي K+0x1429c في الصفحة وفقًا للتخطيط أعلاه، الاحتمالات = إصابة الصفحة فقط). خطأ = crash = إعادة تشغيل، أعد المحاولة. B. القوة الغاشمة على المرشحين التسعة S الثابتين (xfrm 0xc118b7ec أولًا، الملاصق لـ tracepoint 0xc111cba4 ثانيًا...): إعادة تشغيل واحدة لكل مرشح، حمولة oracle الحميدة أولًا، السلاح عند الإصابة. C. G دقيق قائم على pagemap (ميت: PFNs مُقنَّعة) — لا تعد إليه.

SESSION 5D — physmap fake مبني؛ G-sweep 0/3؛ تم القضاء على المتغيرات المُربكة

تم إثباته هذه الجلسة (كل شيء مُتحقق ثنائيًا/على الجهاز)

  • هوية الـ Cache مؤكدة نفسها: region = kmem_cache_alloc_trace( kmalloc_caches[7], GFP|0x8000, 0x48) [تفكيك kbase_alloc_free_region]؛ event = __kmalloc(89, GFP) → kmalloc-96. الأحداث والمناطق يمكن أن تتشارك cache الضحية. (MEMCG مغلق؛ مجموعة cache واحدة.)
  • قائمة انتظار inotify: ≥5000 حدث محتفظ به، لا تجاوز، لا انهيار merge (تشغيلات qmeas 1000 و 5000) — spray الحدث يستمر
  • iso2 (physmap spray + ذيل المنطقة + oracle): HIT — physmap spray لا يكسر استرجاع المنطقة؛ الآلية سليمة
  • الأحداث مقابل استرجاع المناطق: المناطق 3/3 (iso، iso2، stage-2)، الأحداث 0/10 لكن تشغيلات pmap الثلاثة مفسرة بأخطاء G باحتمالات 0.3-0.45 لكل منها (P(3 أخطاء|الأحداث تعمل) ≈ 0.2-0.3 — غير قاطع)
  • وضع mix مُربك: spray 480MB يخنق renames بعد القتل (+0)؛ 350MB جيد (+4452)؛ خيوط rename الفاشلة الدوارة أيضًا تُشوش استرجاع المنطقة (mix انهار، iso2 نظيف)
  • query_commit يُرجع -1 عند EINVAL أيضًا — مُجهز؛ EINVAL الملاحظ = خطأ بحث rbtree = محرر حقًا ✓ (ليس إيجابية كاذبة)

تصميم pmap الحالي (في stage3.c، وضع pmap/mix/iso2)

  • G مخبوز في كل صفحة مرشوشة (إزاحة 0x2a4): nents@+0x2ac=0، evict self@+0x2bc/0x2c0، K=G+0x100@+0x2dc، D=G+0x200@+0x3a8؛ K+0x141c8/-0x1429c تهبط ~20 صفحة أعلى (مخزن sub-0 غير ضار / القراءة يجب أن تكون 0-أو-صحيحة). PM_SPRAY_MB 350، G sweep جُرّب: c2a412a4، c2f4b2a4، c2a7d2a4 — كلها crash عند deref
  • سلامة نطاق physmap: RAM 1GB → physmap ~0xc0000000-0xc3fffffff؛ carveouts MTK (GPU/M4U/secure) قد تشغل أجزاء — G ألغام أرضية

TODO الجلسة 6 (مرتبة)

  1. استخرج مصدر النواة الكامل (tarball 2.2GB في ~/Desktop/amazon-mustang/ — platform.tar): احصل على arch/arm + mm/ + drivers/of + تعيينات MTK reserve → احسب خريطة carveout → استهدف G في نطاقات physmap RAM مُتحقق منها؛ تحقق أيضًا من هندسة cache kmalloc (ARCH_KMALLOC_MINALIGN!) وبت GFP 0x8000
  2. G sweep مع تخمينات مستنيرة بالموقع (إعادات تشغيل متعددة، غيّر حجم spray لإزالة الارتباط)
  3. إذا استُنفد G sweep: أعد النظر في حمولة متعددة-G أو مرشحي S من ثوابت معقولة في وقت التشغيل (حقول مؤشر ملاصقة لـ uts فشلت: NULL-K)

SESSION 6 — اكتشاف zram، استخراج المصدر، سؤال الحدث لا يزال مفتوحًا### تم الآن استخراج مصدر النواة الكامل

/tmp/opencode/ksrc2/kernel/mediatek/mt8163/4.9/ (arch/arm بما في ذلك mustang.dtsi، mm/، fs/eventpoll.c، fs/notify) من ksrc/platform.tar. النتائج:

  • بت 0x8000 في GFP = ___GFP_ZERO (مجرد kzalloc؛ لا انقسام في الـ cache)
  • kmalloc-96 هو cache حقيقي بحجم 96 بايت (لا HWCACHE_ALIGN على caches الـ kmalloc)
  • mustang.dtsi: عقدة الذاكرة 0x40000000/512MB — موسّعة بواسطة preloader (يعرض الجهاز MemTotal 977MB)؛ CONFIG_VMSPLIT_3G، HIGHMEM=y
  • zram0 نشط (SwapCached > 0) → "dirty anon = unevictable" كان خاطئًا: رشّ physmap يُبدَّل خارج الذاكرة تحت الضغط → يصبح alias الـ G قديمًا → أُضيف physmap_retouch() بعد القتل (يعيد إدخال جميع صفحات الرشّ قبل trailing/deref)

التشغيلات في هذه الجلسة (جميعها O_SYNC مسجّلة، ~6 عمليات إعادة تشغيل)

الحكم على الأحداث (بايزي، صادق)

استرجاع المناطق: 3/3. الأحداث: 0/~12 محاولة بما في ذلك 28K تخصيص حصري مع أسبقية و fake صحيح بالبناء. إذا استرجعت الأحداث مع p_hit(G)≈0.35، فإن خمس إخفاقات pmap ≈ 11.6% — ممكن لكنه الآن غير مرجّح (~10-15%). إما أن الأحداث بنيويًا لا يمكنها أخذ هذه الخانة (السبب غير معروف — نفس الـ cache، نفس السياق، نفس التوقيت) أو أن تخميناتنا لـ G تفشل بشكل منهجي (انحراف حدود highmem، توزيع الـ allocator).

شجرة قرار الجلسة 7

  1. حسم G أولًا (رخيص، بلا exploit): أضف instrumentation مؤقتًا إلى تدفق ISO2 — region trailing + oracle — لكن اجعل حدث PAYLOAD fake من physmap وتحقق مما إذا كان أي G في نطاق ممسوح ينتج تغييرًا في nodename دون تسابق المناطق (pmap خالص، مسح G على ~0xc1500000-0xc2a00000 مركز lowmem، 1 G لكل إعادة تشغيل، 4-5 عمليات إعادة تشغيل)
  2. إذا استُنفد مسح G → تُعلن الأحداث ميتة → ابحث عن مخصّصات kmalloc-96 بديلة للبايتات الخام يمكن الوصول إليها من الـ shell (تدقيق: seq_file، tty ldisc، fdtable، sk_filter (محجوب: حقل الكود عند +0x38)، netlink nlmsg (skb ✗)، keys (مرفوض)) — أو أعد النظر في primitives المنطقة ذات المرحلتين (التحليل سلبي حتى الآن)
  3. فكّر في فتح UART/ramoops عبر root لاحقًا؛ لا تطارد ذلك

الجلسة 7 — قنابل cmdline؛ لغز الأحداث أصبح محدودًا بدقة الآن

CONFIG_CMDLINE في mustang_defconfig (الحقيقة الأرضية للتوزيع):

vmalloc=496M slub_max_order=0 slub_debug=O loglevel=8 initcall_debug

  1. vmalloc=496M → physmap = 0xc0008000..~0xc2080000 فقط (الـ 520MB الدنيا من RAM) — جميع تخمينات G السابقة (0xc2a4xxxx+) كانت في فضاء VMALLOC. كل استنتاجات "G-miss" من الجلستين 5D/6 تُبطَل؛ تفسير الـ crash يبقى قائمًا لكن المسح كان موجّهًا نحو الخريطة الخاطئة.
  2. slub_max_order=0: جميع صفحات slab من الرتبة 0
  3. KMALLOC_MIN_SIZE = ARCH_KMALLOC_MINALIGN = 64 (L1_CACHE_SHIFT 6) → الحالات الخاصة لـ kmalloc_index() من أجل 96/192 معطّلة → region kzalloc(0x48=72) → caches[7]؛ event __kmalloc(89) → caches[7] (تم التحقق من disasm + include/linux/slab.h:287) — كلاهما في الـ cache المدمج "kmalloc-128/96". هوية الـ cache: أُعيد تأكيدها كمتساوية.
  4. zram نشط → استُبدل رشّ anon physmap بـ kbm_spray: 160 × 2MB مناطق kbase MEM_ALLOC (GFP_KERNEL → ZONE_NORMAL → lowmem فقط، مثبّتة → محصّنة ضد zram)، النمط يُكتب عبر CPU mmap — النطاق الصحيح، تغطية lowmem ~65%

التشغيلات (O_SYNC مسجّلة)

  • kbm + G=0xc16412a4: crash at deref (+4831 renames)
  • kbm + G=0xc1c4b2a4 @200MB: NO eviction (query=16 — معايرة الضغط تتغير مع الرشّ المثبّت؛ free قانوني، نجا)
  • kbm + G=0xc1c4b2a4 @400MB: eviction ✓، +4053 renames، crash at deref
  • سجل G في النطاق الصالح: 0/2. إذا عملت الأحداث مع تغطية ~65%: P(2 misses) ≈ 12%. السؤال لا يزال مفتوحًا لكنه أضيق من أي وقت مضى.

السؤال، صيغته النهائية

المناطق تأخذ خانة الضحية 3/3؛ الأحداث 0/13. نفس الـ cache (مُثبت على مستوى المصدر + disasm)، نفس سياق العملية، نفس cpus المثبّتة، نفس توقيت ما بعد القتل، آلاف التخصيصات مع أسبقية حصرية. الآلية غير معروفة. المشتبه بهم المتبقّون: ارتباط معدل/تكرار التخصيص مع دوران القائمة الجزئية (region ioctls بفاصل ~1ms مقابل event renames بفاصل ~100µs — اتجاهان متعاكسان؟)، أو تفاصيل ترتيب freelist في SLUB تحت slub_max_order=0 التي تفضّل... غير واضح.

مهام الجلسة 8

  1. تجربة بمستوى instrumentation: متغيّرا event payload بـ قيم N/P مختلفة بالتناوب (مجموعتا أسماء) — إذا تغيّر nodename يومًا ما، يُحدَّد الفائز الأخير؛ امسح G في [0xc1200000..0xc2000000] مع kbm spray، ميزانية 3-4 عمليات إعادة تشغيل
  2. إذا بقي 0/N: تخلَّ عن الأحداث. البدائل مرتّبة: a. pipe_buf arrays عبر F_SETPIPE_SZ(4096 → 1 buf؟ لا — 16 bufs = kcalloc(16, 28)=448→512 ✗) — ميت b. دقّق في fs/notify + fs بحثًا عن كائنات kmalloc-128 أخرى تحمل أسماء/بيانات (أحداث fanotify؟ mq معطّل؛ fanotify يحتاج groups...) c. seq_file buffers (kmalloc(PAGE_SIZE) ✗) d. sock filters (تصادم حقل الكود عند +0x38 ✗) e. اقبل استرجاع نوع المنطقة + اربط خطأ/تقنية ثانية
  3. أعد فحص لماذا تفوز المناطق — ربما عبر instrumentation بـ jit ids متعددة: N خانة معلّقة، تسابق region-vs-event لكل خانة، oracle يكتشف أي رشّ أخذ أي خانة → بصمة إحصائية للآلية

الجلسة 8 — تحقّق الكتابة الاعتباطية على الجهاز؛ لغز dispatch متبقٍّ

الإنجاز```

[+] uname.nodename="X?lhost" (was "localhost") <<< ORACLE HIT

root@kitploit:~
**سلسلة البايت الخام الكاملة تعمل على الجهاز الحيّ**: الحدث يستعيد
فتحة المنطقة المحرَّرة → kbase_jit_free يُشير إلى المزيّف الخاص بنا (S=صفحة physmap من
رشّ kbm، G=0xc154b2a4) → يقوم unlink بتنفيذ الكتابتين لدينا.
تم التحقق منها مرارًا باستخدام الحمولة الحميدة (كتابة nodename).

### سلسلة الاكتشافات خلال الجلسة
1. كانت صفحات kbm من نوع GFP_HIGHUSER → HIGHMEM، غير مرئية لـ physmap
   (mali_kbase_mem_pool.c:164!) — تم إصلاحها عبر zonelist spill: رشّ 260 × 2MB
   > highmem-free → الفائض يهبط في ZONE_NORMAL (physmap)
2. محاولات السلاح الأولى انهارت: هدف W1 عند N+4 كان صفحة USER —
   يعمل unlink في سياق kworker (بدون mm) → خطأ. تم الإصلاح بدمج
   shellcode ذي الحلقة-0 داخل نمط physmap عند page+0x600 (خريطة مباشرة RWX
   على arm32 غير LPAE) — كود مقيم في النواة، دون حاجة إلى ret2usr
3. خطأ في إزاحة ctl_table: proc_handler يقع عند entry+0x14، وليس +0x18 (المسح
   الأصلي كان صحيحًا؛ تعريفـي كان خاطئًا) — كان يكتب extra1
4. تم اكتشاف تراجع الضغط المفرط وإرجاعه: ميزانية kid 20×100MB +
   ذيل 10s أفسد الاستعادة؛ الإعداد العامل هو 2 kids/200MB +
   5s/+3200 ذيل (إصابة حميدة 1/1 بعد الإرجاع)
5. **diag2: W2 → &pid_max العام (0xc1114d7c) → القراءة تُعيد قيمتنا
   (-1055861411 = 0xc110d55d كـ int32) — الكتابة + القراءة العكسية مُثبَتة**

### اللغز المتبقي (تجربة واحدة تفصلنا عن الإغلاق)
diag1 مع عنوان المعالج الصحيح (0xc1113f3c): W1 يُطلق (nodename
يتغير)، W2 لا بد أنه نُفِّذ (التعليمة التالية) — ومع ذلك لا تزال قراءات pid_max
تُعيد قيمًا نظيفة → حقل المعالج الذي نكتبه ليس الذي
يوزّع عبره الـ inode. diag3 (في الانتظار؛ يحتاج إقلاع إصابة): W2 →
حقل entry->data (0xc1113f2c) مشيرًا إلى nodename — إذا أظهرت القراءة حينها
بايتات nodename كعدد صحيح، فإن entry لدينا حيّ وأن إزاحة المعالج فقط
خاطئة بطريقة ما؛ وإذا لم تتأثر، فإن الـ inode يستخدم نسخة جدول ظلّية
ونبحث عن الحيّة.

### واقع معدل الإصابة
رمية عملة لكل إقلاع (~25-40%)، متجمّعة؛ عدة إقلاعات آمنة-تفويت/انهيار
متتالية أمر طبيعي. تقريبًا 1 من كل 3-4 إقلاعات تكون إصابة. أبقِ الإعداد
العامل بالضبط (2 kids، ذيل 5s، kbm بـ 260 منطقة، G=0xc154b2a4).

### مهام الجلسة-9
1. أكمل diag3 على إقلاع إصابة (أعد المحاولة حتى "W1 FIRED")
2. إذا كان entry حيًّا: أعد فحص إزاحة المعالج تجريبيًا (اكتب
   عنوان proc_dostring كمعالج عبر... يجب أن يساوي N قيمة
   مفيدة — استخدم unlink لكتابة entry->data بدلًا من ذلك وانتقل: مثلًا،
   data=selinux_enforcing-المجاور...)
3. إذا كان جدول ظلّي: حدّد الحي — kallsyms لا يحتوي على رموز بيانات؛
   المرشحون: امسح سلوك /proc/sys، أو اعثر على منطقة ctl_table ثانية
   عبر نمط قائمة الترويسة في .data (مدخلات بخطوة 0x20
   مع handler=proc_dointvec_minmax و maxlen=4 — عدّد الكل و
   اكتب تشخيصيًا في كل منها)
4. فئة هدف بديلة تتجنب التوزيع كليًا: مؤشرات دوال .data
   تُستدعى من مسارات يمكن الوصول إليها من shell (يلزم تدقيق)
5. الكتابة البدائية نفسها منتهية — أي هدف موثوق لعنوان نواة
   يكفي الآن للحصول على root

## الجلسة 9 — سلاح-nf: reclaim+unlink+إصابة-G مُثبَتة داخل السلاح؛ لم يتبقَّ سوى مسح الخطاف

### تصميم الزناد الجديد (يستبدل مسار معالج sysctl بالكامل)
فكرة المستخدم مترجمة إلى ذاكرة النواة: لا ملف SUID (النظام
dm-verity للقراءة فقط؛ البدائي يكتب في RAM النواة). بدلًا من ذلك: **خطاف netfilter
مزيّف**. هذه النواة لديها backport الشائع في Android لواجهة
nf_hook_entries الجديدة، لكنها منفَّذة كقائمة مترابطة (تم التحقق عبر
تفكيك nf_hook_slow + المساعد 0xc09897d4):
- `__ip_local_out(net, sk, skb)` يحمّل خلية entries من
  **[net+0x58c]**، يخزّنها في state+0x1c، يستدعي nf_hook_slow
- المسح: `entry = *cell`; while(entry){ if (state->[4] <= entry->[0x20])
  call entry->[0xc](entry->[0x14], skb, state); entry = entry->[0]; }
  — أي **fn@entry+0x0c، priv@entry+0x14، priority@entry+0x20،
  next@entry+0x00**؛ state+4 = عتبة INT_MIN (تمرّ دائمًا)
- init_net = **0xc1104548** (CONFIG_NET_NS=n → sock_net() تُضمّن
  الثابت؛ 6678 مرجع movw/movt، بطل المدرّج التكراري؛ مؤكَّد متقاطعًا بواسطة
  القيمة الحرفية لـ nf_hook_slow نفسها). الهدف: **[init_net+0x58c] = 0xc1104ad4**
- خطاف LOCAL_OUT يعمل في سياق عملية المُرسِل → hookfn لدينا
  commit_creds(prepare_kernel_cred(0)) يجذّر العملية التي أرسلت
  الحزمة. الزناد = sendto(127.0.0.1:9) UDP.

### تخطيط وضع nf (poc/stage3.c، الوضع `nf 200`)
- صفحات نمط kbm (نسبةً إلى الصفحة، مصدر حقيقة واحد — السلاح
  القديم كان به ثلاثة أخطاء أُصلحت الآن: proc_handler@+0x14 وليس +0x18؛ هدف W1
  يجب أن يكون ذاكرة نواة (سياق kworker، بدون mm)؛ الكود المدمج عند
  page+0x600 مقابل عدم تطابق entry G+0x600=page+0x8a4):
  - +0x2ac/+0x2bc/+0x2c0/+0x2dc/+0x3a8: سلسلة phy-alloc المزيّفة (دون تغيير)
  - +0x600: hookfn الخاص بـ nf_code (تخزين علامة + prepare_kernel_cred +
    commit_creds + إرجاع NF_ACCEPT(1))
  - +0x700: entry مزيّف {next=0, fn=PM_PAGE+0x600, priv=0, prio=0x100}
  - +0x740: cell → PM_PAGE+0x700
- الحمولة: N = PM_PAGE+0x740 (0xc154b740)، P = 0xc1104ad4
  → W1: *(cell+4)=P (تهبط في صفحتنا)، W2: *(init_net+0x58c)=cell

### أدوات تشخيص ذاتي (kbm_scan_for)
يتم الاحتفاظ بتخطيطات kbm لوحدة المعالجة المركزية؛ بعد التحرير نمسح كل صفحة
مرشوشة بحثًا عن كلمة معروفة:
- scan(HOOKS_PTR_ADDR) عند page+0x744 → يثبت reclaim + unlink ويكشف
  أي صفحة فيزيائية تسند تخمين G
- scan(0x600d600d) عند page+0x7f0 → يثبت أن hookfn نُفِّذ
  (nf_code يكتب هذه العلامة كإجرائه الثاني)

### التشغيل المهم (2026-09-11، أواخر الجلسة 9)```
[+] W1 CONFIRMED: region 135 page+0x185000 <- 0xc1104ad4 (reclaim + G hit!)
[+] nf trigger done, uid=2000 euid=2000

سلسلة السلاح الكاملة أُطلقت على إقلاع حيّ: استعاد الحدث الفتحة، وعمل المزيّف، ونُفّذ إلغاء الربط، وكانت صفحة تخمين G (0xc154b000) ملكنا فعلاً (المنطقة 135 الصفحة 389). W2 = *(0xc1104ad4)=cell هي التعليمة المجاورة — لا بد أنها نُفّذت. ومع ذلك لم يمنحنا إرسال UDP صلاحيات الجذر → الفشل داخل مسار الخطاف: دلالات المشي، توصيلات [state+0x1c]، مقارنة الأولوية، أو حقول الإدخال. (أُضيفت تجربة العلامة للتمييز بين تنفيذ hookfn وعدمه؛ لا يوجد سوى إقلاعات الانهيار قبل نهاية الجلسة — لا بيانات نظيفة بعد.)

دروس معدل الإصابة / حالة الإقلاع (مكتسبة بصعوبة)

  • الإعداد العامل (لا تلمسه): 2 kids × 100MB ضغط متدرج، ذيول 100×50ms/+3200 إعادة تسمية، رشّ kbm 260×2MB، G=0xc154b2a4، تصريف مسبق ~5000 إعادة تسمية
  • الضغط المفرط (20 kids / ذيل 10s) يكسر الاستعادة — تم التراجع عنه
  • استقرار الإقلاع مهم: التشغيلات المنطلقة فور boot_completed تتسابق مع تخصيصات إقلاع النظام → سلاسل باردة؛ انتظر 60-90 ثانية بعد الإقلاع قبل التشغيل
  • "W1 لم يُطلق" (فحص nodename) لا معنى له مع حمولة nf — W1 يكتب في صفحتنا؛ استخدم kbm_scan_for بدلاً من ذلك
  • إقلاعات الانهيار عند التحرير ≈ فتحة عشوائية أو تحويلات فقدان G؛ الضابط الحميد (pmap 200) هو فحص سلامة البيئة (4/4 إصابات عند السخونة؛ فقدان-انهيار عند البرودة)
  • مجلدات /data/local/tmp/.w* تُنظّف عند بدء التشغيل (المجلدات المتراكمة تُضعف الاستعادة)

مهام الجلسة-10 (شجرة قرار، بالترتيب)

  1. شغّل nf 200 على إقلاعات متأخرة الاستقرار حتى يظهر سطر W1-CONFIRMED، ثم اقرأ سطر MARKER: a. العلامة موجودة، uid!=0 → فشلت بيانات اعتماد shellcode (تحقق من عناوين prepare_kernel_cred/commit_creds؛ ترميزات blx) b. العلامة غائبة → المشي لم يستدعنا أبداً: تحقق بعلامة ثانية يكتبها... التشخيصات التالية: hookfn يكتب العلامة فقط ويعيد 1 (بدون بيانات اعتماد) — إن بقيت غائبة:
    • اقرأ بايتات إدخالنا الفعلية عبر تعيين CPU قبل الإطلاق مباشرة (إنها ملكنا لنقرأها!)
    • تحقق مما إذا كان [init_net+0x58c] يُستشار أصلاً: استخدم الكتابة لإفساد شيء قابل للملاحظة بدلاً من ذلك (مثلاً وجّهه إلى خلية حيث *cell = إدخال مع fn = دالة نواة مثل kfree → انهيار فوري عند الإطلاق = الحقل يُستشار فعلاً)
    • أعد التحقق من إزاحة 0x58c: ربما hooks_ipv4[NF_INET_LOCAL_OUT] يقع عند فهرس مختلف (NF_INET_POST_ROUTING=4؟)
  2. إذا استدعانا المشي: أصلح بيانات الاعتماد → الجذر → ثم خطة المستخدم: setenforce 0; cp /system/bin/sh /data/local/tmp/su; chown root; chmod 6755; تحقق من ls -la; اترك ملفات العلامة
  3. الاستمرارية (بعد الجذر): ترقيع صورة الإقلاع عبر /dev/block/by-name/boot
    • تعطيل dm-verity، أو بأسلوب Magisk؛ su على /data وحده هو uid0 في نطاق-shell بعد إعادة الإقلاع (SELinux مفعّل مجدداً) — setenforce 0 وقت التشغيل فقط
  4. ملاحظات التنظيف: عمليات st3 الموقوفة لديها حالة kctx تالفة — اقتلها بإعادة الإقلاع فقط؛ اختطاف nf يكسر خطافات LOCAL_OUT لكل حركة المرور — أعد الإقلاع بعد الجذر للاستعادة

إضافات أصول الليلة

  • أوضاع poc/stage3.c: pin/root/drain{,2,3,4,5}/iso — السلاح الكامل + oracle + حاضنة العزل، تحقيقات جنائية لنقطة انهيار O_SYNC
  • بنية تحتية للمزيّف في مساحة المستخدم (ufake_prep) — أبقِها لكنها قابلة للاستخدام فقط إذا وُجد مسار إلغاء مرجع مضمّن في السياق
  • tools/: kdis/scan_s/resolve/dumpb/findsysctl لتحليل vmlinux دون اتصال

الجلسة 10 — تحقق الجذر (2026-09-11)

الأخطاء الثلاثة التي كانت تعيق سلاح-nf، جميعها مُصلحة

  1. init_net خاطئ: 0xc1104548 هو __stack_chk_guard (مدرّج movw/movt تلوّث بأحمال stack-canary — 2025 بنت خطة nf بأكملها عليه). init_net الحقيقي = 0xc1185040 (مؤكد: ip_send_skb(net,...) يُستدعى بهذا الحرف؛ ~994 مرجعاً كلها في مكدس الشبكة). خلية IPv4 LOCAL_OUT = init_net+0x58c = 0xc11855cc.
  2. خطأ إلغاء المرجع المزدوج: nf_iterate يعامل [init_net+0x58c] كمؤشر nf_hook_ops نفسه — يقرأ fn@+0xc, priv@+0x14, prio@+0x20 مباشرة من تلك القيمة. إدخال الجلسة-9 المزيّف كان يقع عند PM_PAGE+0x700 مع الخلية تشير إليه (حقول next/ غير المستخدمة عند الخلية → → انهيار). الإدخال المزيّف يجب أن يقع (0xc154b740). مع إصلاح هذا، ثبت استدعاء الخطاف (وضع = SAFE_FN يعالج كل 200 إرسال بنظافة).

جدار SELinux وتجاوز الحزمتين

commit_creds(prepare_kernel_cred(0)) / override_creds(&init_cred) يمنح uid 0 لكنه يهبط في SID النواة kernel، الذي لا تسمح له سياسة Fire OS هذه بالكتابة في /data أو /sys/fs/selinux/enforce (مؤكد: EACCES). SID init الحقيقي (7) مرفوض أيضاً (اختبار struct cred المزيّف). enforcing_setup هو __init (محرَّر → انهيار). atomic_sub في mark_reclaim يحتاج nents=1 مما يكسر الخروج المبكر لـ shrink_cpu_mapping.

الانتصار: عملية الاستغلال تحتفظ بتعيينات kbm CPU، لذا يمكن إعادة كتابة إدخال nf المزيّف في مكانه بين الحزم:

  • وضع selroot: الإدخال = {fn=0xc01d503c (mov r3,#0;str r3,[r0];bx lr), priv=0xc1213ea8 (selinux_state.enforcing)}.
  • الحزمة 1: *(enforcing)=0 → SELinux متساهل.
  • أعد كتابة الإدخال عبر kbm_cpu[reg]+off إلى {fn=commit_creds, priv=&init_cred}.
  • الحزمة 2: commit_creds(&init_cred) في مهمة المرسل → uid 0 مع SELinux متساهل → جذر قابل للاستخدام، كل ذلك في استعادة واحدة، دون حاجة إلى سلسلة.

مؤكد على الجهاز (2026-09-11)```

[+] W1 CONFIRMED: region 67 page+0xad000 <- 0xc11855cc (reclaim + G hit!) [*] sendto 0 -> -1 errno=1 uid=0 euid=0 [+] nf trigger done, uid=0 euid=0 <<< HOOK RAN - commit_creds OK [+] ROOT: uid=0 euid=0 [+] setenforce write=1 [+] su copied bytes=236220

root@kitploit:~
- `getenforce` → **Permissive**؛ st3 المتوقف هو `Uid: 0 0 0 0`،
  `CapEff: 3fffffffff`.
- `/data` مُثبَّت **nosuid** لذا لا يمكن لـ `su` ذي setuid أن يعمل. أداة
  `rootshell` صغيرة (إرسال UDP → `commit_creds` على الذات → `execl sh`) تمنح
  صدفة root تفاعلية: `uid=0(root) context=u:r:kernel:s0`.
- يمكن لـ root القراءة/الكتابة على `/dev/block/by-name/*` (`dd if=boot ...` يعمل).

### حالة كود Stage-3 (`poc/stage3.c`)
- الأوضاع: `nf <mb> [probe|uprobe|oc|chain|fc <sid>|notrig|selroot]`
- `selroot` هو السلاح العامل. الثوابت الأساسية: `init_net=0xc1185040`،
  `HOOKS_PTR_ADDR=0xc11855cc`، `ENFORCING_ADDR=0xc1213ea8`،
  `ZERO_GADGET=0xc01d503c`، `commit_creds=0xc014993c`،
  `init_cred=0xc1114f54`.
- ما بعد الاستغلال هو استدعاءات نظام مباشرة (بدون `system()`)؛ أبقِ kctx حيًّا
  (`pause()`) لتجنب انهيار التفكيك.

### المتبقي (Stage 4/5)
- الاستمرارية عبر إعادة التشغيل (verity / صورة boot / recovery)، لأن اختطاف
  الخلية + SELinux المتساهل يعملان في وقت التشغيل فقط وإعادة تشغيل الاستغلال
  تتطلب رمي العملة العشوائي لإعادة الاستحواذ بنسبة ~1/3.
- سيحتاج `su` إلى home غير nosuid (`/system`) أو مشغّل يعيد التشغيل.

## SESSION 11 — PERSISTENCE RECON (Track B + Track A) and the RE handoff

الهدف كان root دائمًا. تم تحديد مسارين:
- **Track B**: تعطيل verified boot (dm-verity / SELinux) حتى يمكن ترقيع `/system`.
- **Track A**: إعادة تشغيل الاستغلال عند الإقلاع.

كلاهما يختزل إلى نفس العائق: **جعل LK يعامل الجهاز كـ `eng`/`unlocked`.**

### حقائق verified-boot (البناء الدقيق)
- Bootloader مقفل، AVB `green`، `ro.boot.unlocked_kernel=false`، `ro.boot.secure_cpu=1`،
  `rpmb_state=1`. Bootrom مُرقَّع (لا BROM)؛ preloader فقط عبر CMD short.
- `/system` مُثبَّت بواسطة **Android dm-verity من سطر أوامر kernel المبني بواسطة lk**:
  `root=/dev/dm-0 dm="system none ro,0 1 android-verity PARTUUID=b6404ef3-… "`,
  `veritykeyid=id:f3530e18f64d11fc25eb2dd762979f078de990bf`، `androidboot.veritymode=eio`،
  `skip_initramfs` (system-as-root). `dm-0` = جهاز verity المسمى `system`؛ `dm-1` = `/vendor`.
- LK: Amazon **UFBL**، `ro.boot.lk_version=0x0006`، بناء `0db73c9-20231025_030009`؛
  preloader `pl_version=0x000a`، بناء `80c6fcb-20230523_065640`. `/dev/block/by-name/lk` = mmcblk0p5 (1 MB).
- GPT الكامل (16 قسمًا، بدون `persist`/`seccfg`/`nvram`/`protect`/`para`):
  `proinfo` p0، `PMT` p1، `kb` p2، `dkb` p3، `lk` p4، `tee1` p5، `tee2` p6، `metadata` p7،
  `MISC` p8، `reserved` p9، `boot` p10، `recovery` p11، `system` p12، `vendor` p13،
  `cache` p14، `userdata` p15. eMMC boot0 (1 MB) = preloader (سحر `EMMC_BOOT`)؛
  boot1 (4 MB) = مخزن IDME.

### نتائج LK (UFBL) الساكنة
ترويسة `lk.img`: `88 16 88 58 | 00052974 | "LK"`؛ جدول متجهات ARM عند 0x200، الباقي Thumb-2،
مستقل عن الموضع/مُعاد توطينه (تجمعات الحرفيات تستخدم `ldr+add pc`، لذا يفشل التفكيك الساذج نسبةً إلى القاعدة).
السلاسل ذات الصلة (إزاحات الملف): `amzn_image_verify`، `amzn_verify_unlock`، `amzn_verify_code_internal`،
`unlock_code`، `unlock code error`، `unlock failed`، `$Common Kernel Signing Engineering CA0`،
`seccfg`، `para`، `ENV_v1`، `LK_ENV`، `Kfos_flags`/`Kdev_flags`/`Kusr_flags`/`Kunlock_code`/`Kunlock_version`،
`FOS_FLAGS_{NONE,ADB_ON,ADB_ROOT,CONSOLE_ON,RAMDUMP_ON,VERBOSITY_ON,ADB_AUTH_DISABLE,FORCE_DM_VERITY,DM_VERITY_OFF,BOOT_DEXOPT}`،
`[DM-VERITY] verify for system(root) is enabled`، `[DM-VERITY] verify off by fos_flags`،
`[DM-VERITY] disabled by fos_flags on eng devices or unlocked device`،
`[SELINUX] set to permissive mode by dev_flags`، `androidboot.prod=1|0`، `androidboot.unlocked_kernel=%s`.
**الخلاصة: LK يربط تأثيرات الأمان لـ `fos_flags`/`dev_flags` على eng/unlocked.**

### مخزن IDME (eMMC **boot1**) — قابل للكتابة، دائم، يُقرأ بواسطة LK وAndroid
- السحر `beefdeed` + `"2.1\0"` + count(0x19=25) عند 0x0؛ العناصر من 0x10.
- صيغة العنصر: `char name[16]; u32 size; u32 type(=1); u32 magic(=0x124); u8 data[size] (pad4)`.
- إزاحات العناصر (الأصلية): `board_id@0x10 serial@0x3c mac_addr@0x68 mac_sec@0x94 bt_mac_addr@0xd0
  bt_mfg@0xfc product_name@0x198 productid@0x1d4 productid2@0x210 region@0x24c bootmode@0x26c
  postmode@0x28c bootcount@0x2ac manufacturing@0x2d0 unlock_code@0x4ec sensorcal@0x908 alscal@0x9c4
  KB@0xa00 DKB@0x1e1c device_type_id@0x2238 dev_flags@0x2274 fos_flags@0x2298 usr_flags@0x22bc
  wifi_mfg@0x22e0 unlock_version@0x26fc`. القيم ASCII (الأعلام **سلاسل hex**).
- القراءة وقت التشغيل: `/proc/idme/<name>` (للقراءة فقط). قيم آخر إقلاع مخزنة مؤقتًا؛ الكتابة إلى boot1
  تسري في الإقلاع التالي. مسار الكتابة يتطلب مسح `/sys/block/mmcblk0boot1/force_ro` (root).
- **تأكد أن LK يقرأ boot1**: تغيير `serial` غيّر `ro.boot.serialno` في الإقلاع التالي.
  لكن LK **يقتطع serial إلى 16 بايت** وتجاهل `fos_flags=0x80`، `dev_flags=0xff`،
  all-ones، إلخ — verity/selinux/`prod` دون تغيير. لذا فشل حقن cmdline عبر serial.

### مستهلكو أعلام IDME من جهة Android
- `/init.fosflags.sh` (خدمة `fosflags`، `u:r:fosflags:s0`): `FOS_FLAGS_ADB_ON=0x1`،
  `CONSOLE_ON=0x4`، `RAMDUMP_ON=0x8`، `VERBOSITY_ON=0x10`، `ADB_AUTH_DISABLE=0x20`،
  `BOOT_DEXOPT=0x100`. تم التحقق: ضبط الأعلام يسري (`sys.usb=adb`، `noadbauth=1`).
- **adbd** (ARM ET_EXEC غير مجرد؛ `.text` VA 0x8160 / ملف 0x160؛ fileoff = VA-0x8000):
  - `amzn_is_root_allowed` @0x2d5b8 = `amzn_is_dev_unlocked() && (fos_flags & 0x2)`
  - `amzn_is_adb_auth_disable_allowed` @0x2d5e8 = `fos_flags & 0x20` (غير مُقيَّد)
  - `amzn_is_dev_unlocked` @0x2d5fc = `/proc/cmdline` يحتوي على `androidboot.prod=0` **أو**
    `androidboot.unlocked_kernel=true`
  - `fos_read_debug_flags` @0x2d724 يقرأ `/proc/idme/<name>` ويحلل **hex**
  - `restart_root_service` @0xcb74 / `restart_unroot_service` @0xcc64
  - السلاسل: `amzn_fos: ADB: Auto-root succeeded`، `… eng_device=%d`، `… unlocked_kernel=%d`،
    `adbd cannot run as root in production builds`، `ro.debuggable`
  - `adb root` → "cannot run as root in production builds" (`ro.debuggable=0`) — لذا حتى مع
    تحقق بوابة auto-root، يقيّد فحص AOSP prod مسار الأمر.

### لماذا لا يستمر root من Session-10
- SELinux المتساهل + اختطاف الخلية + root تعمل في وقت التشغيل فقط.
- `/data/metrics` هو **vpartition**: `/system/bin/vpartition.sh` يثبّت `/data/vp/metrics.img`
  (ext4، غير nosuid/noexec) عند `/data/metrics` في كل إقلاع؛ `su` المكتوب هناك **لا**
  ينجو من إعادة التشغيل. (أيضًا لماذا أعطى setuid `su` uid 0 لكن **بدون caps**.)

### Track A (إعادة الاستغلال عند الإقلاع) — محظور
- لا يوجد مشغّل `.rc` في init ينفذ كودًا قابلًا للتحكم (جميع الاستيرادات تم التحقق منها؛ مشغّلات `persist.*` فقط
  `start` لخدمات ثابتة؛ سكربتات في `/system`/`/vendor`).
- خدمات root تقرأ إعدادات `/data` لكنها لا تنفذ منها أبدًا (`perfmonitord`، `amazonfiled`،
  `vpartition.sh`، `kisd`، …).
- المنفذ الوحيد للإقلاع = **تطبيق**، لكن بصمة الاستغلال المتوقفة هي **VmRSS 534 MB**
  (رشّ `kbm`) → lmkd يقتله؛ بالإضافة إلى panic عند فقدان إعادة الاستحواذ (`PANIC_ON_OOPS`) → bootloop.
- **adbd auto-root** موجود لكنه مقيّد بسطر أوامر kernel المبني بواسطة LK (`prod=0`/`unlocked_kernel=true`).

### الخلاصة / الهدف التالي (المختار: Track B RE)
كل شيء يتوقف على جعل LK يبلّغ `eng`/`unlocked`. في المتناول:
`androidboot.prod=1|0` و`androidboot.unlocked_kernel=false` يتم ضبطهما بواسطة LK. اعكس LK لتجد:
1. أين يقرأ `fos_flags`/`dev_flags`/`usr_flags` (عناصر `K*`) والبوابة الدقيقة؛
2. تحديد `prod`/`unlocked` (عنصر IDME؟ buildvariant؟ نتيجة `amzn_verify_unlock`؟)؛
3. `amzn_verify_unlock` (تحقق RSA من libtomcrypt) لتجاوز أو مسار unlock_code/version ضعيف؛
4. تخزين `seccfg`/`para`/`ENV_v1`(LK_ENV) (ليس في أي قسم مُفرَّغ — ربما محمي بـ tee)؛
5. preloader (`boot0`، `EMMC_BOOT`) بحثًا عن ثغرة.
إذا سمح أي من هذه بضبط eng/unlocked (بشكل دائم، عبر boot1 أو كتابة قسم خام)، فإن
`FOS_FLAGS_DM_VERITY_OFF` يعطّل verity لـ system(root) ويمكن ترقيع `/system` بشكل دائم.

### المخرجات (من هذه الجلسة)
`/tmp/opencode/mustang-dumps/` (قد تُمسح عند إعادة تشغيل المضيف): `lk.img`، `boot1.img` (أصلي)،
`boot.img`، `MISC.img`، `metadata*.img`، `pmt.img`، `mbr.img`، `kb.img`، `dkb.img`، `reserved.img`،
`cache.img`، `boot0.img`، `boot1.img`، `adbd.bin`، `perfmonitord.bin`، `amazonfiled.bin`.
المساعدات: `tools/findinitnet.py`، `findgadget*.py`، `findstores.py`، `adbd_sym.py` (في /tmp)؛
المستودع يحتوي على `run.sh`، `poc/stage3.c` (`selroot`)، `poc/su.c`، `rootcmd.sh`.

### أوامر مفيدة```
# IDME read
/data/metrics/su sh -p -c 'for f in fos_flags dev_flags usr_flags serial region device_type_id unlock_version; do echo -n "$f="; cat /proc/idme/$f; echo; done'
# write boot1 (root; su lives only until reboot -> re-run run.sh first)
/data/metrics/su sh -p -c 'echo 0 > /sys/block/mmcblk0boot1/force_ro; dd if=/data/local/tmp/boot1.img of=/dev/block/mmcblk0boot1 bs=4096 count=4; sync; echo 1 > /sys/block/mmcblk0boot1/force_ro'
# dump a partition to host
adb exec-out '/data/metrics/su dd if=/dev/block/by-name/lk bs=4096 2>/dev/null' > lk.img

الجلسة 12 — LK RE: بوابة eng/unlocked حقيقية، ومخازن الأعلام غير الموقّعة غير موجودة

الهدف: جعل LK يتعامل مع الجهاز كـ eng/unlocked، أو إيجاد ثغرة في preloader/LK، حتى يمكن تعطيل verity/SELinux بشكل دائم. النتيجة: تم عكس مسار كود LK ذي الصلة من البداية إلى النهاية؛ القلب غير قابل للوصول عبر المخازن المتاحة. لم يتم إتلاف أي جهاز؛ تمت إعادة تجربة boot1 الوحيدة إلى حالتها الأصلية.

LK هو Thumb-2 PIC، يُنقل إلى القاعدة 0xFF400000

يبدأ lk.img بجزء ARM صغير (الملف 0x200). الناسخ المُعيد للتمركز عند 0x224 ينسخ من 0x200 إلى وجهة حرفية ويتفرع إلى نقطة دخول حرفية:

إذن عنوان وقت التشغيل = 0xFF400000 + إزاحة الملف للإزاحات >= 0x200. كل ما بعد الجزء الصغير هو Thumb-2، مستقل عن الموضع. تُبنى السلاسل النصية بـ ldr rT,[pc,#imm] (إزاحة T1 = imm8*4؛ إزاحة ldr.w = imm12) متبوعة بـ add rT, pc؛ الهدف هو (add+4) + *pool. تمت إضافة ماسح قوي يصمد أمام جزء ARM ومجمعات الحرفيات كـ tools/lk_xref.py (يتعامل مع الصيغ 16- و32-بت، ويفحص كل بايتين). جميع الإزاحات أدناه هي إزاحات الملف؛ أضف 0xFF400000 لعناوين وقت التشغيل.

تدفق التحكم المُفكَّك (الإزاحة -> المعنى)

رمز الفتح موقّع بـ Amazon-RSA — غير قابل للتزوير

يقرأ 0x20b4 عنصر unlock_code في IDME (0x400 بايت، كلها أصفار على هذه الوحدة) ويشغّل amzn_verify_unlock (0x222c -> 0x20f0). تلك الدالة تقود libtomcrypt (عشرات المسارات /features/libtomcrypt/src/pk/asn1/der/... و التحقق من RSA)، والصورة تتضمن مادة الشهادة: Sunnyvale / Amazon Lab126 / "$Common Kernel Signing Engineering CA0" عند 0x317d9+، بالإضافة إلى رسائل التشخيص Image FAILED AUTHENTICATION on PRODUCTION device (0x3166e), Authentication failed on engineering device with production certificate (0x316a0), Image FAILED AUTHENTICATION on ENGINEERING device (0x31703), Image AUTHENTICATED with PRODUCTION certificate (0x31736). لا يوجد اختصار للرمز الفارغ / الطول / الإصدار: verify(zeros) != 0، وبالتالي (مؤكَّد في ). قلب أو يتطلب إما صالحًا موقّعًا من Amazon (المفتاح الخاص غير متاح) أو ثغرة تنفيذ كود في المُتحقِّق. لم يُعثر على أي شيء قابل للاستغلال (حدود/حجم) بشكل ساكن في 0x20b4/0x222c/0x20f0. =>

أعلام verity/SELinux لا تأتي من مخزن موجود

تُقرأ أعلام الأمان عبر الجالب عند 0x57c. اختبار تجريبي:```

boot1 IDME item fos_flags data (offset 0x22B4, 8 bytes) set to "00000080"

dd if=/dev/block/mmcblk0boot1 ... ; reboot /proc/idme/fos_flags -> 00000080 (persisted, Android sees it) ro.boot.veritymode -> eio (unchanged!) root=/dev/dm-0 dm="system none ro,0 1 android-verity PARTUUID=..." (unchanged) androidboot.prod=1 / secure_cpu=1 / buildvariant=user (unchanged)

root@kitploit:~
`fos_flags=0x80` هو `FOS_FLAGS_DM_VERITY_OFF`؛ كانت البوابة المُفكَّكة ستُعطِّل
verity **إذا** كان الـ getter قد أعادها. لم يفعل. البوابة فعّالة، وليست
كودًا ميتًا: قيمة الإرسال المخزَّنة مؤقتًا لمرة واحدة هي `-1` في الصورة
(`*(u32*)0x50c74 == 0xffffffff`)، لذا نفّذت الدالة فعلاً مسار
`check_flag("fos_flags",0x80)` وحصلت على 0. لذلك فإن الـ getter (على الأقل
عند وقت حماية verity) **لا** يقرأ عناصر boot1 IDME.

المخزن المرشح الآخر هو **LK env**، المحمَّل من قسم يُسمَّى حرفيًا
`"para"` (loader 0x12fd4، magic `ENV_v1`، checksum @0x3ffc). يسرد جدول
الأقسام الخاص بـ LK (0x4fcc0..0x50340) preloader/proinfo/nvram/protect1/
protect2/persist/seccfg/secro/**para**/logo/custom/expdb/tee1/tee2/metadata/
system/cache/userdata — لكن GPT الفعلي للجهاز اللوحي يحتوي على **16 مدخلاً فقط**، جميعها
من النوع `af3dc60f838472478e793d69d8477de4`:```
#0 proinfo 0x400   #1 PMT 0x1c00    #2 kb 0x4000     #3 dkb 0x4800
#4 lk 0x5000       #5 tee1 0x5800   #6 tee2 0x8000   #7 metadata 0xa800
#8 MISC 0x1e400   #9 reserved 0x1e800  #10 boot 0x22800  #11 recovery 0x2a800
#12 system 0x34800 #13 vendor 0x644000 #14 cache 0x6b4800 #15 userdata 0x7ae800

لا توجد أقسام para أو seccfg أو nvram أو protect أو persist على هذا المنتج (وجميع تفريغات PMT/pmt.img أصفار). لذا فإن بيئة LK فارغة، ومفاتيح Kfos_flags/Kdev_flags غير موجودة أبدًا، وجميع فحوصات fos_flags/dev_flags تُحل إلى 0 — بغض النظر عما تحتويه عناصر IDME. عناصر boot1 IDME يستهلكها Android (/init.fosflags.sh، adbd، /proc/idme/*) ولكن ليس بوابات الأمان في LK.

الخلاصة — لماذا eng/unlock الدائم محظور

  1. يتطلب unlocked_kernel وجود unlock_code موقّع من Amazon (RSA/libtomcrypt، CA مضمّن). غير قابل للتزوير دون اتصال؛ لم يُعثر على خطأ في المدقّق. حظر صارم.
  2. تُستهلك رايتا DM_VERITY_OFF / selinux=permissive من بيئة LK (para/ENV_v1)، وهي غير موجودة في جدول GPT هذا. يتم تجاهل IDME fos_flags تجريبيًا من قِبل LK (استمر 0x80، وبقي verity على eio). حظر صارم ما لم يُعدَّل جدول الأقسام.
  3. حتى نجاح fos_flags=0x80 لن يضبط سوى androidboot.veritymode= disabled وroot= غير dm-0؛ لن يفتح القفل، وسيظل SELinux بحاجة إلى dev_flags من نفس البيئة الغائبة ليصبح permissive.

المسارات المتبقية (مستقبلية، أعلى خطورة؛ لم تُجرَّب)

  • تصنيع مخزن para/ENV_v1: أضف مدخل GPT باسم para (يجب تحديث GPT الأساسي + الاحتياطي معًا) في المساحة الحرة بعد userdata (ينتهي userdata عند LBA 0x3a3dfde؛ القرص = 30535680 قطاعًا)، ثم اصنع بيئة تحتوي على fos_flags=0x80 وdev_flags=0x40 (المجموع الاختباري عند +0x3ffc = مجموع البايتات على 0x3ffc). هذا هو المسار الوحيد المتبقي لإيقاف verity. المخاطر: إتلاف GPT الأساسي/الاحتياطي قد يُعطّل الجهاز؛ ولم يُثبَت أن بوابة verity تقرأ فعليًا para (فقط أنها ليست boot1 IDME).
  • خطأ Preloader (boot0/EMMC_BOOT): لم يُعكس هندسيًا في هذه الجلسة. كتابة boot0 محظورة حتى يتوفر نسخة أصلية ومسار استرداد.
  • بحث المدقّق: مسار شهادة الهندسة (0x316a0/0x31703) لا يمكن الوصول إليه إلا بهوية جهاز مقبولة كـ "engineering" بالإضافة إلى كود موقّع بمفتاح الهندسة؛ لا يتوفر مفتاح خاص.

المخرجات / قابلية إعادة الإنتاج

  • أداة مضافة: tools/lk_xref.py — محلّل مراجع سلاسل LK مستقل عن القاعدة.
  • التفريغات المستخدمة: /tmp/opencode/mustang-dumps/lk.img، boot1.img (أصلي)، boot0.img، mbr.img (GPT)، pmt.img (أصفار).
  • صورة تجربة boot1 (fos_flags=0x80) محفوظة في /tmp/opencode/s12/boot1_f80.img؛ أُعيد الجهاز إلى boot1 الأصلي (تم التحقق من /proc/idme/fos_flags -> 0).

أوامر مفيدة (تتطلب root؛ أعد التهيئة بـ ./run.sh)```

re-arm runtime root (~1/3 per boot)

./run.sh --no-build

confirm LK's decisions without a UART

/data/metrics/su /system/bin/sh -p -c 'cat /proc/cmdline'

watch: root=/dev/dm-0 dm="system ... android-verity ..." (verity on)

androidboot.veritymode=eio ; androidboot.selinux=enforce ; prod=1

IDME read (Android copy; NOT what LK's gates use)

for f in fos_flags dev_flags usr_flags unlock_version serial; do cat /proc/idme/$f; echo; done

root@kitploit:~
## الجلسة 13 — الـ preloader قابل للوصول في النهاية (`1949:20ff` = MTK preloader، ناقل HID)

أثناء إيقاف التشغيل وتوصيله بمنفذ USB، يُعدّد الجهاز اللوحي نفسه كـ **`1949:20ff`**
(`Lab126`) — *ليس* Android و*ليس* `0e8d:0003` bootrom.  الوصف:```
bInterfaceClass 3 (HID), iConfiguration "HID", iInterface "HID Interface"
HID report descriptor = 05 01 09 00 a1 01 c0   (empty collection!)
EP 0x81 IN  interrupt  4 bytes, bInterval 4
EP 0x01 OUT interrupt  4 bytes, bInterval 4
iSerial = GCC0X90805310009 (the IDME serial)

التعريف. 0x20FF مُدرج باسم "MTK Preloader" في ملف mtkclient config/usb_ids.py (تحت MediaTek VID 0x0e8d: 0xe8d:{0x0003 Brom, 0x2000/0x2001/0x20ff/0x3000 Preloader}). أبقَت Amazon على معرّف preloader PID وغيّرت VID إلى 0x1949، وتُقدّمه كزوج من نقاط نهاية HID مع واصف تقرير وهمي. لذا فهذا هو وضع MediaTek preloader / USBDL، وهي مرحلة أدنى من LK — يُوصَل إليها هنا عبر إيقاف التشغيل + التوصيل، وليس عبر قصر CMD.

سلاسل الواصفات "HID"/"HID Interface" غير موجودة في lk.img، أو boot0.img، أو boot.img أو غيرها من الملفات المستخرجة، أي أن الوضع ينتجه مكوّن لم نستخرجه بعد (bootrom/TEE) أو يُجمَّع في وقت التشغيل.

لماذا يهم هذا. إن preloader الخاص بـ Amazon المستخدم بواسطة aftv2-tools يكشف عن أوامر مدمجة، بدون Download-Agent، عبر هذا التدفق البايتي تحديدًا:``` handshake : host A0 0A 50 05 -> dev 5F F5 AF FA 0xD1 read32 (addr, n_words) : echo cmd/addr/n, 00 00, nu32, 00 00 0xD4 write32(addr, words[]) : echo cmd/addr/n, 00 00, nu32, 00 00

root@kitploit:~
`aftv2-tools/read_mmc.py` يستخدم `read32`/`write32` للوصول إلى متحكم MSDC
(القاعدة `0x11230000` على MT8173؛ تحقق من ذلك بالنسبة لـ MT8163) وقراءة/كتابة **كتل eMMC
الخام** بدون DA وبالتالي بدون AVB/verity في الطريق. إذا كان preloader الخاص بـ mustang
يقبل 0xD1/0xD4، فهذا مسار مباشر لفتح دائم (patch `boot` /
`lk`)، مستقل عن رمز فتح RSA وبيئة LK الغائبة.

### الأدوات المضافة (تحتاج root؛ قم بعمل chmod لعقدة USB أولاً)```
lsusb -d 1949:20ff                 # note Bus/Dev, e.g. Bus 001 Device 003
sudo chmod 666 /dev/bus/usb/001/003
# 1) does it answer the MTK handshake? (no DA, no flash access)
nix-shell -p python3Packages.pyusb --run \
    'python3 tools/mtk_preloader_hid.py handshake'
# 2) read-only arbitrary memory read
nix-shell -p python3Packages.pyusb --run \
    'python3 tools/mtk_preloader_hid.py read32 0x00100000 4'

tools/probe_preloader.py هو مسبار المصافحة فقط (handshake-only) الأدنى؛ tools/mtk_preloader_hid.py هو النقل الكامل (handshake/read32/ write32؛ write32 محمي ولا ينبغي استخدامه حتى يتم تأكيد خريطة سجلات eMMC).

الحالة / الخطوات التالية

  • غير مؤكد: ما إذا كان preloader الخاص بـ mustang ينفّذ فعلاً 0xD1/0xD4 (مسبار المصافحة هو ما يحدد ذلك). إذا كان كذلك، فمن المحتمل أن يكون مسار القراءة/الكتابة على eMMC الخاص بـ aftv2 قابلاً للنقل مباشرةً.
  • بعد ذلك: العثور على قاعدة MT8163 MSDC (kernel DT أو preloader)، وتفريغ قسم (read_mmc)، ثم ترقيع boot.img/lk من الـ preloader وإعادة التشغيل.
  • هذه مرحلة إقلاع أدنى من كل ما في SESSION 12، لذا فهي لا تعتمد على amzn_verify_unlock أو بوابة بيئة LK.
  • لا تقم بتشغيل كتابات SP Flash Tool / mtkclient عليه قبل تأكيد البروتوكول وتخطيط eMMC.
تنزيل الأداة
pi_blocked_on
  • proxy waiter يعيش على stack خيط waiter نفسه (futex.c:1975 يمرر this->rt_waiter، المُعرّف في futex_wait_requeue_pi عند futex.c:2880) → waiter يطبع إطاره المحرر عبر arm32 select (nr 142) fd_sets
  • مناخ الاستغلال: لا KASLR (قاعدة ثابتة 0xc0008000)، لا PAN، DEBUG_RT_MUTEXES معطّل → rt_mutex_waiter مضغوط بحجم 48 بايت (tree_entry@0، pi_tree_entry@0xc، task@0x18، lock@0x1c، prio@0x20، deadline@0x28)
  • سلسلة مختصرة: 2 write-slots → modprobe_path @ 0xc111488c (السلسلة موجودة ذاتيًا في vmlinux؛ KALLSYMS_ALL معطّل لذا رموز البيانات تحتاج هذه الحيلة) → exec binfmt غير معروف → root script (setenforce 0، تعطيل OTA، su)
  • المراجع في refs/: NebuSec/CyberMeowfia (الأصلي)، GhostLock-5.10 (منفذ Fire OS 8، trigger كامل لـ 32-bit ARM في src/exp32/)، ghostlock-...-4.19-k40 (منفذ Qualcomm 4.19 Android)
  • selroot
    selinux_state.enforcing
    commit_creds(&init_cred)
    uid=0
  • [_] المرحلة 4: root script (su، permissive، OTA off) + الاستمرارية — تم الحصول على root؛ الاستمرارية محظورة (انظر الجلسة 11): LK يقيّد verity-off/SELinux-permissive على eng/unlocked، إعادة الاستغلال عند الإقلاع ليس لديها منفذ قابل للتطبيق. التالي: عكس LK/amzn_verify_unlock.
  • المرحلة 5: سلسلة إقلاع نظام تشغيل مخصص
  • jc
    nr_extres
  • نتيجة JIT alloc يكتبها kernel عبر info->gpu_alloc_addr (GPU VA يجب أن تخصصه مسبقًا وتمرره)
  • كائن قابل للإخلاءالضغطالنتيجة
    لا شيء900 MBنجا
    لا شيء1300 MBpanic (خلل lowmem في النظام — غير ذي صلة)
    منطقة عادية + DONT_NEED700 MBنجا
    منطقة JIT + DONT_NEED700 MBpanic في مسار reclaim
    kernel/config-*
    /proc/config.gz
  • ksrc/ — مصدر Amazon OSS (platform.tar + شجرة midgard-r26p0 المستخرجة)
  • OTA: /tmp/opencode/mustang_ota.bin (sha256 6068515a… يطابق fireos-archive) و tarball مصدر kernel بحجم 2.2 GB محفوظ في ~/Desktop/amazon-mustang/
  • مسار شجرة المصدر: ksrc/kernel/mediatek/mt8163/4.9/drivers/misc/mediatek/gpu/gpu_mali/mali_midgard/midgard-r26p0/
  • renames تتوقف (journal/GFP_NOFS) → 128 إجمالي → deref عشوائي
    pre-drain 12K أحداث + ضغط + ذيل صغيرcrash عند deref
    + kill-child-at-eviction (استطلاع 10ms)crash عند deref
    + دورة حياة مثبتة على cpu0 (drain3)crash عند deref
    ضغط متدرج (drain4 v1)الأطفال حرروا الذاكرة عند الخروج → لا إخلاء (مسار قانوني مُتحقق)
    runresult
    pmap G=c2a412a4 400MBcrash at deref
    pmap G=c2f4b2a4 480MBcrash; +0 post-kill renames (480MB suffocates fs)
    iso2 (spray + regions + oracle)REGION HIT — spray doesn't break reclaim
    mix v1 (concurrent events+regions)crash; confounded (spinning event threads)
    pmap G=c2a7d2a4 350MB + retouchcrash; +4452 renames OK
    mix2 (sequential: 2s events THEN regions)crash; +7126 renames (28K event allocs), 2715 regions
    fn
    fn=0
    عند عنوان الخلية
    PM_PAGE+NF_CELL_OFF
    probe
  • الخريطة المباشرة physmap هي XN فوق kernel_x_end: arch/arm/mm/mmu.c map_lowmem() يعيّن lowram تحت نص النواة MT_MEMORY_RWX، لكن كل شيء فوق kernel_x_end MT_MEMORY_RW → PMD_SECT_XN (السطر 509). shellcode المضمّن عند 0xc154b600 يسبب prefetch-abort. الحمولة يجب أن تكون مؤشر دالة نواة حقيقي، لا كوداً في physmap.
  • الحرفي (إزاحة الملف)القيمةالمعنى
    0x2700xFF4002F8str r4,[r6] مساحة مؤقتة
    0x2740xFF40027Cالوجهة (base+0x27C)
    0x2780xFF54A440نهاية النسخ (بما في ذلك BSS)
    0x27C0xFF400484نقطة الدخول
    الإزاحةالدالة
    0xdf7cis_secure_or_prod() -> byte[ [[g]+0 ] + 0x163 ]; g = global @0x52838. 1 على هذه الوحدة.
    0x20b4verify_stored_unlock() = memset(buf,0,0x100); read IDME/env unlock_code (0x100) via 0x57c; bl 0x222c; return (verify==0).
    0x222c / 0x20f0amzn_verify_unlock(code,len) — libtomcrypt RSA/PKCS#1 verify (انظر أدناه).
    0xda3eis_unlocked() = is_secure_or_prod() && verify_stored_unlock().
    0x29a28is_verity_disabled() = (fos_flags & 0x80) && !(is_secure_or_prod() && verify_stored_unlock()); مخزّن مؤقتًا في global @0x50c74.
    0x29974باني سطر أوامر SELinux: dev_flags & 0x20 -> androidboot.selinux=enforce, dev_flags & 0x40 -> ...=permissive (كل منهما مُقيَّد بـ byte[+0x162]).
    0x118xx/0x11bxxباني سطر أوامر النواة (unlocked_kernel, prod=1/0, verifiedbootstate, rpmb_state, secure_cpu, الإصدارات, root=).
    0x27af8بوابة UART: fos_flags & 0x4 -> printk.disable_uart=0, وإلا =1.
    0x12fd4مُحمِّل بيئة LK: القسم "para"، 0x4000 بايت، السحر ENV_v1، المجموع الاختباري = مجموع البايتات على 0x3ffc مقارنةً بالكلمة @0x3ffc.
    0x1efd0البحث عن القسم بالاسم (يُستخدم لـ "para", "boot", ...).
    0x57cموزّع الجالب عبر خانة الاستدعاء @0x58218؛ الخانات @0x58200..0x5821c مسجّلة من جدول عند 0x5a8-0x734.
    0x2a19cfastboot oem unlock: bl 0x222c(code,len); عند النجاح يكتب unlock_code (0x100) عبر 0x408.
    unlocked_kernel=false
    /proc/cmdline
    androidboot.unlocked_kernel
    androidboot.prod
    unlock_code
    قلب eng/unlocked عبر المسار الموثّق مستحيل تشفيريًا.
  • لذلك يبقى المسار A (إعادة الاستغلال عند الإقلاع) محظورًا تمامًا كما في SESSION 11: مسار فتح القفل الوحيد لديه هو نفس بوابة LK.