Безопасность IOS-приложений (часть 8) – реализация техники method swizzling при помощи Cycript

Безопасность IOS-приложений (часть 8) – реализация техники method swizzling при помощи Cycript

В предыдущей статье мы рассмотрели установку собственных приложений на устройство без использования сертификата разработчика. В этой статье мы рассмотрим реализацию method swizzling при помощи Cycript.

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

В предыдущей статье мы рассмотрели установку собственных приложений на устройство без использования сертификата разработчика. В этой статье мы рассмотрим реализацию method swizzling при помощи Cycript.

Для начала необходимо скачать тестовый проект для Xcode. Вы можете загрузить либо исходные тексты, либо бинарный файл. Если вы решили работать с исходными текстами, вам необходимо скомпилировать проект и привязать к нему самоподписанный сертификат. В предыдущей статье подробно рассказано о том, как это сделать. Если вы будете работать сразу с бинарным файлом, то можете сразу запустить его на джейлбрейковом устройстве, однако я все равно рекомендую вам ознакомиться с исходными текстами и загрузить проект для Xcode.

Повторюсь еще раз, не забудьте подписать ваш проект, используя самоподписанный сертификат.

Рисунок 1: Привязка сертификата к проекту в Xcode

После запуска приложения, подключитесь к устройству через ssh и подцепитесь к запущенному процессу при помощи Cycript.

Вы можете подцепиться к любому запущенному процессу, используя команду cycript -p [APP_ID].

Рисунок 2: Выяснение идентификатора запущенного процесса, используя который мы подцепляемся к самому процессу

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

Рисунок 3: Форма авторизации тестового приложения

При вводе имени пользователя admin и пароля password мы попадаем в админку.

Рисунок 4: Страница администратора тестового приложения

Если ввести некорректную комбинацию имени пользователя и пароля, выведется сообщение об ошибке.

Рисунок 5: Сообщение об ошибке, которое выводится при вводе некорректной комбинации имени пользователя и пароля

Таким образом, наша задача – обойти авторизацию.

Для начала отыщем корневой контроллер представления, используя соответствующую команду UIApp.keyWindow.rootViewController в Cycript.

Рисунок 6: Результаты выполнения команды по поиску корневого контроллера представления

Поскольку форма авторизации – первое представление, которое мы видим при запуске приложения, мы можем убедиться в том, что класс контроллера представления, отвечающий за отображения формы авторизации, является частью корневого контроллера, найденного нами ранее. Текущее представление можно найти внутри свойства visibleViewController, которое принадлежит корневому контроллеру.

Рисунок 7: Нахождение текущего представления

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

Я буду использовать следующую функцию.

function printMethods(className) {
var count = new new Type("I");
var methods = class_copyMethodList(objc_getClass(className), count);
var methodsArray = [];
for(var i = 0; i < *count; i++) {
var method = methods[i];
methodsArray.push({selector:method_getName(method), implementation:method_getImplementation(method)});
}
free(methods);
free(count);
return methodsArray;
}

Рисунок 8: Функция для вывода на экран всех методов определенного класса

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

Рисунок 9: Вывод на экран всех методов текущего контроллера представления

Еще один способ узнать обо всех методах – использовать свойство isa.messages. Согласно документации Apple, isa представляет собой указатель на структуру класса.

Ниже приводится выдержка из документации.

При создании объекта выделяется память, и инициализируются все экземпляры переменных этого объекта. Первая переменная объекта – указатель на структуру класса. Этот указатель называется isa, который позволяет получить доступ к классу и ко всем классам, находящимся выше по иерархии.

Для лучшего понимания материала ниже приводится картинка из документации Apple.

Рисунок 10: Картинка, проясняющая к каким классам можно получить доступ через указатель isa

А что же представляет собой свойство messages? Прежде, чем прояснить смысл этого свойства, необходимо выяснить, что такое таблица диспетчеризации (dispatch table). Таблица диспетчеризации содержит записи, связывающие селекторы методов и классовые адреса методов, с которыми связаны селекторы. Ниже приводится выдержка из документации Apple.

Когда сообщение посылается объекту, функция обработки сообщения (messaging function) через указатель isa и структуру класса пытается отыскать селектор метода в таблице диспетчеризации. Если нужный селектор не найден, функция objc_msgSend через указатель и суперкласс (класс, находящийся выше по иерархии) пытается найти селектор в таблице диспетчеризации суперкласса. В случае последующих неудач поиск продолжается до тех пор, пока функция не доберется до класса NSObject. Как только селектор найден, функция вызывает метод, найденный в таблице, и передается в него полученную структуру данных объекта.

