Как открыть и изучить Mach-O файл на macOS, как если бы ты работал с PE-файлом в Windows

Как открыть и изучить Mach-O файл на macOS, как если бы ты работал с PE-файлом в Windows

Ты — разработчик, который привык к IDA Pro, Ghidra или Binary Ninja на Windows, и тебе нужно разобраться с macOS-приложением. Ты знаешь, как читать PE-заголовки, искать секции .text и .data, находить импорты и анализировать вызовы API. А теперь — Mach-O. Ничего не похоже. Нет «PE заголовка», нет «Import Address Table». Ты чувствуешь, как будто перешёл в чужую вселенную. Не паникуй. Это не магия. Это просто другой формат. И если ты умеешь читать PE, ты уже умеешь читать Mach-O. Просто нужно перевести язык.

В этой статье я покажу, как открыть Mach-O файл так, как ты бы открыл PE. Без лишней теории. Только то, что нужно, чтобы начать анализировать бинарник на macOS прямо сейчас.

Что тебе нужно: инструменты, которые уже есть

Если ты работаешь с PE-файлами, ты, скорее всего, используешь:

  • IDA Pro
  • Ghidra
  • Binary Ninja
  • objdump / dumpbin / x64dbg

Хорошие новости: все эти инструменты работают с Mach-O без дополнительной настройки. Да, ты не ошибся. IDA Pro открывает .app/Contents/MacOS/YourApp без вопросов. Ghidra — тоже. Binary Ninja — отлично. Ты не должен учиться заново. Ты просто должен понять, как названия и структуры изменились.

Если ты не используешь ни один из них — установи Ghidra. Бесплатно, от NSA, поддерживает Mach-O на уровне PE. Или используй objdump из Xcode Command Line Tools — он есть на любом Mac с установленным Xcode.

Шаг 1: Найди настоящий бинарник — не .app

Ты скачал приложение. Открываешь папку — вижу MyApp.app. Пытаешься открыть его в IDA — не получается. Почему? Потому что .app — это не бинарник. Это пакет — директория с ресурсами, локализацией, Info.plist и, самое главное, бинарником внутри.

Путь к настоящему файлу:

/Applications/MyApp.app/Contents/MacOS/MyApp

Именно этот файл — Mach-O. Именно его нужно открывать. Это аналог myapp.exe в Windows. Не пытайся открыть Info.plist или Resources/ в Ghidra — они не бинарные. Если ты не знаешь, где бинарник — открой терминал и введи:

find /Applications/MyApp.app -type f -perm +111 -name "*"

Это найдёт все исполняемые файлы внутри пакета. Обычно их один — и он лежит в MacOS/.

Шаг 2: Понимай структуру — как PE, но по-маковски

PE-файл начинается с DOS-заголовка, потом — PE-заголовок, потом — секции (.text, .data, .rdata). Mach-O — другая логика. Но структура та же: заголовок → сегменты → секции.

В PE:

  • IMAGE_NT_HEADERS — заголовок
  • IMAGE_SECTION_HEADER — описание секций
  • Секции — .text, .data, .rdata

В Mach-O:

  • mach_header_64 — заголовок (вместо IMAGE_NT_HEADERS)
  • load_command — команды загрузки (вместо таблицы секций)
  • Сегменты — __TEXT, __DATA, __LINKEDIT
  • Секции внутри сегментов — __text, __data, __bss

Ключевое отличие: в PE секции — это основа. В Mach-O — сегменты. Секции — это подразделы сегментов. Например:

  • __TEXT — содержит исполняемый код (аналог .text)
  • __DATA — переменные, инициализированные данные (аналог .data)
  • __LINKEDIT — метаданные для динамического линковщика (аналог .idata + .edata в PE, но в одном месте)

Чтобы увидеть это в Ghidra: открой файл → в окне «Symbol Tree» найди «Segments». Там ты увидишь __TEXT, __DATA и т.д. Кликни на __TEXT → увидишь секции внутри: __text, __stubs, __cstring.

Это как если бы в PE ты видел не только .text, но и .rdata, .idata, .pdata внутри одного сегмента. Mach-O объединяет логику. Не пугайся — просто запомни: сегмент = контейнер, секция = часть контейнера.

