
Злоупотребляет энтайтлментами отладчика macOS и переменной DYLD_INSERT_LIBRARIES для снятия дампа памяти запущенного процесса или поиска по ней, перекладывая атрибуцию EDR на подписанный вспомогательный бинарный файл.
memdumper читает память запущенного процесса macOS, используя entitlement отладчика другого подписанного бинарника. Цель — чтобы task_for_pid и ptrace выполнялись от имени этого бинарника с entitlement, и EDR записал именно его, а не memdumper.
от cenobyte [email protected] 2026
https://github.com/cenobyte-vincit/memdumper
memdumper запускает бинарник с entitlement, который несёт com.apple.security.cs.debugger плюс два entitlement, разрешающих вставку через DYLD_INSERT_LIBRARIES. Используйте его, чтобы искать и дампить память другого процесса от root. OpenJDK jspawnhelper — часто встречающийся бинарник, особенно на машинах разработчиков.
Передайте attach.sh PID цели и бинарник с entitlement. Конструктор dylib выполняется до main этого бинарника, читает цель, затем вызывает _exit. Бинарник с entitlement так и не доходит до main, поэтому такой хелпер, как jspawnhelper, не печатает свой баннер использования. Дополнительные аргументы после бинарника с entitlement остаются в командной строке, которую записывает EDR. Без -s dylib делает hex-дамп начала каждой читаемой области. С -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 от root из каталога, в котором лежит memdumper.dylib. Вызов не от root отклоняется до запуска бинарника с entitlement (root required). Это позволяет избежать диалога пароля Developer Tool Access (taskgated / Authorization Services). Этот диалог не является частью Transparency, Consent, and Control (TCC).
sudo ./attach.sh [options] -p <pid> <entitled-binary> [args...]
| Опция | Значение |
|---|---|
-p, --pid PID | ID целевого процесса (обязательно) |
-s, --search STRING | Искать STRING в читаемых областях |
-n, --max-regions N | Максимум читаемых областей для hex-дампа (по умолчанию 100). Только для пути дампа |
-f, --force | Продолжить, если codesign сообщает о Hardened Runtime |
-h, --help | Показать справку (код выхода 1) |
Убедитесь, что кандидат в бинарники с entitlement имеет все три entitlement. Префикс Cellar и версия OpenJDK различаются; attach.sh использует такую раскладку в собственной справке:
codesign -d --entitlements - \
/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib/jspawnhelper
Дамп читаемых областей PID 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 как бинарник с entitlement. TestSpawn остаётся в командной строке; main у java не выполняется:
sudo ./attach.sh -n 50 -p 4543 \
/opt/homebrew/Cellar/openjdk/25.0.2/bin/java TestSpawn
Редактор без Hardened Runtime — рабочая цель для быстрой проверки. Запустите его от пользователя, которому принадлежит буфер, затем присоединитесь от root. В одном терминале:
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 не проверяет entitlement бинарника с entitlement. Если в бинарнике не хватает любого из трёх, вставка или task_for_pid завершатся ошибкой.
| Код | Значение |
|---|---|
| 0 | Конструктор завершился после успешной вставки |
| 1 | Ошибка использования, не root, отсутствует memdumper.dylib в текущем каталоге, отсутствует -p, PID цели не найден, Hardened Runtime без --force, или вставка не произошла (тогда бинарник с entitlement запустился и завершился с ненулевым кодом) |
Смотрите [RESULT] для оценки результата дампа. Код выхода 0 означает только то, что конструктор выполнился и вызвал _exit.
Dylib читает три переменные окружения. attach.sh задаёт их из флагов.