macOS Universal binaries & Mach-O Format
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)
Mac OS бінарні файли зазвичай компілюються як універсальні бінарні файли. Універсальний бінарний файл може підтримувати кілька архітектур в одному файлі.
Ці бінарні файли слідують структурі Mach-O, яка в основному складається з:
Заголовок
Команди завантаження
Дані
Шукайте файл за допомогою: mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
Заголовок має магнітні байти, за якими слідує кількість архітектур, які файл містить (nfat_arch
), і кожна архітектура матиме структуру fat_arch
.
Перевірте це за допомогою:
або за допомогою інструменту Mach-O View:
Як ви, напевно, думаєте, зазвичай універсальний бінарний файл, скомпільований для 2 архітектур, подвоює розмір одного, скомпільованого лише для 1 архітектури.
Заголовок містить основну інформацію про файл, таку як магнітні байти для його ідентифікації як файлу Mach-O та інформацію про цільову архітектуру. Ви можете знайти його в: mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
Існують різні типи файлів, ви можете знайти їх визначення в джерельному коді, наприклад, тут. Найважливіші з них:
MH_OBJECT
: Переміщуваний об'єктний файл (проміжні продукти компіляції, ще не виконувані).
MH_EXECUTE
: Виконувані файли.
MH_FVMLIB
: Фіксований файл бібліотеки VM.
MH_CORE
: Вивантаження коду
MH_PRELOAD
: Попередньо завантажений виконуваний файл (більше не підтримується в XNU)
MH_DYLIB
: Динамічні бібліотеки
MH_DYLINKER
: Динамічний зв'язувач
MH_BUNDLE
: "Файли плагінів". Генеруються за допомогою -bundle в gcc і явно завантажуються за допомогою NSBundle
або dlopen
.
MH_DYSM
: Супутній файл .dSym
(файл з символами для налагодження).
MH_KEXT_BUNDLE
: Розширення ядра.
Or using Mach-O View:
Джерельний код також визначає кілька прапорців, корисних для завантаження бібліотек:
MH_NOUNDEFS
: Немає невизначених посилань (повністю зв'язано)
MH_DYLDLINK
: Зв'язування Dyld
MH_PREBOUND
: Динамічні посилання попередньо зв'язані.
MH_SPLIT_SEGS
: Файл розділяє сегменти r/o та r/w.
MH_WEAK_DEFINES
: Бінарний файл має слабко визначені символи
MH_BINDS_TO_WEAK
: Бінарний файл використовує слабкі символи
MH_ALLOW_STACK_EXECUTION
: Зробити стек виконуваним
MH_NO_REEXPORTED_DYLIBS
: Бібліотека не має команд LC_REEXPORT
MH_PIE
: Виконуваний файл, незалежний від позиції
MH_HAS_TLV_DESCRIPTORS
: Є секція з локальними змінними потоку
MH_NO_HEAP_EXECUTION
: Немає виконання для сторінок купи/даних
MH_HAS_OBJC
: Бінарний файл має секції oBject-C
MH_SIM_SUPPORT
: Підтримка емулятора
MH_DYLIB_IN_CACHE
: Використовується для dylibs/frameworks у кеші спільних бібліотек.
Розташування файлу в пам'яті вказується тут, детально описуючи місцезнаходження таблиці символів, контекст основного потоку на початку виконання та необхідні спільні бібліотеки. Інструкції надаються динамічному завантажувачу (dyld) щодо процесу завантаження бінарного файлу в пам'ять.
Використовується структура load_command, визначена в згаданому loader.h
:
There are about 50 different types of load commands that the system handles differently. The most common ones are: LC_SEGMENT_64
, LC_LOAD_DYLINKER
, LC_MAIN
, LC_LOAD_DYLIB
, and LC_CODE_SIGNATURE
.
Основна суть цього типу Load Command полягає в тому, як завантажити __TEXT (виконуваний код) та __DATA (дані для процесу) сегменти відповідно до зсувів, вказаних у секції даних під час виконання бінарного файлу.
Ці команди визначають сегменти, які відображаються у віртуальному адресному просторі процесу під час його виконання.
Існують різні типи сегментів, такі як __TEXT сегмент, який містить виконуваний код програми, та __DATA сегмент, який містить дані, що використовуються процесом. Ці сегменти розташовані в секції даних файлу Mach-O.
Кожен сегмент може бути додатково поділений на кілька секцій. Структура команди завантаження містить інформацію про ці секції в межах відповідного сегмента.
У заголовку спочатку ви знайдете заголовок сегмента:
Example of segment header:
This header defines the number of sections whose headers appear after it:
Приклад заголовка секції:
Якщо ви додасте зсув секції (0x37DC) + зсув, де архітектура починається, в даному випадку 0x18000
--> 0x37DC + 0x18000 = 0x1B7DC
Також можливо отримати інформацію про заголовки з командного рядка за допомогою:
Загальні сегменти, завантажені цим cmd:
__PAGEZERO
: Він інструктує ядро відобразити адресу нуль так, щоб вона не могла бути прочитана, записана або виконана. Змінні maxprot і minprot у структурі встановлені в нуль, щоб вказати, що немає прав на читання-запис-виконання на цій сторінці.
Це виділення важливе для зменшення вразливостей, пов'язаних з розіменуванням нульових вказівників. Це пов'язано з тим, що XNU забезпечує жорстку нульову сторінку, яка гарантує, що перша сторінка (тільки перша) пам'яті недоступна (за винятком i386). Бінарний файл може виконати ці вимоги, створивши невелику __PAGEZERO (використовуючи -pagezero_size
), щоб покрити перші 4k, а решта 32-бітної пам'яті була доступна як в режимі користувача, так і в режимі ядра.
__TEXT
: Містить виконуваний код з правами на читання та виконання (без запису). Загальні секції цього сегмента:
__text
: Скомпільований бінарний код
__const
: Константні дані (тільки для читання)
__[c/u/os_log]string
: Константи рядків C, Unicode або os logs
__stubs
та __stubs_helper
: Бере участь у процесі завантаження динамічної бібліотеки
__unwind_info
: Дані для розгортання стеку.
Зверніть увагу, що весь цей вміст підписаний, але також позначений як виконуваний (створюючи більше можливостей для експлуатації секцій, які не обов'язково потребують цього привілею, як секції, присвячені рядкам).
__DATA
: Містить дані, які є читабельними та записуваними (без виконуваних).
__got:
Глобальна таблиця зсувів
__nl_symbol_ptr
: Неледачний (прив'язка при завантаженні) вказівник на символ
__la_symbol_ptr
: Ледачий (прив'язка при використанні) вказівник на символ
__const
: Має бути даними тільки для читання (насправді не так)
__cfstring
: Рядки CoreFoundation
__data
: Глобальні змінні (які були ініціалізовані)
__bss
: Статичні змінні (які не були ініціалізовані)
__objc_*
(__objc_classlist, __objc_protolist тощо): Інформація, що використовується середовищем виконання Objective-C
__DATA_CONST
: __DATA.__const не гарантує, що є константним (права на запис), як і інші вказівники та GOT. Цей розділ робить __const
, деякі ініціалізатори та таблицю GOT (після розв'язання) тільки для читання за допомогою mprotect
.
__LINKEDIT
: Містить інформацію для компоновщика (dyld), таку як символи, рядки та записи таблиці переміщення. Це загальний контейнер для вмісту, який не знаходиться в __TEXT
або __DATA
, а його вміст описується в інших командах завантаження.
Інформація dyld: Переміщення, неледачні/ледачі/слабкі коди прив'язки та інформація про експорт
Початок функцій: Таблиця початкових адрес функцій
Дані в коді: Острівці даних у __text
Таблиця символів: Символи в бінарному файлі
Таблиця непрямих символів: Вказівники/стаб символів
Таблиця рядків
Підпис коду
__OBJC
: Містить інформацію, що використовується середовищем виконання Objective-C. Хоча цю інформацію також можна знайти в сегменті __DATA, у різних секціях __objc_*.
__RESTRICT
: Сегмент без вмісту з єдиною секцією, що називається __restrict
(також порожня), яка забезпечує, що при виконанні бінарного файлу він ігноруватиме змінні середовища DYLD.
Як можна було побачити в коді, сегменти також підтримують прапорці (хоча вони не використовуються дуже часто):
SG_HIGHVM
: Тільки ядро (не використовується)
SG_FVMLIB
: Не використовується
SG_NORELOC
: Сегмент не має переміщення
SG_PROTECTED_VERSION_1
: Шифрування. Використовується, наприклад, Finder для шифрування тексту сегмента __TEXT
.
LC_UNIXTHREAD/LC_MAIN
LC_MAIN
містить точку входу в атрибуті entryoff. Під час завантаження dyld просто додає це значення до (в пам'яті) бази бінарного файлу, а потім переходить до цієї інструкції, щоб почати виконання коду бінарного файлу.
LC_UNIXTHREAD
містить значення, які повинні мати регістри при запуску основного потоку. Це вже застаріло, але dyld
все ще використовує це. Можливо побачити значення регістрів, встановлені цим:
LC_CODE_SIGNATURE
Містить інформацію про код підпису файлу Macho-O. Він містить лише зсув, який вказує на блоб підпису. Це зазвичай знаходиться в самому кінці файлу. Однак ви можете знайти деяку інформацію про цей розділ у цьому блозі та у цьому гісті.
LC_ENCRYPTION_INFO[_64]
Підтримка шифрування бінарних файлів. Однак, звичайно, якщо зловмисник зможе скомпрометувати процес, він зможе скинути пам'ять у незашифрованому вигляді.
LC_LOAD_DYLINKER
Містить шлях до виконуваного файлу динамічного зв'язувача, який відображає спільні бібліотеки в адресний простір процесу. Значення завжди встановлено на /usr/lib/dyld
. Важливо зазначити, що в macOS, відображення dylib відбувається в режимі користувача, а не в режимі ядра.
LC_IDENT
Застарілий, але коли налаштований на створення дампів при паніці, створюється дамп ядра Mach-O, і версія ядра встановлюється в команді LC_IDENT
.
LC_UUID
Випадковий UUID. Він корисний для чого завгодно, крім XNU, який кешує його разом з рештою інформації про процес. Його можна використовувати в звітах про збої.
LC_DYLD_ENVIRONMENT
Дозволяє вказати змінні середовища для dyld перед виконанням процесу. Це може бути дуже небезпечно, оскільки може дозволити виконувати довільний код всередині процесу, тому ця команда завантаження використовується лише в dyld, зібраному з #define SUPPORT_LC_DYLD_ENVIRONMENT
, і додатково обмежує обробку лише змінними у формі DYLD_..._PATH
, що вказують шляхи завантаження.
LC_LOAD_DYLIB
Ця команда завантаження описує залежність динамічної бібліотеки, яка інструктує завантажувач (dyld) завантажити та зв'язати зазначену бібліотеку. Існує команда завантаження LC_LOAD_DYLIB
для кожної бібліотеки, яка потрібна бінарному файлу Mach-O.
Ця команда завантаження є структурою типу dylib_command
(яка містить структуру dylib, що описує фактичну залежну динамічну бібліотеку):
Ви також можете отримати цю інформацію з командного рядка за допомогою:
Деякі потенційні бібліотеки, пов'язані з шкідливим ПЗ:
DiskArbitration: Моніторинг USB-накопичувачів
AVFoundation: Захоплення аудіо та відео
CoreWLAN: Сканування Wi-Fi.
Mach-O бінарний файл може містити один або більше конструкторів, які будуть виконані перед адресою, вказаною в LC_MAIN. Зсуви будь-яких конструкторів зберігаються в секції __mod_init_func сегмента __DATA_CONST.
В основі файлу лежить регіон даних, який складається з кількох сегментів, як визначено в регіоні команд завантаження. Різноманітні секції даних можуть міститися в кожному сегменті, при цьому кожна секція містить код або дані, специфічні для певного типу.
Дані в основному є частиною, що містить всю інформацію, яка завантажується командами завантаження LC_SEGMENTS_64
Це включає:
Таблиця функцій: Яка містить інформацію про функції програми.
Таблиця символів: Яка містить інформацію про зовнішні функції, що використовуються бінарним файлом
Вона також може містити внутрішні функції, імена змінних та інше.
Щоб перевірити це, ви можете використовувати інструмент Mach-O View:
Або з командного рядка:
У сегменті __TEXT
(r-x):
__objc_classname
: Імена класів (рядки)
__objc_methname
: Імена методів (рядки)
__objc_methtype
: Типи методів (рядки)
У сегменті __DATA
(rw-):
__objc_classlist
: Вказівники на всі класи Objective-C
__objc_nlclslist
: Вказівники на некешовані класи Objective-C
__objc_catlist
: Вказівник на категорії
__objc_nlcatlist
: Вказівник на некешовані категорії
__objc_protolist
: Список протоколів
__objc_const
: Константні дані
__objc_imageinfo
, __objc_selrefs
, objc__protorefs
...
_swift_typeref
, _swift3_capture
, _swift3_assocty
, _swift3_types, _swift3_proto
, _swift3_fieldmd
, _swift3_builtin
, _swift3_reflstr
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)