macOS Universal binaries & Mach-O Format
Основна інформація
Бінарні файли Mac OS зазвичай компілюються як універсальні бінарні файли. Універсальний бінарний файл може підтримувати кілька архітектур у одному файлі.
Ці бінарні файли відповідають структурі Mach-O, яка в основному складається з:
Заголовка
Команд завантаження
Даних
Fat Header
Шукайте файл за допомогою: mdfind fat.h | grep -i mach-o | grep -E "fat.h$"
Заголовок містить магічні байти, за якими слідує кількість архітектур, які містить файл (nfat_arch
), і кожна архітектура матиме структуру fat_arch
.
Перевірте це за допомогою:
або використовуючи інструмент Mach-O View:
Як ви, можливо, мислите, зазвичай універсальний бінарний файл, скомпільований для 2 архітектур, подвоює розмір того, що скомпільовано лише для 1 архітектури.
Заголовок Mach-O
Заголовок містить базову інформацію про файл, таку як магічні байти для ідентифікації його як файлу Mach-O та інформацію про цільову архітектуру. Ви можете знайти його за допомогою: mdfind loader.h | grep -i mach-o | grep -E "loader.h$"
Типи файлів:
MH_EXECUTE (0x2): Стандартний виконавчий файл Mach-O
MH_DYLIB (0x6): Динамічна зв'язана бібліотека Mach-O (тобто .dylib)
MH_BUNDLE (0x8): Пакунок Mach-O (тобто .bundle)
Або використовуючи Mach-O View:
Команди завантаження Mach-O
Макет файлу в пам'яті вказаний тут, деталізуючи розташування таблиці символів, контекст основного потоку під час початку виконання та необхідні спільні бібліотеки. Інструкції надаються динамічному завантажувачу (dyld) щодо процесу завантаження бінарного файлу в пам'ять.
Використовує структуру load_command, визначену в зазначеному loader.h
:
Існує близько 50 різних типів команд завантаження, які система обробляє по-різному. Найпоширеніші з них: LC_SEGMENT_64
, LC_LOAD_DYLINKER
, LC_MAIN
, LC_LOAD_DYLIB
та LC_CODE_SIGNATURE
.
LC_SEGMENT/LC_SEGMENT_64
Фактично, цей тип команди завантаження визначає, як завантажувати сегменти __TEXT (виконавчий код) та __DATA (дані для процесу) згідно з вказаними зміщеннями в розділі Дані при виконанні бінарного файлу.
Ці команди визначають сегменти, які відображаються в віртуальному просторі пам'яті процесу при його виконанні.
Існують різні типи сегментів, такі як сегмент __TEXT, який містить виконавчий код програми, та сегмент __DATA, який містить дані, використовувані процесом. Ці сегменти розташовані в розділі даних файлу Mach-O.
Кожен сегмент може бути поділений на кілька секцій. Структура команди завантаження містить інформацію про ці секції у відповідному сегменті.
У заголовку спочатку ви знаходите заголовок сегмента:
Приклад заголовка сегмента:
Цей заголовок визначає кількість секцій, заголовки яких з'являються після нього:
Приклад заголовка розділу:
Якщо ви додаєте зміщення розділу (0x37DC) + зміщення, де починається архітектура, у цьому випадку 0x18000
--> 0x37DC + 0x18000 = 0x1B7DC
Також можна отримати інформацію про заголовки з командного рядка за допомогою:
Загальні сегменти, завантажені цією командою:
__PAGEZERO
: Це вказує ядру відобразити адресу нуль, щоб її не можна було читати, записувати або виконувати. Змінні maxprot та minprot у структурі встановлені на нуль, щоб показати, що на цій сторінці немає прав на читання-запис-виконання.Це виділення важливе для пом'якшення вразливостей нульових вказівників.
__TEXT
: Містить виконуваний код з правами на читання та виконання (без можливості запису). Загальні розділи цього сегмента:__text
: Скомпільований бінарний код__const
: Константні дані__cstring
: Рядкові константи__stubs
та__stubs_helper
: Використовуються під час процесу завантаження динамічних бібліотек__DATA
: Містить дані, які можна читати та писати (без можливості виконання).__data
: Глобальні змінні (які були ініціалізовані)__bss
: Статичні змінні (які не були ініціалізовані)__objc_*
(__objc_classlist, __objc_protolist, тощо): Інформація, використовувана в середовищі виконання Objective-C__LINKEDIT
: Містить інформацію для лінкера (dyld), таку як "запис, рядок та записи таблиці перенесення."__OBJC
: Містить інформацію, використовувану в середовищі виконання Objective-C. Хоча цю інформацію також можна знайти в сегменті __DATA, у різних розділах __objc_*.
LC_MAIN
LC_MAIN
Містить точку входу в атрибуті entryoff. Під час завантаження dyld просто додає це значення до (в пам'яті) бази бінарного файлу, а потім переходить до цієї інструкції для початку виконання коду бінарного файлу.
LC_CODE_SIGNATURE
Містить інформацію про підпис коду файлу Mach-O. Він містить лише зсув, який вказує на блоб підпису. Зазвичай це знаходиться в самому кінці файлу. Однак деяку інформацію про цей розділ можна знайти в цьому блозі та цьому gists.
LC_LOAD_DYLINKER
Містить шлях до виконавчого файлу динамічного лінкера, який відображає спільні бібліотеки в адресний простір процесу. Значення завжди встановлене на /usr/lib/dyld
. Важливо зауважити, що в macOS відображення dylib відбувається в режимі користувача, а не в режимі ядра.
LC_LOAD_DYLIB
LC_LOAD_DYLIB
Ця команда завантаження описує залежність динамічної бібліотеки, яка вказує завантажувачу (dyld) завантажити та зв'язати цю бібліотеку. Існує команда завантаження LC_LOAD_DYLIB для кожної бібліотеки, яку потребує бінарний файл Mach-O.
Ця команда завантаження є структурою типу
dylib_command
(яка містить структуру dylib, що описує фактичну залежну динамічну бібліотеку):
Ви також можете отримати цю інформацію з командного рядка за допомогою:
Деякі потенційно пов'язані з шкідливим ПЗ бібліотеки:
DiskArbitration: Моніторинг USB-накопичувачів
AVFoundation: Захоплення аудіо та відео
CoreWLAN: Сканування Wifi.
Mach-O бінарний файл може містити один або більше конструкторів, які будуть виконані перед адресою, вказаною в LC_MAIN. Зміщення будь-яких конструкторів зберігаються в розділі __mod_init_func сегмента __DATA_CONST.
Дані Mach-O
У основі файлу знаходиться область даних, яка складається з кількох сегментів, які визначені в області команд завантаження. У кожному сегменті можуть міститися різні секції даних, причому кожна секція містить код або дані, що специфічні для типу.
Дані - це в основному частина, що містить всю інформацію, яка завантажується командами завантаження LC_SEGMENTS_64
Це включає:
Таблиця функцій: Яка містить інформацію про функції програми.
Таблиця символів: Яка містить інформацію про зовнішню функцію, використану бінарним файлом
Також може містити внутрішні функції, назви змінних та інше.
Для перевірки цього можна використовувати інструмент Mach-O View:
Або з командного рядка:
Last updated