Доброго времени суток!
Сегодня я хотел бы вновь затронуть тему разработки jailbreak-программ под iOS. В русскоязычном интернете довольно проблематично найти что-то понятное новичкам, поэтому я попытаюсь исправить это недоразумение и объяснить как решаются некоторые моменты.
Установка ПО, настройка среды и устройства, написание твика с нуля — именно это ждёт вас под катом. Если вам интересно, как поменять часть iOS под себя — добро пожаловать.
Начнём!
Что нам потребуется?
- Компьютер под управлением Mac OS X с утилитами git, dpkg;
- Xcode с установленными Command Line tools (я использую 5.1.1, однако, подходит 4.0+);
- Theos;
- Пакет iOSOpenDev;
- iУстройство с jailbreak'ом и установленным OpenSSH.
Подготовка
Первым шагом будет установка Xcode. Его вы можете скачать с Mac AppStore, а так же с страницы загрузок на сайте Apple. Подробно на этом останавливаться не буду.
Следом нужно поставить theos. Нам говорят, что его можно запихать на любую платформу, и он будет работать, но сегодня мы не изобретаем велосипед делаем всё на родной для iOS SDK системе.
Скачиваем theos в папку /opt/theos. Можно ставить в любую папку, но на свой страх и риск:
export THEOS=/opt/theos
git clone git://github.com/DHowett/theos.git $THEOS
На этом настройка c theos можно закончить.
Скачиваем iOSOpenDev и запускаем инсталлер. Он сделает всё за вас.
В случае возникновения неизвестной ошибки скорее всего приложение называется не «Xcode.app» или вы его не запускали вовсе.
Халява закончилась. Время настройки переменных.
Открываем ~/.bash_profile и редактируем следующие строчки:
export iOSOpenDevDevice=айпи.адрес.вашего.устройства
Теперь iOSOpenDev будет знать IP вашего устройства, осталось только разрешить подключаться по SSH без запроса пароля и скачать всю необходимую базу библиотек:
iod-setup base
iosod sshkey -h <IP устройства>
Дважды вводим пароль устройства, и, если запросит — придумываем пароль для кейчейна.
Более того, нужно скачать пачку хеадеров и положить в /opt/iOSOpenDev/include.
Тут сложнее всего с IOSurfaceAPI.h, так как он не является свободно распространяемым кодом. Но если вы не сможете его добыть из системы (на Mac OS X 10.10 я его не нашел), то заберите «заглушку» из папки _fallback, для нашего разбора будет достаточно и её.
На этом установку можно считать завершенной.
Пишем основу
Вся разработка будет проходить в Xcodе, хотя с некоторыми ограничениями.
Создаём новый проект и встречаем новый пункт «iOSOpenDev».
Нам требуется Logos Tweak:
Заполняем информацию о проекте. Include Simple PreferenceLoader добавит в проект простой блок настроек в Settings.app. Но о нём позже.
Теперь мы должны сделать то, что Xcode сам не делает — добавить в список для линковшика UIKit.framework и libsubstrate.dylib (последняя лежит в /opt/iOSOpenDev/lib/).
После этого заходим в наш .xm файл, сносим директиву #error и нажимаем на сборку. Первая сборка будет неудачной, а вторая должна быть успешной, это нормально. Еще в xm файле нет подсветки синтаксиса, но это решается закрытием и открытием Xcode после первой сборки.
Расставим все точки над «i»: .xm файл отвечает за код твика, а .mm файл является «промежуточным», он автоматически генерируется logos-препроцессором и потом компилируется.
Первые шаги
Сегодня мы будем менять унылую надпись «Разблокируйте» экрана блокировки на свой текст.
Во-первых, было бы хорошо раздобыть хеадеры бинарника «подопытного». Со SpringBoard все гораздо проще — народ готов выкладывать хеадеры спрингбоарда для каждой версии iOS. Но если вы хотите сделать их сами, то утилита class-dump-z вам в этом поможет.
Я пишу для iPad 4 на iOS 8.1, поэтому и хеадеры смотрим соответсвующие.
Есть два способа быстро найти то, что нам требуется. Первый — использовать cycript и посмотрев на иерархию объектов найти то, что нужно. Второй — искать поиском по содержимому хеадеров. В данном случае я решил поискать по запросу «unlockText» и нашел в классе SBLockScreenView такой вот метод:
- (id)_defaultSlideToUnlockText;
Предположим, что это то, что нам надо. Напишем первый набросок:
#import <UIKit/UIKit.h>
%hook SBLockScreenView
- (id)_defaultSlideToUnlockText
{
return @"Привет!";
}
%end
Для компиляции с установкой на девайс выберите Build for profiling:
И, о, чудо! На удивление всё заработало с первого раза:
Наша основная цель — заставить меняться текст, поэтому создадим конструктор (%ctor) и будем подгружать настройки.
#import <UIKit/UIKit.h>
#define SETTINGS_FILE @"/var/mobile/Library/Preferences/ru.firemoon777.LockLabel8Bundle.plist"
NSDictionary *settings;
%hook SBLockScreenView
- (id)_defaultSlideToUnlockText
{
return [settings objectForKey:@"Text"];
}
%end
static void loadSettings()
{
settings = [[NSDictionary alloc] initWithContentsOfFile:SETTINGS_FILE];
}
%ctor
{
loadSettings();
}
Создаём панель настроек
Создадим новую цель: File — New — Target — iOS Open Dev — PreferenceBundle; Назовём её LockLabel8Bundle.
Есть большой плюс проекта с «сложными» PreferenceBundle'ом — в настройках можно сделать весь графический интерфейс для приложения и не заморачиваться с запуском от рута и подписями. Но есть и минус — панель в настройках и сам твик собираются в отдельных пакетах, поэтому для релиза придётся их ещё и объединить.
Можете попробовать собрать шаблон и полюбоваться на множество возможных встроенных PSSpecifier'ов.
Возможно, оно не скопилится сразу как надо. Значит, вы пропустили скачивание хеадров, про которые я говорил в начале статьи.
Из всех я оставлю только первую и последнюю группу, TextView и одну кнопку.
Еще можно подредактировать поле «label», чтобы не светить этим «Bundle».
У кнопки есть Action «respring:», поэтому опишем метод респрингa в LockLabel8BundleController:
- (void)respring:(PSSpecifier*)specifier
{
system("killall SpringBoard");
}
С разработкой внутри настроек всё гораздо проще: здесь действуют те же законы, что и в обычных приложениях.
Исходный код доступен на гитхабе.
Поддержка нескольких версий iOS?
Когда твик становится из простого наброска масштабным проектом, возникает вопрос, а как организовать поддержку нескольких версий iOS так, чтобы не загружалось ничего лишнего? Тут на помощь приходят группы.
%group iOS6
// Методы для iOS6
%end
%group iOS78
// Методы для iOS7+
%end
%ctor
{
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) {
init(iOS78);
} else {
init(iOS6);
}
}
Подведём итог
iOSOpenDev всего лишь плагин для Xcode, но, на мой взгляд, он гораздо проще и удобнее, чем «голый» theos. На Mac OS X он заметно облегчает разработку iOS-твиков для новичков.
Автор: Firemoon