Шаг 3: Импорты и экспорты — где они?

В PE ты ищешь Import Address Table (IAT) — там перечислены все функции из DLL. В Mach-O нет IAT. Есть dyld — динамический линковщик. И он работает иначе.

Импорты в Mach-O — это символы, которые бинарник запрашивает у системных библиотек. Их можно найти в двух местах:

  1. Символы импорта — в секции __stubs и __stub_helper внутри __TEXT. Там лежат маленькие «переходники» — они вызывают dyld, чтобы найти реальную функцию.
  2. Список библиотек — в команде LC_LOAD_DYLIB. Ты можешь увидеть их в Ghidra: «Symbol Table» → «Imported Symbols». Там будут строки вроде _NSLog, _objc_msgSend, _pthread_create.

Чтобы посмотреть, какие библиотеки подключены — в терминале:

otool -L /Applications/MyApp.app/Contents/MacOS/MyApp

Вывод:

/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 2113.20.115)
/usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)

Это твой аналог dumpbin /imports в Windows. Только здесь ты видишь не адреса, а имена библиотек.

Экспорты — это функции, которые сама библиотека предоставляет другим. В Mach-O они тоже в __LINKEDIT. В Ghidra: «Symbol Table» → «Exported Symbols». В терминале:

nm -g /Applications/MyApp.app/Contents/MacOS/MyApp

Вывод будет вида:

0000000100001234 T _main
0000000100002345 T _initializeApp
0000000100003456 T _handleClick

Буква T — значит, символ экспортируется (текстовая секция). U — неопределённый (импорт). Просто как в PE, но без таблиц.

Шаг 4: Динамический линковщик — твой новый друг

В Windows ты видишь, как kernel32.dll подгружается в память. В macOS всё делает dyld. Он загружает библиотеки, разрешает адреса, заполняет таблицы вызовов — всё автоматически.

Чтобы увидеть, как он работает — используй dyld в режиме отладки. В терминале:

export DYLD_PRINT_LIBRARIES=1
/Applications/MyApp.app/Contents/MacOS/MyApp

Ты увидишь, какие библиотеки загружаются, в каком порядке. Это как Process Monitor для загрузки DLL, но для macOS.

Если ты хочешь понять, как работает линковка на уровне байтов — открой секцию __LINKEDIT в Ghidra. Там лежит бинарный «словарь»: имена функций, адреса, таблицы хеширования. Это как IAT, но сжато и с хешами. Ты не будешь читать это вручную — Ghidra сделает это за тебя. Но если ты видишь, что в __LINKEDIT есть огромный блок данных — это нормально. Это не вирус, это метаданные линковщика.

Сравнение: PE vs Mach-O — что где искать

Вот таблица, которая поможет тебе быстро переключиться с Windows на macOS.

Что ты ищешь в PE Где искать в Mach-O Команда в терминале
PE-заголовок mach_header_64 в начале файла otool -h /path/to/binary
Секции (.text, .data) Сегменты __TEXT, __DATA с секциями внутри otool -l /path/to/binary
Import Address Table (IAT) Символы в __stubs и __stub_helper; список библиотек в LC_LOAD_DYLIB otool -I /path/to/binary
Экспорты Символы с флагом T в __TEXT nm -g /path/to/binary
Ресурсы (ICO, DLL, строковые таблицы) Внутри .app/Contents/Resources/ — это не бинарник ls /Applications/MyApp.app/Contents/Resources/
Стек, куча, импорты в отладчике То же самое — Ghidra/IDA показывают память одинаково

Запомни: если ты ищешь импорты — смотри на otool -I и nm -u. Если ищешь экспорты — nm -g. Если хочешь увидеть структуру — otool -l. Все команды есть в macOS по умолчанию, если установлены Xcode Command Line Tools.

