Обучение волшебству: исследуем потайные места и темную магию macOS

Обучение волшебству: исследуем потайные места и темную магию macOS, image #1

Отправляемся на платформу 9¾, или Как настроить окружение

Прежде всего нам понадобится тестовое окружение для исследования ОС и анализа ВПО. Так сложилось, что, когда я начинал свой путь в роли исследователя безопасности приложений macOS, у меня был только компьютер с Windows и x86 процессором. К счастью, есть проверенное решение — средство виртуализации VMware Workstation.

Запускаем, начинаем создавать виртуалку и… сталкиваемся с тем, что в списке гостевых ОС нельзя выбрать macOS. Чтобы такая возможность появилась, можно воспользоваться сторонней утилитой Unlocker, которая легко находится в поиске. «Зря я это сказал…» :)

Рисунок 1. Создание виртуальной машины в VMware Workstation
Рисунок 1. Создание виртуальной машины в VMware Workstation

Теперь нужно где-то раздобыть установочный образ ОС. Можно, конечно, просто скачать один из множества представленных в интернете ISO-образ, но никогда не знаешь, кто в них покопался. Лучше возьмем оригинальный образ с серверов Apple.

Для этого существуют различные скрипты, к примеру, можно воспользоваться вот этим репозиторием. Забираем последний релиз, переходим в каталог \Utilities\macrecovery\, запускаем Python-скрипт macrecovery.py и следуем инструкциям. Пример запуска:

$ python3 macrecovery.py -b Mac-937A206F2EE63C01 -m 00000000000000000 download

После скачивания файла в системе появится каталог \com.apple.recovery.boot\ с образом BaseSystem.dmg. Его нужно сконвертировать в VMDK для VMware Workstation. В этом нам поможет утилита qemu-img и следующая команда:

$ qemu-img convert -O vmdk -o compat6 BaseSystem.dmg recovery.vmdk

Полученный после конвертации файл recovery.vmdk подключаем в качестве жесткого диска к виртуальной машине с macOS и завершаем установку.

Рисунок 2. Приветственное меню инсталлятора macOS
Рисунок 2. Приветственное меню инсталлятора macOS
Важная ремарка: в 2020 г. Apple перешла на собственные ARM-процессоры, соответственно, все свежие версии ОС разрабатываются под эту архитектуру. Эпоха Intel и x86 закончилась на macOS Catalina (10.15): начиная с macOS Big Sur (11) новые версии выходят только под ARM.
Это нужно иметь в виду, потому что виртуализация новых версий macOS на x86-процессоре будет работать медленно. Можно попробовать установить вторую виртуальную машину с macOS Catalina, но здесь поджидает другая проблема: не все современное ПО, в том числе вредоносное, имеет обратную совместимость, поэтому может попросту не запуститься.

Теперь переходим к компьютерам Apple. «Я почему раньше злой был? Потому что у меня макбука не было…» И правда, в этом случае окружение настраивается гораздо проще. Можно воспользоваться платным решением Parallels или же бесплатным UTM (на базе QEMU). Прелесть в том, что в них прямо из коробки есть функциональность для скачивания образов ОС в нужном формате.

Рисунок 3. Интерфейс UTM и Parallels
Рисунок 3. Интерфейс UTM и Parallels

Следующий шаг — необязательный, при необходимости к нему можно вернуться позже. Но раз уж заговорили о настройке окружения, сразу расскажу и о том, как получить больше контроля в гостевой среде, а именно об отключении SIP.

SIP (System Integrity Protection) — это один из встроенных в macOS механизмов безопасности, который контролирует доступ к критически важным компонентам системы и разрешает только чтение (в том числе для root-пользователя). В частности, речь идет о каталогах и подкаталогах /System, /usr, /bin, /sbin, var, а также предустановленных по умолчанию приложениях. Кроме того, SIP:

  • контролирует доступ к подсистеме Endpoint Security;
  • запрещает отладку и трассировку исполняемых системных файлов;
  • проверяет подпись расширений ядра;
  • контролирует целостность системного раздела (SSV).

