
يستغل صلاحيات مصحح الأخطاء في macOS وDYLD_INSERT_LIBRARIES لتفريغ ذاكرة عملية قيد التشغيل أو البحث فيها، مع تحويل إسناد EDR إلى ملف ثنائي مساعد موقّع.
memdumper يقرأ ذاكرة عملية macOS قيد التشغيل باستخدام ترخيص المصحح (debugger entitlement) الخاص بثنائي موقّع آخر. الهدف هو أن يتم تشغيل task_for_pid وptrace بصفة ذلك الثنائي المصرَّح له، بحيث يسجّله نظام كشف الاستجابة للنقاط الطرفية (EDR) بدلاً من memdumper.
بقلم cenobyte [email protected] 2026
https://github.com/cenobyte-vincit/memdumper
يُشغّل memdumper ثنائيًا مصرَّحًا له يحمل com.apple.security.cs.debugger بالإضافة إلى الترخيصين اللذين يسمحان بحقن DYLD_INSERT_LIBRARIES. استخدمه للبحث عن ذاكرة عملية أخرى وتفريغها بصلاحيات الجذر (root). يُعد jspawnhelper من OpenJDK ثنائيًا شائع الوجود، خاصة على أجهزة المطوّرين.
أعطِ attach.sh معرّف العملية المستهدفة (PID) والثنائي المصرَّح له. يعمل مُنشئ (constructor) الـ dylib قبل main الخاص بذلك الثنائي، فيقرأ الهدف ثم يستدعي _exit. لا يصل الثنائي المصرَّح له أبدًا إلى main، لذلك لا يطبع مساعد مثل jspawnhelper رسالة الاستخدام الخاصة به. تبقى الوسائط الإضافية بعد الثنائي المصرَّح له في سطر الأوامر الذي يسجّله EDR. بدون -s، يقوم الـ dylib بطباعة تمثيل سداسي عشري (hex-dump) لبداية كل منطقة قابلة للقراءة. مع -s يبحث في تلك المناطق عن السلسلة النصية. لا تتم كتابة مساحة العنوان المستهدفة. قد يؤدي الإرفاق عبر ptrace إلى إيقاف الهدف مؤقتًا.
حالة التفريغ هي سطر [RESULT] في المخرجات المدمجة. يؤدي الإدراج الناجح إلى الخروج بالكود 0 بعد انتهاء المُنشئ.
com.apple.security.cs.debugger وcom.apple.security.cs.allow-dyld-environment-variables وcom.apple.security.cs.disable-library-validation (على سبيل المثال jspawnhelper من OpenJDK)clangmakeattach.sh وسكربتات الاختبار (brew install shellcheck)brew install cppcheck)على مضيف البناء:
make
يُنتج ذلك memdumper.dylib. انسخ attach.sh وmemdumper.dylib إلى الهدف إذا لم يكن الجهازان متطابقين.
شغّل attach.sh بصلاحيات الجذر من الدليل الذي يحتوي على memdumper.dylib. يُرفض أي مستخدم غير جذر قبل بدء تشغيل الثنائي المصرَّح له (root required). يتجنّب ذلك نافذة كلمة مرور Developer Tool Access (taskgated / Authorization Services). تلك النافذة ليست من نظام الشفافية والموافقة والتحكم (TCC).
sudo ./attach.sh [options] -p <pid> <entitled-binary> [args...]
| الخيار | المعنى |
|---|---|
-p, --pid PID | معرّف العملية المستهدفة (مطلوب) |
-s, --search STRING | البحث عن STRING في المناطق القابلة للقراءة |
-n, --max-regions N | الحد الأقصى للمناطق القابلة للقراءة للطباعة السداسية العشرية (الافتراضي 100). مسار التفريغ فقط |
-f, --force | المتابعة عندما يُبلغ codesign عن Hardened Runtime |
-h, --help | طباعة الاستخدام (الخروج بالكود 1) |
تحقّق من أن الثنائي المصرَّح له المرشّح يحمل التراخيص الثلاثة. يختلف بادئة Cellar وإصدار OpenJDK؛ يستخدم attach.sh هذا التخطيط في ملف المساعدة الخاص به:
codesign -d --entitlements - \
/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
افرغ المناطق القابلة للقراءة للعملية 4543:
sudo ./attach.sh -p 4543 \
/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
ابحث في تلك العملية عن HELLO:
sudo ./attach.sh -s HELLO -p 4543 \
/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
اقصر التفريغ على 50 منطقة قابلة للقراءة، باستخدام java كالثنائي المصرَّح له. يبقى TestSpawn في سطر الأوامر؛ ولا يتم تشغيل main الخاص بـ java:
sudo ./attach.sh -n 50 -p 4543 \
/opt/homebrew/Cellar/openjdk/25.0.2/bin/java TestSpawn
محرّر نصوص بدون Hardened Runtime يُعد هدفًا جيدًا لاختبار سريع (smoke test). ابدأ تشغيله باسم المستخدم الذي يملك الملف، ثم ارفق بصلاحيات الجذر. في طرفية واحدة:
nano foo
ضع HELLO في المخزن المؤقت بحيث تكون السلسلة موجودة في العملية (احفظ الملف أو اتركه دون حفظ). في طرفية أخرى، من الدليل الذي يحوي memdumper.dylib:
sudo ./attach.sh -s HELLO -p "$(pgrep nano)" \
/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
تبدو النتيجة الناجحة هكذا. بعدها تخرج العملية بالكود 0:
Target: pid 9810 (nano)
Target has no hardened runtime
[MEMDUMPER] Injected into PID 9852 (UID 0)
[MEMDUMPER] Entitled binary: /opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
[MEMDUMPER] Target: PID 9810 (nano)
[METHOD1] task_for_pid() on PID 9810 (nano)
[*] Got task port 0x1013
[*] Task info: virt=425098.0MB res=8.3MB
[*] Threads: 1
[MEMORY SEARCH] Looking for "HELLO" in PID 9810
MATCH at 0xa70c60200 (region: 0xa70c00000-0xa71000000, prot: rw-)
Context:
0x0000000a70c601c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x0000000a70c601d0: 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |O...............|
0x0000000a70c601e0: 23 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |#...............|
0x0000000a70c601f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x0000000a70c60200: 48 45 4c 4c 4f 00 00 00 00 00 00 00 00 00 00 00 |HELLO...........|
0x0000000a70c60210: 66 6f 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 |foo.............|
0x0000000a70c60220: 6f d9 b9 83 27 dc 6a ec 00 04 00 00 00 00 00 00 |o...'.j.........|
0x0000000a70c60230: 48 45 4c 4c 4f 00 00 00 00 00 00 00 00 00 00 00 |HELLO...........|
0x0000000a70c60240: 2e 2f 00 00 00 |./...|
MATCH at 0xa70c60230 (region: 0xa70c00000-0xa71000000, prot: rw-)
Context:
0x0000000a70c601f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
0x0000000a70c60200: 48 45 4c 4c 4f 00 00 00 00 00 00 00 00 00 00 00 |HELLO...........|
0x0000000a70c60210: 66 6f 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 |foo.............|
0x0000000a70c60220: 6f d9 b9 83 27 dc 6a ec 00 04 00 00 00 00 00 00 |o...'.j.........|
0x0000000a70c60230: 48 45 4c 4c 4f 00 00 00 00 00 00 00 00 00 00 00 |HELLO...........|
0x0000000a70c60240: 2e 2f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |./..............|
0x0000000a70c60250: 66 6f 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 |foo.............|
0x0000000a70c60260: 2f d9 b9 83 27 dc 6a ec 22 90 00 00 00 00 00 00 |/...'.j.".......|
0x0000000a70c60270: 00 00 00 00 00 |.....|
Search complete: 2 matches found in 70 regions
[METHOD2] ptrace(PT_ATTACHEXC) on PID 9810 (nano)
[*] Attached with ptrace
[*] Detached successfully
[METHOD3] proc_pidinfo() on PID 9810 (nano)
[*] Process info: virt=425098MB res=8MB threads=1
[RESULT] 3/3 methods successful - FULL ACCESS
[MEMDUMPER] Done
لا يتحقق attach.sh من تراخيص الثنائي المصرَّح له. أي ثنائي ينقصه أي من التراخيص الثلاثة يفشل عند الإدراج أو عند task_for_pid.
| الكود | المعنى |
|---|---|
| 0 | انتهى المُنشئ بعد إدراج ناجح |
| 1 | خطأ في الاستخدام، أو عدم وجود صلاحيات الجذر، أو غياب memdumper.dylib في الدليل الحالي، أو غياب -p، أو عدم العثور على معرّف العملية المستهدف، أو Hardened Runtime بدون --force، أو لم يحدث الإدراج (عندها يعمل الثنائي المصرَّح له ويخرج بكود غير صفري) |
اقرأ [RESULT] لمعرفة نتيجة التفريغ. الخروج بالكود 0 يعني فقط أن المُنشئ عمل واستدعى _exit.
يقرأ الـ dylib ثلاثة متغيرات بيئة. يضبطها attach.sh من الخيارات.
| المتغير | يُضبط بواسطة | المعنى |
|---|---|---|
MEMDUMPER_TARGET_PID | -p | معرّف العملية المستهدفة. إذا كان غير مضبوط أو ليس عددًا صحيحًا موجبًا، يطبع المُنشئ رسالة تشخيص ويعود |
MEMDUMPER_SEARCH | -s | إذا لم يكن فارغًا، يبحث بدلاً من التفريغ |
MEMDUMPER_MAX_REGIONS | -n | المناطق القابلة للقراءة للطباعة السداسية العشرية (الافتراضي 100). يُتجاهل في مسار البحث |
يمكن لأي مُشغِّل يضبط DYLD_INSERT_LIBRARIES على memdumper.dylib وهذه المتغيرات أن يحقن الـ dylib. attach.sh هو المُشغِّل المضمّن في المستودع.