Частые ошибки — и как их избежать

  1. Открываешь .app как файл — не работает. Открывай только Contents/MacOS/YourApp.
  2. Ищешь .idata — её нет. Ищи __stubs и LC_LOAD_DYLIB.
  3. Думаешь, что __LINKEDIT — это вредоносный код — нет, это просто метаданные линковщика. Там нет исполняемого кода, только структуры.
  4. Не видишь функций в Ghidra — возможно, бинарник оптимизирован. Попробуй включить «Auto Analysis» с опцией «Create Functions». Иногда нужно вручную указать начало функции (клик правой кнопкой → «Create Function»).
  5. Используешь Windows-инструменты вроде x64dbg — они не работают с Mach-O. Используй только те, что поддерживают macOS (IDA, Ghidra, Binary Ninja).

Что выбрать в зависимости от ситуации

Ты не всегда хочешь глубокий анализ. Иногда нужно просто понять, что делает приложение.

  • Ты просто хочешь посмотреть, какие библиотеки использует приложение?otool -L — 2 секунды, ничего не устанавливать.
  • Ты хочешь найти, где вызывается NSAlert или CFNetwork? → Открой Ghidra → ищи в «Symbol Table» по ключевому слову. Удобно, быстро, без реверсинга всего.
  • Ты разбираешь вредоносное ПО и ищешь вызовы system() или execve()? → Используй IDA Pro или Binary Ninja. Они лучше показывают контроль потока и вызовы функций. Ghidra — тоже подойдёт, но медленнее.
  • Ты хочешь быстро посмотреть, есть ли в бинарнике строки вроде «https://» или «localhost»?strings /path/to/binary | grep -i "http" — это работает и на Mach-O, как и на PE.
  • Ты хочешь понять, как работает динамическая загрузка (dlopen)? → Открой Ghidra, найди вызовы _dlopen, _dlsym — это означает, что приложение загружает библиотеки на лету. Это часто используется в плагинах или для обфускации.

Как лучше делать — практические рекомендации

  • Всегда начинай с otool -L — это твой первый шаг. Как если бы ты сначала смотрел, какие DLL подгружает PE-файл.
  • Используй Ghidra для первого анализа — он бесплатный, хорошо разбирает Mach-O, и не требует лицензии. IDA Pro — мощнее, но платный. Если ты не делаешь это профессионально — Ghidra хватит.
  • Не пытайся читать __LINKEDIT вручную — он не для людей. Он для dyld. Ты можешь его «увидеть» в Ghidra, но не читай байты — он закодирован.
  • Проверяй, не обфусцирован ли бинарник — если ты видишь только __text и нет имён функций, а в __LINKEDIT много непонятных данных — возможно, приложение защищено (например, через obfuscator или LLVM с оптимизацией). В этом случае используй strings и ищи ключевые слова.
  • Если ты видишь много сегментов с именами вроде __AUTH_ или __RESTRICT — это не ошибка. Это современные механизмы защиты (SIP, Code Signing). Они не мешают анализу, но могут ограничивать модификацию.

Итог: что делать прямо сейчас

Ты получил бинарник на macOS. Что делать?

  1. Найди настоящий файл: /Applications/MyApp.app/Contents/MacOS/MyApp
  2. В терминале: otool -L /path/to/binary — посмотри, какие библиотеки используются
  3. Открой Ghidra → загрузи этот файл → дождись анализа
  4. В окне «Symbol Table» найди импорты (символы с «U») и экспорты (с «T»)
  5. Перейди в сегмент __TEXT → секция __text → ищи функции по именам (например, _main)
  6. Если нужно — используй nm -g и strings для быстрого поиска

Ты не учишь новую систему. Ты просто переводишь привычные понятия с Windows на macOS. Импорты — это не IAT, а символы в __stubs. Секции — это не отдельные блоки, а части сегментов. Загрузка — это не LoadLibrary, а dyld. Но логика та же: ты ищешь, откуда берутся функции, куда они ведут, и что делает код.

Сделай это один раз — и ты будешь чувствовать себя как дома. Mach-O — не враг. Это просто другой язык. А ты уже знаешь, как читать по-английски. Теперь научишься читать по-маковски.

Информация в этой статье предназначена для образовательных и исследовательских целей. Анализ программного обеспечения без разрешения владельца может нарушать законы и условия использования. Всегда действуй в рамках законодательства и этических норм.

Оцените статью
PEFile — Безопасность и технологии простым языком