Чтобы узнать, какие файлы защищает SIP, вводим в консоль следующую команду:

Обучение волшебству: исследуем потайные места и темную магию macOS, image #5

Для отключения SIP нужно загрузиться в режиме Recovery, открыть консоль и ввести команду csrutil disable, а затем перезагрузить ОС в обычном режиме.

Магия вне архитектуры процессора: как запускаются Intel-сборки ПО на Apple Silicon

В октябре 2020 г. появились компьютеры с процессорами Apple Silicon и архитектурой ARM. При переходе на новую архитектуру возникла проблема с совместимостью приложений: все, что было создано под Intel, перестало работать. Для решения этой задачи Apple предлагает использовать технологию Rosetta 2, которая позволяет транслировать код из x86 в arm. Возникает вопрос: что тогда такое Rosetta 1? Дело в том, что Apple уже меняла архитектуру процеcсора в 2006 году: Rosetta 1 транслировала код из PowerPC в x86.

Как все это работает? Основные сервисы расположены в каталоге /usr/libexec/rosetta.

Рисунок 4. Исполняемые файлы для трансляции кода из x86_64 в arm64
Рисунок 4. Исполняемые файлы для трансляции кода из x86_64 в arm64

При запуске программы, написанной под x86, в дело вступает oahd-демон. Он проверяет, не производилась ли трансляция кода ранее. Для этого демон сканирует каталог /var/db/oah/*/*/ на наличие файла с расширением .aot, в котором должны содержаться результаты трансляции кода. Если такого файла нет, начнется процесс трансляции и его создания. К слову, .aot — это аббревиатура Ahead-Of-Time, которая означает, что трансляция выполняется до фактического запуска потока выполнения.

Рисунок 5. Результат трансляции кода
Рисунок 5. Результат трансляции кода

Папки и файлы в каталоге /var/db/oah защищены SIP, поэтому без его отключения нельзя получить доступ к содержимому.

После того как файл .aot был найден или создан, он попадает в виртуальную память исходного процесса. Далее управление передается в эту секцию, и начинается выполнение кода программы.

Фантастические твари и где они обитают: форматы файлов и базовые инструменты

Предположим, у нас есть некий файл без расширения с неочевидным именем. Нужно его проанализировать и выдать вердикт: вредонос это или нет.

Для начала необходимо определить тип файла — от этого будет зависеть дальнейший выбор инструментов. Задачу можно решить с помощью встроенной утилиты командной строки file (к слову, этот способ подходит и для Linux):

$ file VT_applet/0107f1667048a08ca5fd79e7139f7df5ca9cfbc6085208582011421a1c91156aVT_applet/0107f1667048a08ca5fd79e7139f7df5ca9cfbc6085208582011421a1c91156a: Mach-O 64-bit executable x86_64

Итак, в macOS есть несколько основных типов файлов:

  • образы дисков Apple Disk Images (.dmg);
  • установочные пакеты (.pkg);
  • различные скрипты: python (.py), shell (.sh), AppleScript (.scpt);
  • application bundle, или пакет приложения (.app);
  • исполняемый файл Mach-O.

Остановимся на каждом из них подробнее.

Образы дисков Apple Disk Images (.dmg)

Начнем с простого — образа macOS, который мы скачали ранее. Вводим в file следующую команду:

$ file /Utilities/macrecovery/com.apple.recovery.boot/BaseSystem.dmg

И получаем ответ:

Utilities/macrecovery/com.apple.recovery.boot/BaseSystem.dmg: zlib compressed data

Как видно из вывода утилиты, перед нами просто сжатые данные. На Windows содержимое образа можно посмотреть с помощью 7-zip (см. рис. 6).

Рисунок 6. Просмотр содержимого образа диска с помощью 7 zip
Рисунок 6. Просмотр содержимого образа диска с помощью 7 zip