Такой способ используется для выбора реализации метода во время выполнения приложения. Если говорить в терминах объектно-ориентированного программирования, то методы динамически связаны с сообщениями.

Теперь нетрудно догадаться, что свойство messages представляет собой полный список сообщений, которые могут быть посланы экземпляру класса или самому классу. Это очень большой список, поскольку указатель isa выбирает все сообщения, в том числе и из суперклассов, пока не дойдет очередь до класса NSObject. Обратите внимание на последнюю строчку из вышеприведенной документации: «Такой способ используется для выбора реализации метода во время выполнения приложения. Если говорить в терминах объектно-ориентированного программирования, то методы динамически связаны с сообщениями».

Поскольку методы динамически привязаны к сообщениям во время выполнения приложения, мы можем изменить реализацию метода для конкретного сообщения.

Выведем на экран сообщения класса App Delegate.

Рисунок 11: Команда для вывода все сообщений класса App Delegate

Можно использовать более простую команду.

Рисунок 12: Альтернативный способ вывода на экран всех сообщений класса App Delegate

Ранее мы нашли контроллер ViewController, который отвечает за отображение формы авторизации. Выведем на экран все сообщения этого контроллера.

Рисунок 13: Список сообщений контроллера представления ViewController

В самом верху списка вы можете видеть некоторые методы ViewController.

Рисунок 14: Некоторые методы контроллера представления ViewController

Метод validateLogin представляет для нас определенный интерес. Рассмотрим его поподробнее, используя выходные данные утилиты class-dump-z. Если вы не знакомы с class-dump-z, обратитесь ко второй статье этой серии.

Ниже приводится информация для ViewController, выданная утилитой class-dump-z.

Рисунок 15: Подробная информация о классе контроллера представления ViewController, полученная при помощи утилиты class-dump-z

Как видно из рисунка выше, метод validateLogin возвращает значение типа BOOL (булево), и можно предположить, что метод проверяет введенные имя пользователя и пароль и по результатам проверки возвращает YES или NO. При помощи Cycript мы можем изменить реализацию метода для конкретного сообщения. Сделаем так, чтобы метод validateLogin всегда возвращал TRUE.

Рисунок 16: Изменение реализации метода validateLogin

Правая часть выражения на Рисунке 16 содержит javascript-функцию, которая всегда возвращает TRUE. Теперь кликнем на кнопку Login Method 1 на форме авторизации.

Рисунок 17: После изменения логики работы метода, мы сразу же попадаем в админку

Как вы можете видеть, наш трюк сработал, и при нажатии на кнопку Login Method 1 мы сразу же попали в админку. Мы использовали method swizzling для обхода проверки имени пользователя и пароля.

Еще один способ обхода авторизации

Теперь, когда мы убедились в том, что method swizzling работает, рассмотрим другой способ обхода авторизации. Из информации, выданной утилитой class-dump-z, мы можем выяснить, что после того, как метод validateLogin возвратил TRUE, вызывается метод pushLoginPage. В других случаях подобные методы могут иметь схожие имена: pushUserPage, pushLoginSuccessfulPage и т. п. Теперь нам нет необходимости менять реализацию метода validateLogin. Мы можем всегда вызывать метод pushLoginPage.

Рисунок 18: Прописываем вызов метода pushLoginPage

Поскольку метод принадлежит экземпляру класса, сначала мы получаем экземпляр ViewController при помощи команды UIApp.keyWindow.rootViewController.visibleViewController, а затем вызываем метод. Обратите внимание, что в некоторых случаях приложение может работать некорректно (или вовсе не работать), поскольку свойства контроллера представления могут зависеть от информации, вводимой в форме авторизации. Если хотите, можете попробовать обойти проверку авторизации при нажатии на кнопку Login Method 2. Мы поговорим об этом в следующих статьях.

Заключение

В этой статье мы научились некоторым техникам для реализации Method Swizzling, используя Cycript. Далее мы рассмотрим другие техники и утилиты, позволяющие добиться этой цели. Мы также будем использовать gdb для динамического анализа и otool для получения дополнительной информации о приложении.  

Тени в интернете всегда следят за вами

Станьте невидимкой – подключайтесь к нашему каналу.