Освоив упомянутые выше инструменты, вы можете приступить к разработке плагинов. Во-первых, вам следует подумать о потребностях, которые должен реализовать плагин, и найти соответствующие представления и контроллеры. Первым шагом является использование дампа классов для анализа оболочки APP. Class-dump может анализировать все имена классов и объявления методов на основе таблицы символов в файле Mach-O.
~ » ./class-dump -H -o /header/path WeChat
После экспорта файлов заголовков перенесите эти файлы заголовков в проект XCode, чтобы облегчить последующий поиск.
После того, как у вас есть файл заголовка, вам необходимо привязать его к классу контроллера соответствующего интерфейса. Используйте OpenSSH для подключения к взломанному iPhone с Mac, используйте Cycript для внедрения процесса, вызовите метод [[UIApp keyWindow] recursiveDescription]
чтобы получить иерархию текущего представления, получите адрес представления и повторно вызовите [#0x2b2b2b00 nextResponder]
, вы наконец получите класс контроллера интерфейса.
На этом этапе обычно есть две точки входа для продолжения обратного анализа.
Если мы хотим, чтобы операция Hook запускалась событием касания представления в интерфейсе, обычно есть два способа найти действие:
[button allTargets]
, чтобы получить адрес цели, [button actionsForTarget: Targets forControlEvent: [button allControlEvents]]
чтобы получить соответствующий метод действия.Источник данных контроллера может использоваться во время записи плагина. Источник данных можно получить путем анализа списка переменных-членов класса контроллера или путем анализа прокси-метода источника данных некоторых представлений (например, TableView). Имея источник данных, можно получить более полную информацию о контроллере.
Простой плагин может заблокировать цель и перехватить ее, используя только Cycript, но во многих случаях целевая функция имеет несколько параметров, а логика относительно сложна. Если вы хотите узнать подробности ее внутренней реализации, вам необходимо это сделать. используйте артефакт статического анализа IDA ---- Он может декомпилировать код, написанный на Objective-C, в ассемблерный код, и детали реализации функции будут понятны с первого взгляда. При анализе результатов дизассемблирования из-за особенностей передачи сообщений Objective-C общая отправка сообщений фактически вызывает функцию objc_msgSend
. Просто запомните значение каждого параметра objc_msgSend
и соглашение о вызовах архитектуры ARM, и вы сможете успешно восстановить вызов функции из ассемблерного кода. Соглашение о вызовах заключается в том, что первые четыре параметра функции передаются с использованием регистров общего назначения R0-R3, остальные параметры помещаются в стек, а возвращаемое значение сохраняется в регистре R0. Тогда [aObject aMessage: arg1];
соответствует objc_msgSend(aObject, aMessage, arg1);
R0 хранит адрес получателя сообщения, R1 хранит селектор, а R2 сохраняет адрес первого параметра. Формат сообщения для дополнительных параметров следующий:
objc_msgSend(R0, R1, R2, R3, *SP, *(SP + sizeOfLastArg), …)
С помощью приведенного выше формата логика определенной функции может анализироваться шаг за шагом. Если это будет дополнено динамической одношаговой отладкой LLDB и отладкой с отслеживанием точек останова по адресу определенного ассемблерного оператора, это поможет понять детали реализации функции.
Существует множество решений целевой функции Hook, но в принципе все они основаны на динамических характеристиках Objective-C и используют метод Swizzling для замены исходной реализации. На этот раз мы подробно познакомим вас с использованием библиотеки CaptainHook. Эта библиотека реализована на основе MSHookMessageEx()
в Cydia Substrate. Объявление этой функции:
void MSHookMessageEx(Class _class, SEL message, IMP hook, IMP *old);
После установки iOSOpenDev вы можете использовать шаблон CaptainHook Tweak для создания проекта. Библиотека CaptainHook представляет новый синтаксис для написания функций-хуков. Сначала загрузите класс, в котором находится подключаемая функция, в CHConstructor()
, например CHLoadLateClass(UIView)
. Затем зарегистрируйте функцию CHHook(argNumber, className, arg1, arg2)
для перехвата. Макроопределение CHConstructor
выглядит следующим образом:
#define CHConcat(a, b) CHConcat_(a, b)
#define CHConstructor static __attribute__((constructor)) void CHConcat(CHConstructor, __LINE__)()
Содержимое после __attribute__((constructor))
гарантированно запускается при загрузке dylib, обычно при запуске программы. Аналогично, другие символы вводятся через определения макросов.
Позвольте мне рассказать, как использовать CaptainHook для объявления функции Hook и реализации ее непосредственно в коде.
CHDeclareClass ( BXViewController );
CHOptimizedMethod ( 0 , self , void , BXViewController , viewDidLoad ) {
CHSuper ( 0 , BXViewController , viewDidLoad );
/* HERE TO WRITE YOUR CODE */
}
CHDeclareMethod0 ( void , BXViewController , addFriends ) {
/* HERE TO WRITE YOUR CODE */
}
После завершения записи подключитесь к подручному iPhone для компиляции, чтобы убедиться, что сгенерирована динамическая библиотека, соответствующая архитектуре.
Используйте инструмент yololib, чтобы добавить скомпилированный файл dylib в список команд загрузки исполняемого файла Mach-O.
~ » ./yololib [binary] [dylib file]
Структура файлов Mach-O в основном состоит из трех частей. Внешняя часть — это структура заголовка, в которой сохраняется тип платформы Mach-O, тип файла, количество команд загрузки и другая информация, за которой следует заголовок — часть команд загрузки. Путем анализа этой части определяется логическая структура файла и. его расположение в виртуальном файле можно определить в памяти. Инструмент yololib изменяет информацию в разделе «Команды загрузки» для загрузки dylib. Конкретный процесс реализации выглядит следующим образом:
Поскольку информация о командах загрузки изменяется, как ncmds, так и sizeofcmds в соответствующей структуре заголовка изменятся, поэтому сначала необходимо изменить заголовок:
// 取出 Header
fseek(newFile, top, SEEK_SET);
struct mach_header mach;
fread(&mach, sizeof(struct mach_header), 1, newFile);
NSData* data = [DYLIB_PATH dataUsingEncoding:NSUTF8StringEncoding];
// 计算 dylib 的大小
uint32_t dylib_size = (uint32_t)[data length] + sizeof(struct dylib_command);
dylib_size += sizeof(long) - (dylib_size % sizeof(long));
// 修改 cmds 和 sizeofcmds
mach.ncmds += 1;
uint32_t sizeofcmds = mach.sizeofcmds;
mach.sizeofcmds += dylib_size;
// 写回修改后的 Header
fseek(newFile, -sizeof(struct mach_header), SEEK_CUR);
fwrite(&mach, sizeof(struct mach_header), 1, newFile);
Затем измените раздел «Команды загрузки» и добавьте информацию о загрузке dylib:
fseek(newFile, sizeofcmds, SEEK_CUR);
// 创建一个 dylib 类型的 command
struct dylib_command dyld;
fread(&dyld, sizeof(struct dylib_command), 1, newFile);
// 修改 dyld 结构体数据
dyld.cmd = LC_LOAD_DYLIB;
dyld.cmdsize = dylib_size;
dyld.dylib.compatibility_version = DYLIB_COMPATIBILITY_VERSION;
dyld.dylib.current_version = DYLIB_CURRENT_VER;
dyld.dylib.timestamp = 2;
dyld.dylib.name.offset = sizeof(struct dylib_command);
// 写回修改
fseek(newFile, -sizeof(struct dylib_command), SEEK_CUR);
fwrite(&dyld, sizeof(struct dylib_command), 1, newFile);
Последние данные, записанные в dylib.
fwrite([data bytes], [data length], 1, newFile);
Используйте команду codesign
, чтобы повторно подписать созданную динамическую библиотеку и все исполняемые файлы в приложении (включая расширение приложения в папке плагина). Используйте команду xcrun -sdk iphoneos PackageApplication -v
чтобы упаковать динамическую библиотеку и все файлы вместе. Вы поймете весь процесс. При наличии корпоративного сертификата подписанное и упакованное приложение можно установить на iPhone без взломанного джейлбрейка, который доверяет этому сертификату. Звезда гребли!