В macOS есть встроенная утилита hdiutil, которая позволяет смонтировать образ в текущую файловую систему:

$ hdiutil attach /Utilities/macrecovery/com.apple.recovery.boot/BaseSystem.dmg

В конце вывода команды будет указан путь (/Volumes/macOS Base System), по которому можно найти смонтированный образ. Кстати, каталог /Volumes/ скрыт: чтобы отобразить его в Finder, нужно нажать Command + Shift +.

Рисунок 7. Просмотр содержимого смонтированного образа с помощью hdiutil
Рисунок 7. Просмотр содержимого смонтированного образа с помощью hdiutil

Установочные пакеты (.pkg)

Все установочные пакеты в macOS имеют расширение .pkg — в противном случае ОС не интерпретирует файл как установочный. В качестве примера рассмотрим fake.pkg.

$ file fake.pkg                                                                     fake.pkg: xar archive compressed TOC: 760, SHA-1 checksum, contains  zlib compressed data

Файл имеет формат XAR (Extensible Archive Format) и обладает следующей структурой:

  • header,
  • TOC (table of content),
  • heap.

Структура заголовка header представлена ниже:

Обучение волшебству: исследуем потайные места и темную магию macOS, image #10

На рис. 8 представлен пример заголовка и TOC, сжатого с помощью zlib.

Рисунок 8. Заголовок установочного пакета .pkg
Рисунок 8. Заголовок установочного пакета .pkg

TOC (оглавление) — это XML-документ, который содержит информацию о файлах в архиве. Формат XAR позволяет независимо сжимать и кодировать отдельные файлы. При этом можно использовать разные варианты сжатия, например GZIP, BZIP2. Содержимое TOC выглядит следующим образом:

Обучение волшебству: исследуем потайные места и темную магию macOS, image #12

Наконец, Heap — это неструктурированный тип данных, в котором хранятся файлы и на который ссылается TOC.

Подсвечу интересные ноды в TOC:

  • xar.toc.file.name == Scripts. Сжатый поток байтов, содержащий скрипты preinstall и postinstall в формате CPIO (Copy Input/Output).
  • xar.toc.file.name == Distribution. Сжатый поток байтов, содержащий файл в формате XML. Может содержать скрипт для проверки окружения.
  • xar.toc.file.name == Plugins. Вспомогательное ПО, которое вызывается в процессе установки.
  • xar.toc.file.name == Payload. Сжатый поток байтов, содержащий основную нагрузку для установки в ОС.

На рис. 9 представлен пример легитимного скрипта в файле Distribution. Скрипт проверяет требования ОС для установки ПО.

Рисунок 9. Легитимный скрипт в файле Distribution
Рисунок 9. Легитимный скрипт в файле Distribution

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

Рисунок 10. Подозрительный скрипт в файле distribution
Рисунок 10. Подозрительный скрипт в файле distribution

Аналогично служебные скрипты preinstall и postinstall отвечают за подготовку системы перед установкой и очистку временных файлов после нее, но часто применяются злоумышленниками для выполнения вредоносного кода.

Чтобы извлечь содержимое установочного пакета для дальнейшего анализа, можно воспользоваться встроенной утилитой командной строки pkgutil:

$ pkgutil --expand fake.pkg out

Кроме того, можно просмотреть содержимое установочного пакета с помощью Suspicious Package (отдельного ПО с GUI).

Рисунок 11. Просмотр содержимого установочного пакета в Suspicious Package
Рисунок 11. Просмотр содержимого установочного пакета в Suspicious Package
Рисунок 12. Просмотр служебных скриптов preinstall и postinstall в Suspicious Package
Рисунок 12. Просмотр служебных скриптов preinstall и postinstall в Suspicious Package

Скрипты: python (*.py), shell (*.sh), AppleScript (*.scpt)

