Безопасность IOS-приложений (часть 3) – внутреннее устройство Objective-C Runtime

Безопасность IOS-приложений (часть 3) – внутреннее устройство Objective-C Runtime

В этой статье основное внимание будет уделено описанию принципов работы Objective-C Runtime и деталям внутреннего устройства этого языка, что в будущем поможет проводить более глубокий и качественный анализ безопасности IOS-приложений.

Автор: Пратик Джианчандани (Prateek Gianchandani)

Введение

Почти все «родные» IOS-приложения написаны на Objective-C. Все эти приложения используются библиотеку Cocoa, содержащую набор высокоуровневых API-функций, которые заметно упрощают разработку приложений под Mac и IOS. Также в Cocoa есть среда выполнения (runtime environment) для приложений. В этой статье основное внимание будет уделено описанию принципов работы Objective-C Runtime и деталям внутреннего устройства этого языка, что в будущем поможет проводить более глубокий и качественный анализ безопасности IOS-приложений.

Objective-C Runtime

Objective-C представляет собой динамически ориентированный язык (runtime-oriented language). При этом возникает закономерный вопрос, что же такое динамический язык (runtime language)? Динамический язык – это такой язык, когда все решения (в том числе и при вызове функций) принимаются во время выполнения приложений. Является ли Objective-C динамическим языком? Ответ: нет. Objective-C – динамически ориентированный язык, а это означает, что принятие решения во время выполнения приложения происходит только там, где это возможно. Как говорилось ранее, библиотека Cocoa предоставляет среду выполнения, которая необходима IOS-приложениям. На рисунке ниже приводится параграф из документации Apple, проясняющий многие вещи.

Рисунок 1: Выдержка из документации Apple для языка Objective-C

Перевод текста с Рисунка 1: Приложения, написанные на языке Objective-C, принимают решения во время выполнения везде, где это возможно (то есть происходит динамическое исполнение кода). Это означает, что для работоспособности приложения требуется не только компилятор, но и среда выполнения, которая предназначена для запуска скомпилированного кода. Для языка Objective-C среда выполнения функционирует как операционная система, которая отвечает за работоспособность приложений, написанных на Objective-C.

Теперь проверим, импортируется ли динамическая библиотека (runtime library) внутри проектов или нет. В идеале импорт должен происходить в каждом IOS-приложении. Чтобы проверить это, подключитесь к устройству и зайдите в директорию с приложениями.

Рисунок 2: Содержимое директории /var/mobile/Applications

Теперь введите «ls *» для вывода списка всех директорий, включая поддиректории.

Рисунок 3: Содержимое директории /var/mobile/Applications вместе с поддиректориями каждой директории

Рассмотрим содержимое директории приложения BADLAND (весьма популярная игра для IOS). Заходим внутрь директории BADLAND.app и смотрим бинарный файл Badland при помощи утилиты otool.

Рисунок 4: Отображение содержимого бинарного файла при помощи утилиты otool

Из рисунка выше мы видим, что импортируется достаточно большое количество библиотек. Импорт библиотеки objc-runtime показано на рисунке ниже.

Рисунок 5: Импорт библиотеки objc-runtime

Библиотека objc-runtime делает возможным манипуляцию кодом во время выполнения программы, написанной на Objective-C. По умолчанию, эта библиотека используется во всех IOS-приложениях. К примеру, рассмотрим приложение Google Maps для платформы IOS (также при помощи утилиты otool).

Рисунок 6: Анализ бинарного файла приложения Google Maps при помощи otool

Как видно из Рисунка 6, в приложении Google Maps также происходит импорт библиотеки Objective-C Runtime.

Динамический анализ приложения при помощи GDB

В этом разделе мы рассмотрим техники анализа во время выполнения приложения при помощи GDB. Первым делом необходимо установить корректную версию этого отладчика. Та версия, которая доступна через Cydia, не работает, и вам необходимо загрузить бинарный файл из других источников. После этого при помощи sftp загрузите файл gdb на устройство, как показано на рисунке ниже.

Рисунок 7: Процедура загрузки бинарного файла на устройство при помощи sftp

Далее устанавливаем права, необходимые для запуска отладчика.

Рисунок 8: Установка прав, необходимых для запуска отладчика

Чтобы подцепиться к запущенному процессу, необходимо вначале убедиться, что процесс запущен. Мы будем проводить тестирование на приложении Google Maps. Вначале запустим это приложение на устройстве и узнаем идентификатор процесса. Кроме того, необходимо убедиться в том, что приложение работает на переднем плане (foreground). Как видно из рисунка ниже, идентификатор процесса приложения Google Maps – 661 (в вашем случае идентификатор может быть другим).

Рисунок 9: Выяснение идентификатора процесса приложения Google Maps

Теперь подцепимся к процессу при помощи GDB.

Рисунок 10: Подцепляемся к процессу с идентификатором 661 при помощи GDB

Из Рисунка 10 видно, что мы успешно подцепились к процессу (на сообщения о предупреждениях пока можно не обращать внимание).

Язык Objective-C основывается на обмене сообщениями и всякий раз, когда сообщение отсылается, вызывается метод objc_msgSend(). Для того, чтобы проанализировать приложение во время выполнения, я добавлю точку останова для метода objc_msgSend() (самый простейший вызов) и распечатаю значения $r0 и $r1. Из $r0 мы можем узнать класс, откуда вызывается метод, а из $r1 мы можем выяснить селектор. Обратите внимание, что на вас может свалиться огромное количество информации, поскольку метод objc_msgSend вызывается каждый раз, когда отсылается сообщение. В следующих статьях мы рассмотрим, как использовать эту технику более эффективно. А сейчас, вне зависимости от того, когда сработает точка останова, я распечатаю значения $r0 и $r1, а затем продолжу выполнение приложения. На рисунке ниже показана процедура установления точки останова и команд, которые выполняются во время срабатывания точки останова.

Рисунок 11: Устанавливаем точку останова на метод objc_msgSend (break objc_msgSend) и команды, который выполняются во время срабатывания точки останова (x/a $r0 – печать $r0; x/a $r1 – печать $r1, c – продолжить выполнение приложения)

Рисунок 12: Информация, выдаваемая отладчиком, во время выполнения приложения

Как видно из Рисунка 12, эта техника хорошо помогает при анализе логики работы приложения. Повторюсь еще раз, возможно, конкретно в данном случае столь огромный объем информации может быть бесполезен во время настоящего анализа. Все это продемонстрировано лишь с целью показать вам, как много информации мы можем получить относительно реализации приложения.

Method Swizzling

Мы уже знаем, что все IOS-приложения во время запуска используют среду выполнения. Это означает, что многие решения принимаются во время выполнения приложения. Method Swizzling – еще одно оружие, которое мы можем использовать для модификации поведения IOS-приложения. Эта техника позволяет нам переопределить логику работы метода. Мы рассмотрим более подробно Method Swizzling в следующей статье.

Ссылки

Ваш провайдер знает о вас больше, чем ваша девушка?

Присоединяйтесь и узнайте, как это остановить!