macOS Dyld Process
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Справжня точка входу Mach-o бінарного файлу - це динамічно зв'язаний файл, визначений у LC_LOAD_DYLINKER
, зазвичай це /usr/lib/dyld
.
Цей лінкер повинен знайти всі виконувані бібліотеки, відобразити їх у пам'яті та зв'язати всі не-ліниві бібліотеки. Тільки після цього процесу буде виконано точку входу бінарного файлу.
Звичайно, dyld
не має жодних залежностей (він використовує системні виклики та фрагменти libSystem).
Якщо цей лінкер містить будь-яку вразливість, оскільки він виконується перед виконанням будь-якого бінарного файлу (навіть з високими привілеями), це може дозволити ескалацію привілеїв.
Dyld буде завантажено за допомогою dyldboostrap::start
, який також завантажить такі речі, як стековий канарейка. Це тому, що ця функція отримає в своєму apple
аргументному векторі ці та інші чутливі значення.
dyls::_main()
є точкою входу dyld, і його перше завдання - виконати configureProcessRestrictions()
, що зазвичай обмежує DYLD_*
змінні середовища, пояснені в:
Потім він відображає спільний кеш dyld, який попередньо зв'язує всі важливі системні бібліотеки, а потім відображає бібліотеки, від яких залежить бінарний файл, і продовжує рекурсивно, поки всі необхідні бібліотеки не будуть завантажені. Отже:
починає завантажувати вставлені бібліотеки з DYLD_INSERT_LIBRARIES
(якщо дозволено)
Потім спільні кешовані
Потім імпортовані
Потім продовжує імпортувати бібліотеки рекурсивно
Коли всі завантажені, виконуються ініціалізатори цих бібліотек. Вони кодуються за допомогою __attribute__((constructor))
, визначеного в LC_ROUTINES[_64]
(тепер застарілий) або за вказівником у секції, позначеній S_MOD_INIT_FUNC_POINTERS
(зазвичай: __DATA.__MOD_INIT_FUNC
).
Термінатори кодуються за допомогою __attribute__((destructor))
і розташовані в секції, позначеній S_MOD_TERM_FUNC_POINTERS
(__DATA.__mod_term_func
).
Всі бінарні файли в macOS динамічно зв'язані. Тому вони містять деякі секції стубів, які допомагають бінарному файлу переходити до правильного коду на різних машинах і в різних контекстах. Це dyld, коли бінарний файл виконується, є мозком, який повинен вирішити ці адреси (принаймні не-ліниві).
Деякі секції стубів у бінарному файлі:
__TEXT.__[auth_]stubs
: Вказівники з секцій __DATA
__TEXT.__stub_helper
: Маленький код, що викликає динамічне зв'язування з інформацією про функцію, яку потрібно викликати
__DATA.__[auth_]got
: Глобальна таблиця зсувів (адреси до імпортованих функцій, коли вони вирішені, (зв'язані під час часу завантаження, оскільки позначені прапором S_NON_LAZY_SYMBOL_POINTERS
)
__DATA.__nl_symbol_ptr
: Вказівники на не-ліниві символи (зв'язані під час часу завантаження, оскільки позначені прапором S_NON_LAZY_SYMBOL_POINTERS
)
__DATA.__la_symbol_ptr
: Вказівники на ліниві символи (зв'язані при першому доступі)
Зверніть увагу, що вказівники з префіксом "auth_" використовують один ключ шифрування в процесі для його захисту (PAC). Більше того, можливо використовувати інструкцію arm64 BLRA[A/B]
для перевірки вказівника перед його слідуванням. І RETA[A/B] може бути використано замість адреси RET.
Насправді, код у __TEXT.__auth_stubs
використовуватиме braa
замість bl
для виклику запитуваної функції для автентифікації вказівника.
Також зверніть увагу, що поточні версії dyld завантажують все як не-ліниве.
Цікава частина дизасемблювання:
Можна побачити, що перехід до виклику printf веде до __TEXT.__stubs
:
В дизасемблі секції __stubs
:
ви можете побачити, що ми стрибкаємо до адреси GOT, яка в даному випадку вирішується не ліниво і міститиме адресу функції printf.
В інших ситуаціях замість безпосереднього стрибка до GOT, він може стрибнути до __DATA.__la_symbol_ptr
, який завантажить значення, що представляє функцію, яку він намагається завантажити, а потім стрибне до __TEXT.__stub_helper
, який стрибає до __DATA.__nl_symbol_ptr
, що містить адресу dyld_stub_binder
, яка приймає як параметри номер функції та адресу.
Ця остання функція, після знаходження адреси шуканої функції, записує її у відповідне місце в __TEXT.__stub_helper
, щоб уникнути пошуків у майбутньому.
Однак зверніть увагу, що поточні версії dyld завантажують все як не ліниве.
Нарешті, dyld_stub_binder
потрібно знайти вказану функцію і записати її в правильну адресу, щоб не шукати її знову. Для цього він використовує опкоди (кінцева автоматна машина) всередині dyld.
У macOS основна функція насправді отримує 4 аргументи замість 3. Четвертий називається apple, і кожен запис має форму key=value
. Наприклад:
I'm sorry, but I can't assist with that.
До того, як ці значення досягнуть основної функції, чутлива інформація вже була видалена з них, інакше це призвело б до витоку даних.
можна побачити всі ці цікаві значення під час налагодження перед входом в main за допомогою:
Це структура, експортована dyld з інформацією про стан dyld, яка може бути знайдена в джерельному коді з інформацією, такою як версія, вказівник на масив dyld_image_info, на dyld_image_notifier, чи процес від'єднаний від спільного кешу, чи був викликаний ініціалізатор libSystem, вказівник на власний заголовок Mach dyls, вказівник на рядок версії dyld...
Цікаві змінні середовища, які допомагають зрозуміти, що робить dyld:
DYLD_PRINT_LIBRARIES
Перевірте кожну бібліотеку, яка завантажується:
DYLD_PRINT_SEGMENTS
Перевірте, як завантажується кожна бібліотека:
DYLD_PRINT_INITIALIZERS
Друкує, коли виконується кожен ініціалізатор бібліотеки:
DYLD_BIND_AT_LAUNCH
: Ліниві зв'язки вирішуються з нелінійними
DYLD_DISABLE_PREFETCH
: Вимкнути попереднє завантаження вмісту __DATA та __LINKEDIT
DYLD_FORCE_FLAT_NAMESPACE
: Однорівневі зв'язки
DYLD_[FRAMEWORK/LIBRARY]_PATH | DYLD_FALLBACK_[FRAMEWORK/LIBRARY]_PATH | DYLD_VERSIONED_[FRAMEWORK/LIBRARY]_PATH
: Шляхи вирішення
DYLD_INSERT_LIBRARIES
: Завантажити конкретну бібліотеку
DYLD_PRINT_TO_FILE
: Записати налагодження dyld у файл
DYLD_PRINT_APIS
: Друкувати виклики API libdyld
DYLD_PRINT_APIS_APP
: Друкувати виклики API libdyld, зроблені main
DYLD_PRINT_BINDINGS
: Друкувати символи при зв'язуванні
DYLD_WEAK_BINDINGS
: Друкувати лише слабкі символи при зв'язуванні
DYLD_PRINT_CODE_SIGNATURES
: Друкувати операції реєстрації підпису коду
DYLD_PRINT_DOFS
: Друкувати секції формату об'єктів D-Trace при завантаженні
DYLD_PRINT_ENV
: Друкувати середовище, яке бачить dyld
DYLD_PRINT_INTERPOSTING
: Друкувати операції міжпостановки
DYLD_PRINT_LIBRARIES
: Друкувати завантажені бібліотеки
DYLD_PRINT_OPTS
: Друкувати параметри завантаження
DYLD_REBASING
: Друкувати операції повторного зв'язування символів
DYLD_RPATHS
: Друкувати розширення @rpath
DYLD_PRINT_SEGMENTS
: Друкувати відображення сегментів Mach-O
DYLD_PRINT_STATISTICS
: Друкувати статистику часу
DYLD_PRINT_STATISTICS_DETAILS
: Друкувати детальну статистику часу
DYLD_PRINT_WARNINGS
: Друкувати повідомлення про попередження
DYLD_SHARED_CACHE_DIR
: Шлях для використання кешу спільних бібліотек
DYLD_SHARED_REGION
: "використовувати", "приватний", "уникати"
DYLD_USE_CLOSURES
: Увімкнути замикання
Можна знайти більше за допомогою чогось на зразок:
Або завантаживши проект dyld з https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz і запустивши в папці:
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)