Сегодня остановимся на AppleScript. Это разработанный Apple и встроенный в macOS язык сценариев, который используется на компьютерах корпорации с 1991 г. Одна из ключевых особенностей AppleScript заключается в том, что написанные на нем скрипты читаются как обычный английский текст (также они могут называться OSAScript — Open Scripting Architecture).

Скрипты позволяют управлять приложениями с поддержкой сценариев, а также компонентами самой macOS. Важное условие: приложения должны поддерживать межпрограммное взаимодействие, т. е. воспринимать и реагировать на AppleEvent-сообщения. Они отправляются, когда команда скрипта взаимодействует с приложением: например, когда сценарий tell application 'Finder' get disks запрашивает у файлового менеджера Finder имена примонтированных дисков.

Каждое приложение, понимающее AppleScript, публикует поддерживаемые команды в специальном словаре. Он представляет собой XML-файл с расширением .sdef (scripting difinition) и хранится в отдельной папке среди ресурсов пакета.

Каждая часть информации в AppleEvent связана с четырехзначным кодом, иногда даже с двумя. При этом файлы с определениями кодов разбросаны по всей системе. Приведу несколько примеров:

Обучение волшебству: исследуем потайные места и темную магию macOS, image #17

Сценарии можно компилировать и сохранять в файл для последующего запуска. Именно со скомпилированными скриптами и сталкивается вирусный аналитик. Однако стоит отметить, что такие скрипты можно «завернуть» еще и в Application Bundle — тогда они будут выглядеть как обычное приложение (но об этом чуть позже).

Скомпилированные скрипты имеют расширение *.scpt и две магических константы: строку FasdUAS в начале файла и байты \xFA\xDE\xDE\xAD в самом конце. Помимо этого, в них есть строка с версией скрипта (длиной 8 байт).

При сохранении сценария по умолчанию конечный файл будет содержать две формы его представления:

  1. Сериализованный формат — с сохранением связей и вложенности AppleEvent-кодов.
  2. Скомпилированный байт-код и список AppleEvent-кодов без сохранения взаимосвязей между ними (связь задается в байт-коде).

Такие файлы можно снова открыть в редакторе сценариев: в результате произойдет декомпиляция на основе п. 1 и вы увидите исходный текст сценария на английском языке. В то же время при сохранении в файл можно выставить флаг RunOnly: в этом случае итоговый файл будет содержать только часть п. 2.

Рассмотрим скрипт, который создает на рабочем столе папку с названием JackDaniels:

tell application "Finder"   
  make new folder at desktop with properties {name:"JackDaniels"}
end tell

В терминах AppleEvent-кодов сценарий будет выглядеть следующим образом:

Обучение волшебству: исследуем потайные места и темную магию macOS, image #18

Если вы откроете файл в hex-редакторе, то увидите все эти core, crel, desk и JackDaniels (см. приложение).

Какой бы вариант сохранения сценария в файл вы ни выбрали, выполнение сценария будет происходить на основе байт-кода виртуальной машины, которая реализована в модуле /System/Library/PrivateFrameworks/AppleScript.framework/Versions/A/AppleScript в функции UASExecute1.

Рисунок 13. Обработчик виртуальной машины AppleScript
Рисунок 13. Обработчик виртуальной машины AppleScript
Рисунок 14. Декодирование байт-кода виртуальной машины AppleScript в команду
Рисунок 14. Декодирование байт-кода виртуальной машины AppleScript в команду
Рисунок 15. Набор команд виртуальной машины AppleScript
Рисунок 15. Набор команд виртуальной машины AppleScript

Для анализа подобных файлов можно использовать два инструмента:

  1. Jinmo/applescript-disassembler
  2. SentineLabs/aevt_decompile

В первом реализована часть функций из /System/Library/PrivateFrameworks/AppleScript.framework/Versions/A/AppleScript (а именно считывание скомпилированных файлов, парсинг и дизассемблирование байт-кода виртуальной машины). Во втором есть человекочитаемое описание четырехсимвольных кодов из некоторых словарей в системе (запускается только на macOS, потому что инструмент сначала проводит поиск словарей в системе).

