
Загрузчик Mach-O dylib в память для штатного Python в macOS; расшифровывает, отображает в память и запускает пейлоады без использования dlopen и записи на диск, с опциональным внедрением зашифрованного файла-обложки.
dyen загружает Mach-O dylib в память с помощью штатного Python из macOS и вызывает экспортируемую точку входа без dlopen для этого dylib. Цель — чтобы образ никогда не существовал отдельным файлом на целевой системе и никогда не регистрировался в dyld.
автор: cenobyte [email protected] 2026
https://github.com/cenobyte-vincit/dyen
dyen — это загрузчик Mach-O в память только на стандартной библиотеке (loader.py) для штатного /usr/bin/python3 на macOS. Он читает обычный dylib, автономный DYEN v1 blob (зашифрованный конверт) или файл-прикрытие с добавленным в конец (EOF) blob, отображает соответствующий слайс через анонимный mmap, применяет классические фиксапы rebase/bind и вызывает dyld_main через указатель на функцию ctypes.
encrypt_dyld.py — это инструмент подготовки: он оборачивает dylib в DYEN blob и выводит его в stdout либо копирует файл-прикрытие в dyld_<basename> с добавленным blob. Входящий в комплект пейлоад dyld/dyld.c только выводит hello world. loader.py работает только на macOS; шифровальщик работает на Linux или macOS.
Загрузчик из репозитория читает локальный путь или stdin. Дроппер или стадия 1 могли бы получать файл-прикрытие (или исходные байты) по сети и передавать их в read_dyld_bytes(), чтобы зашифрованный dylib никогда не оказывался на целевой системе отдельным файлом.
.dyen или файл-прикрытие.hello world). Диагностика идёт в stderr./usr/bin/python3 (только стандартная библиотека)loader.py работает только на macOS. Для компиляции PoC 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 blob перед отображением — либо из отдельного .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.
CLI загрузчика принимает один путь к файлу или читает stdin. У него нет флага пароля. При расшифровке сначала пробуется аппаратный серийный номер этого Mac, затем DEFAULT_DYLD_KEY (CHANGEMEASAP). Если серийный номер прочитать не удаётся, используется только захардкоженный ключ. CLI всегда загружает входные данные и вызывает 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
Файл-прикрытие без добавленного blob завершается ошибкой:
/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 blob, расшифровку, 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 по хранению паролей для 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 запускает линтеры, собирает PoC 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.
Маскировка добавлением в конец файла (EOF) видна в hex-дампе.
Боевое применение: перед использованием измените DYENCRYPT. PoC-магическое значение в DYLD_BLOB_MAGIC (loader.py) — статическая сигнатура. YARA-правила, файловые сканеры и простые эвристики с rfind могут использовать её в качестве ключевого признака. Замените её уникальным значением по вашему выбору (держите encrypt и loader синхронизированными; длина не фиксирована, но для встраивания в файлы-прикрытия лучше больше).
Входящий в репозиторий dylib лишь выводит hello world.