Application bundle, или пакет приложения (.app)

Это каталог со стандартизированной структурой, который содержит исполняемый код и ресурсы, используемые этим кодом. Внутри application bundle есть подраздел Contents, в котором как минимум должны находиться следующие файлы:

  • MacOS/<Executable>: исполняемый файл или скрипт.
  • Info.plist: файл-манифест для приложения. В нем задается имя исполняемого файла, который запускается при старте приложения, а также различные требования (например, минимальная версия ОС или запрос на права доступа к микрофону и камере).

В качестве примера рассмотрим содержимое application bundle для Telegram (см. рис. 16).

Рисунок 16. Содержимое application bundle для Telegram
Рисунок 16. Содержимое application bundle для Telegram

Фрагмент Info.plist представлен ниже:

Обучение волшебству: исследуем потайные места и темную магию macOS, image #23

Application Bundle также может содержать необязательные файлы. Приведу несколько примеров:

  • Resources/. Вспомогательные файлы: например, иконка, шрифты, локализации строк.
  • Plugins/. Небольшие исполняемые файлы, расширяющие функционал основного.
  • Frameworks/. Плагины и высокоуровневые фреймворки, необходимые приложению (модули зависимостей).
  • _CodeSignature/CodeResources. XML-файл, содержащий сигнатуры для файлов в application bundle.

Вернемся к поднятому выше вопросу «завертывания» скомпилированных файлов в application bundle. Для этого в ОС хранится заготовка скомпилированного исполняемого файла:

/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/OpenScripting.framework/Versions/A/Resources/AppletStub.

При «заворачивании» AppletStub переименовывается в applet. Сама заготовка содержит вызовы нескольких API-функций, которые подгружает и запускает скомпилированный скрипт:

OpenDefaultComponent('aplt', 'scpt')CallComponentDispatch(…);

Cкомпилированный скрипт должен располагаться в application bundle в папке Scripts и называться main.scpt.

Рисунок 17. Содержимое application bundle для скомпилированных AppleScript
Рисунок 17. Содержимое application bundle для скомпилированных AppleScript

Mach-O

В macOS есть свой формат исполняемых файлов — Mach-O. Такие файлы состоят из трех частей: заголовка (mach_header), команд загрузки (load_commands) и набора сегментов (segments).

  • Заголовок содержит магические байты, информацию об архитектуре процессора, для которого скомпилирован файл, типе файла (динамическая библиотека, исполняемый и т. д.) и другие метаданные.
  • Команды загрузки содержат информацию, которую динамический загрузчик использует для отображения двоичного файла в памяти. Здесь указаны адрес точки входа (LC_MAIN) для исполняемого файла, динамические зависимости (LC_LOAD_DYLIB) и др.
  • За командами загрузки следует оставшаяся часть двоичного файла Mach O: в основном она состоит из байт-кода, переменных и т. д.

Первичную информацию об исполняемом файле можно получить прямо в консоли с помощью встроенных утилит:

  • $ otool -L <input> — покажет, какие библиотеки находятся в зависимостях у исполняемого файла.
  • $ otool -oV <input> — позволяет узнать, какие методы есть у исполняемого файла.
  • $ otool -tV <input> — позволяет дизассемблировать код.

Исполняемый файл Mach-O содержит код только для одной архитектуры процессора. Поэтому приложения часто распространяются в виде бинарных файлов Universal Binary или Fat Binary, которые содержат несколько Mach-O для разных архитектур. В этом случае бинарный файл начинается с fat_header, а его тело содержит несколько склеенных Mach-O. Загрузчик Mach-O выбирает двоичный файл с подходящей архитектурой и загружает его в память для запуска программы.

Отмечу, что файл Fat Binary можно усечь и оставить только нужную архитектуру:

$ lipo <input> -thin x86_64 -output <output>

Важной частью исполняемого файла является подпись. Она позволяет установить авторство и убедиться в целостности файла, но не гарантирует, что это не вредонос. Чтобы посмотреть подпись, нужно ввести следующую команду (чем больше символов v, тем подробнее будет вывод):

Обучение волшебству: исследуем потайные места и темную магию macOS, image #25

Вместе с подписью хранятся Entitlements приложения — пары «ключ:значение», которые дают исполняемым файлам разрешения использовать тот или иной сервис либо технологию. Например, com.apple.security.get-task-allow разрешает подключаться к процессу отладчиком. Тем не менее приложение может запрашивать и другие права, не указанные в Entitlements.

Чтобы посмотреть набор прав, используйте команду: $ codesign -d --entitlements - <input>

Внимание к деталям: разбираемся с соглашениями о вызовах x86_64 и arm64

Говоря о реверсе, нельзя не упомянуть соглашения о вызовах. Они определяют, каким образом аргументы передаются в функцию, как возвращается результат и какая функция отвечает за очистку стека. Эти правила сформулированы в двоичном интерфейсе приложения — Application Binary Interface (ABI).

Аргумент

Регистр

1-й аргумент

rdi

2-ой аргумент

rsi

3-ий аргумент

rdx

4-ый аргумент

rcx

5-ый аргумент

r8

6-ой аргумент

r9

7-ой аргумент

стек

Возвращаемое значение

rax

Таблица 1. ABI для x86_64 в macOS

Аргумент

Регистр

C 1-го по 8-ой аргумент

x0 – x7

9-ый аргумент и далее

стек

Возвращаемое значение

x0

Таблица 2. ABI для arm64 в macOS

Скажите, почему когда что-то происходит, вы трое всегда оказываетесь рядом?

При обратной разработке бинарного файла чаще всего встречаются семплы, скомпилированные с помощью C/C++, Objective-C и Swift. Конечно, бывают и образцы на Go и Rust, но в их анализе нет ничего особенного (впрочем, как и в случае с C/C++): все как на Windows и Linux. А вот при встрече с сеэмплами, скомпилированным на Objective-C и Swift, сразу возникают непривычные языковые конструкции. Поговорим о них подробнее.

Objective-C

Этот язык часто используется для разработки приложений для macOS и iOS. Рассмотрим пример объявления и инициализации числа:

NSNumber *number = [[NSNumber alloc] initWithInt:1];

Если мы скомпилируем, а затем декомпилируем код, то получим следующий результат:

int v1; int v2;
v1 = objc_msgSend(_OBJC_CLASS_$_NSNUMBER, "alloc");
v2 = objc_msgSend(v1, "initWithInt", 1);

Этот пример демонстрирует основы выделения объектов и вызова методов. Каждый вызов метода выполняется путем вызова функции среды выполнения objc_msgSend:

id objc_msgSend(id self, SEL op, …);

Обязательные аргументы self и op — это указатели на объект класса и так называемый селектор; id — ключевое слово, обозначающее указатель на объект. В свою очередь, селектор представляет понятное для человека название метода: в рамках нашего примера это alloc и initWithInt.

Основная цель функции objc_msgSend — найти реализацию метода для данного объекта и селектора, а затем вызвать его и передать все указанные аргументы. Описанный подход привносит специфический нюанс в процесс реверса: человекочитаемый селектор не может быть обфусцирован для системного API, что помогает быстрее понять исполняемый код.

Названия селекторов содержатся в разделе __objc_methname сегмента __TEXT.

Swift

Язык Swift тоже активно применяется для разработки приложений для macOS и iOS. У него есть три интересные особенности…

Первая: помимо вызова привычных функций вида sub_* при реверсе можно встретить что-то невнятное, а-ля $s7SwiftUI3AppPAAE4mainyyFZ. Это вызов Swift-функции, которая представлена в виде «замангленного» имени (Swift mangled name).

В подобных именах закодирован класс, в котором содержится функция, ее имя, а также типы принимаемых аргументов и возвращаемого значения. Отмечу, что в пакете разработчика Swift есть специальная утилита для декодирования «замангленных» имен: swift-demangle.

Начиная с версии 7.7 (ниже не проверял) IDA умеет раскодировать такие имена прямо из коробки — функция включена по умолчанию. В качестве примера рассмотрим вывод декодированного имени Button.init (см. рис. 18).

Рисунок 18. Декодирование имени функции
Рисунок 18. Декодирование имени функции

Итак, функция принимает два аргумента, но что они означают? Мы помним, что в закодированном имени в том числе содержатся типы принимаемых аргументов. Пора испытать swift-demangle, причем в каждом из трех возможных режимов (см. рис. 19).

Рисунок 19. Декодирование имени функции утилитой swift-demangle
Рисунок 19. Декодирование имени функции утилитой swift-demangle

Выводы в первых двух режимах практически одинаковы: вероятнее всего, в IDA используется один из них. Из вывода в третьем режиме становится ясно, что функция Button.init первым аргументом принимает LocalizatedString с текстом кнопки, а вторым — action() — функцию, которая обрабатывает нажатие на кнопку. Резюмируем: IDA многое умеет из коробки, но за подробностями лучше обращаться в swift-demangle.

Теперь переходим ко второй особенности Swift — это реализация строк, которые здесь представлены в виде структур:

struct _StringObject
{  
  var _countAndFlagsBits;
  var _object;
};

В зависимости от значений флагов в _countAndFlagsBits (60–63-й биты) в _objects может храниться либо непосредственное значение (для коротких строк), либо адрес.

Рисунок 20. Пример короткой Swift-строки
Рисунок 20. Пример короткой Swift-строки

Если посмотреть на биты с номерами 60–63 в 0xE600000000000000 и интерпретировать их согласно документации, то станет ясно, что перед нами короткая строка, а 'lecnaC' — не что иное, как непосредственное значение "Cancel".

Рассмотрим другой пример (см. рис. 21). Что такое 0xD0000000000000002A и 0x80000000010000AD80? Из документации узнаем, что это длинная строка, а младшие значащие биты первого числа 0x2A — это ее размер. Чтобы получить адрес строки, нужно к 0x10000AD80 прибавить 0x20 (nativeBias тоже из документации). В итоге получаем адрес 0x10000ADA0.

Рисунок 21. Пример длинной Swift-строки
Рисунок 21. Пример длинной Swift-строки
Рисунок 22. Вычисленный адрес строки и ее значение
Рисунок 22. Вычисленный адрес строки и ее значение

Наконец, третья особенность: в коде Swift можно вызывать методы из Objective-C. Для этого нужно сконвертировать данные из одного представления в другое. Например, Swift-строку из struct _StringObject в объект класса NSString в Objective-C. Для этого используются методы с названием class.bridgeToObjectiveC() и class.bridgeFromObjectiveC(). В контексте реверса достаточно понимать, что это обычная конвертация данных.

Рисунок 23. Пример конвертации строки и вызова Objective-С-метода
Рисунок 23. Пример конвертации строки и вызова Objective-С-метода

***

Сегодня мы разобрались, как настроить тестовую среду для исследования вредоносных программ, как использовать статический анализ для реверса двоичных файлов, а также рассмотрели несколько распространенных форматов файлов и инструменты их анализа. Продолжение — в будущих статьях ;)

Если это ваш первый опыт реверс-инжиниринга ВПО для macOS, также рекомендую две полезные ссылки:

Objective-See’s Blog

SentielOne

Приложение

Hexdump-представление скомпилированного osascript

Обучение волшебству: исследуем потайные места и темную магию macOS, image #32
Обучение волшебству: исследуем потайные места и темную магию macOS, image #33
Обучение волшебству: исследуем потайные места и темную магию macOS, image #34

Источник

74 views·14 shares