写点什么

【iOS 逆向与安全】iOS 插件开发光速入门

作者:小陈
  • 2022-12-05
    四川
  • 本文字数:7589 字

    阅读完需:约 25 分钟

前言

经过之前的学习,相信你已经能熟练的使用 Frida-trace、IDA Pro 等逆向工具。不过,仅仅到这肯定是不够的。接下来,学会把你逆向的结果打包成插件并运行,那 iOS 逆向,你也就真正的入门了。



一、目标

把逆向的结果制作成插件并运行


二、工具

三、流程

iOS 端的插件按设备分为

  • 越狱插件:扩展名为.deb,类似于安卓的 xposed 插件

    优点:独立于 ipa 文件,ipa 可单独升级(前提是相关的 hook 代码逻辑没变)

    缺点:必须要越狱设备

  • 非越狱插件:扩展名为.dylib 或.framework,类似于安卓的 so 文件

    优点:可在非越狱机上运行。由于非越狱机,App 自然也就检测不到越狱状态,但仍然可以检测 ipa 包的完整性

    缺点:ipa 无法单独升级,必须要砸壳,动态库注入,重签名后,才能完成升级操作。

注:不管是 deb 格式、dylib 格式还是 framework 格式,都支持使用 c、c++和 OC 语言进行开发


MonkeyDev,原有 iOSOpenDev 的升级,非越狱插件开发集成神器!

  • 可以使用 Xcode 开发 CaptainHook Tweak、Logos Tweak 和 Command-line Tool,在越狱机器开发插件,这是原来 iOSOpenDev 功能的迁移和改进。

  • 只需拖入一个砸壳应用,自动集成 class-dump、restore-symbol、Reveal、Cycript 和注入的动态库并重签名安装到非越狱机器。

  • 支持调试自己编写的动态库和第三方 App

  • 支持通过 CocoaPods 第三方应用集成 SDK 以及非越狱插件,简单来说就是通过 CocoaPods 搭建了一个非越狱插件商店。


所以在本教程中,deb 插件和 dylib 插件,主要使用 MonkeyDev 来完成,安装教程参考官方即可。而 framework 插件,则直接使用 Xcode 开发。


deb 插件开发

1. 创建插件工程

启动 Xcode 后,按如下流程依次点击:







至此,插件已经创建完毕:



2. 编写插件代码

创建完这是 MonkeyDev 作者写的使用步骤

// Objective-C runtime hooking using CaptainHook:

// 1. declare class using CHDeclareClass()

// 2. load class using CHLoadClass() or CHLoadLateClass() in CHConstructor

// 3. hook method using CHOptimizedMethod()

// 4. register hook using CHHook() in CHConstructor

// 5. (optionally) call old method using CHSuper()

咱按以上步骤编写完的插件代码如下:

#if TARGET_OS_SIMULATOR#error Do not support the simulator, please use the real iPhone Device.#endif// 导入常用的UI框架和Foundation框架#import "AppHelper.h" // 注意,一定要导入这,因为这头文件里有Monkey dev定义的宏#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>// 导入MonkeyDev提供的头文件,这头文件,后边我们创建framework插件时也可使用#import "CaptainHook/CaptainHook.h"/* 定义你需要Hook的类及需要Hook的方法。 */@interface DetailViewController : NSObject// 需要Hook的实例方法- (void)loginButtonDidClick:(UIButton *)sender;// 以下两个方法并不存在,在这只是为了演示如何hook多个参数的方法和hook类方法- (NSString *)loginWithPhone:(NSString *)phone password:(NSString *)pwd;+ (id)factory:(id)arg1;@endCHDeclareClass(DetailViewController); // 步骤1、申明需要Hook的类/* 步骤3、你的勾子函数,Hook函数被调用时,会执行到这 CHOptimizedMethod的参数说明 第一个参数:固定写死self即可 第二个参数:返回值类型,无返回值写void。c语言的类型,直接写对应的类型即可(int,float,double...)。其他类型,直接写id即可,如果你知道具体的类型,也可写具体的类型 第三个参数:类名 第四个参数:方法名 // 方法名有一个参数时 第五个参数:第一个入参的类型,和第二个参数写法类型 第六个参数:第一个入参的形参名 // 方法名有两个参数时 第七个参数:第二个入参的类型,和第二个参数写法类型 第八个参数:第二个入参的形参名 ...*/CHOptimizedMethod1(self, void, DetailViewController, loginButtonDidClick, UIButton*, sender) {    CHSuper1(DetailViewController, loginButtonDidClick, sender);  // 调用原方法    NSLog(@"witchan =该方法的入参为:%@", sender);}// Hook两个入参的实例方法CHOptimizedMethod2(self, id, DetailViewController, loginWithPhone, NSString *, p, password, NSString*, pwd) {    id result = CHSuper2(DetailViewController, loginWithPhone, p, password, pwd); // 调用原方法    NSLog(@"witchan =该方法的第一个入参为:%@", p);    NSLog(@"witchan =该方法的第二个入参为:%@", pwd);    NSLog(@"witchan =该方法的返回值为:%@", result);    return result;}// Hook一个入参的类方法,相对于实例方法,只是在Method前多了个Class单词。其他操作完全一样CHOptimizedClassMethod1(self, id, DetailViewController, factory, id, arg1) {    id result = CHSuper1(DetailViewController, factory, arg1);  // 调用原方法    NSLog(@"witchan =该方法的入参为:%@", arg1);    NSLog(@"witchan =该方法的返回值为:%@", result);        /*      由于deb格式、dylib格式还是framework格式,都支持使用c、c++和OC语言进行开发。     以下代码为oc语法的简单示例。     注意:整个插件的写法,和ios开发完全一致,你可以创建新类,也可以调用oc提供的类及相关方法     如果你不熟悉oc语法,请看我公众号的另一篇文章,iOS快速入门:https://mp.weixin.qq.com/s/g89Sdyqc4ONlyAWtXTCwRA     */    NSMutableDictionary *params = [NSMutableDictionary dictionary];    params[@"微信公众号"] = @"移动端Android和iOS开发技术分享";    params[@"QQ群"] = @"812546729";        NSData *body = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil];        // 调用登录接口    NSURL *loginURL = [NSURL URLWithString:@"https://127.0.0.1:9080/login"];    // 接口    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:loginURL]; // 请求对象    request.HTTPMethod = @"POST";    // 请求方式    [request setValue:@"d83kd9d323" forHTTPHeaderField:@"x-sign"];   // 设置header    request.HTTPBody = body;    // 注意,HTTPBody是一个16进制数据,一般直接16进制输出,再转换成文本查看        NSURLSession *session = [NSURLSession sharedSession];   // 获取网络对象    NSURLSessionTask *task = [session dataTaskWithRequest:request                                        completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {        // 请求结果会调到这                if (error != nil) {            NSLog(@"witchan =网络请求出错了");        } else {            NSLog(@"witchan =网络请求成功");        }            }]; // 创建请求任务    [task resume];  // 发起网络请求        return result;}// 入口函数CHConstructor{  @autoreleasepool  {        NSLog(@"witchan =FirsDeb hook success!=");    // 一般在入口函数输出一条日志,确定你的插件是否加载成功        CHLoadLateClass(DetailViewController);  // 步骤2、加载需要Hook的类        CHHook1(DetailViewController, loginButtonDidClick); // 步骤4、注册你需要hook的实例方法        CHHook2(DetailViewController, loginWithPhone, password);        CHClassHook1(DetailViewController, factory);    // 注册需要hook的类方法  }}
复制代码


填写目标 ipa 的包名:



编辑插件扩展信息(可选):



3. 编译插件

编译:依次选择菜单栏的 Product -> Build



编译后的结果是这样的:



打开目录,就能看到你需要的 deb 文件了




安装方式 1:拿到 deb 文件后,你可以用爱思或 ssh 工具,把 deb 文件换过血到手机,再使用 Filza 工具找到对应的文件,安装即可:




安装方式 2: 如果你是 mac 电脑,那你可使用隔空投送功能来把 deb 文件传输至手机并安装:





安装方式 3:是不是觉得上边的安装方式有点麻烦,如果是自己真机调试,那你可以这样玩:



然后依次选择菜单栏的 Product -> Build 或快捷键 command+b,这次编译完成后,没有报错。并且你的手机已自动注销了。打开 cyida 查看你最近安装的 deb,就会发现刚编写的插件已经安装成功,简单吧。

在电脑端打开控制台工具:




这时启动被注入 deb 插件的 App 后,看到的日志如下:



如果你编辑时,遇到这错误:

An empty identity is not valid when signing a binary for the product type 'Dynamic Library'.



解决办法:




dylib 插件使用

在 deb 插件开发教程中,我们已经同时得到了 deb 插件和 dylib 插件,所以在本教程中,则教大家如何把 dylib 插件注入到 ipa,注入工具为 optool,该工具主要有两条命令:

  • 注入:optool install -c load -p "@executable_path/Frameworks/aaa.framework/aaa" -t demo.app/demo

  • 卸载:optool uninstall -p "@executable_path/Frameworks/aaa.framework/aaa" -t demo.app/demo

把 ipa 文件的扩展名改为 zip,解压后,得到 Payload 文件夹:




切换到 Payload 目录cd Desktop/Payload,然后执行注入命令optool install -c load -p "@executable_path/Frameworks/FirstDeb.dylib" -t ExampleCode.app/ExampleCode,至此,我们的 dylib 文件已经注入完成:

witchan@witchandeMacBook-Air Payload % optool install -c load -p "@executable_path/Frameworks/FirstDeb.dylib" -t ExampleCode.app/ExampleCodeFound thin header...Inserting a LC_LOAD_DYLIB command for architecture: arm64Successfully inserted a LC_LOAD_DYLIB command for arm64Writing executable to ExampleCode.app/ExampleCode...witchan@witchandeMacBook-Air Payload %
复制代码

接下来将 Payload 文件夹压缩成 zip 包,然后把扩展名改为 ipa,然后用爱思,或其他重签名工具对该 ipa 进行签名后,即可在越狱或非越狱机上安装使用了。运行后的日志如下:



Framework 插件开发


1. 创建插件工程

启动 Xcode 后,按如下流程依次点击:







2. 编写插件代码

创建 AppHelper 类





创建完成后的目录结构如下:



在这。我们使用之前的 deb 插件编写方式来写。先把 CaptainHook.h 文件的所有内容复制到 AppHelper.h:



复制整个头文件的内容到 AppHelper.h 即可。然后把 deb 插件.m 文件的全部代码复制到 AppHelper.m,再在.m 文件的顶部引入 AppHelper.h 文件,然后剩下的代码编写方式就和 deb 的编写完全一样。

最终的 AppHelper.h 的完整代码如下:

由于该文件较大,在这进行删除,完整代码请看文末。
复制代码


AppHelper.m 的完整代码如下(和 deb 相比,只多了#import "AppHelper.h"):

#if TARGET_OS_SIMULATOR#error Do not support the simulator, please use the real iPhone Device.#endif// 导入常用的UI框架和Foundation框架#import "AppHelper.h" // 注意,一定要导入这,因为这头文件里有Monkey dev定义的宏#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>// 导入MonkeyDev提供的头文件,这头文件,后边我们创建framework插件时也可使用#import "CaptainHook/CaptainHook.h"/* 定义你需要Hook的类及需要Hook的方法。 */@interface DetailViewController : NSObject// 需要Hook的实例方法- (void)loginButtonDidClick:(UIButton *)sender;// 以下两个方法并不存在,在这只是为了演示如何hook多个参数的方法和hook类方法- (NSString *)loginWithPhone:(NSString *)phone password:(NSString *)pwd;+ (id)factory:(id)arg1;@endCHDeclareClass(DetailViewController); // 步骤1、申明需要Hook的类/* 步骤3、你的勾子函数,Hook函数被调用时,会执行到这 CHOptimizedMethod的参数说明 第一个参数:固定写死self即可 第二个参数:返回值类型,无返回值写void。c语言的类型,直接写对应的类型即可(int,float,double...)。其他类型,直接写id即可,如果你知道具体的类型,也可写具体的类型 第三个参数:类名 第四个参数:方法名 // 方法名有一个参数时 第五个参数:第一个入参的类型,和第二个参数写法类型 第六个参数:第一个入参的形参名 // 方法名有两个参数时 第七个参数:第二个入参的类型,和第二个参数写法类型 第八个参数:第二个入参的形参名 ...*/CHOptimizedMethod1(self, void, DetailViewController, loginButtonDidClick, UIButton*, sender) {    CHSuper1(DetailViewController, loginButtonDidClick, sender);  // 调用原方法    NSLog(@"witchan =该方法的入参为:%@", sender);}// Hook两个入参的实例方法CHOptimizedMethod2(self, id, DetailViewController, loginWithPhone, NSString *, p, password, NSString*, pwd) {    id result = CHSuper2(DetailViewController, loginWithPhone, p, password, pwd); // 调用原方法    NSLog(@"witchan =该方法的第一个入参为:%@", p);    NSLog(@"witchan =该方法的第二个入参为:%@", pwd);    NSLog(@"witchan =该方法的返回值为:%@", result);    return result;}// Hook一个入参的类方法,相对于实例方法,只是在Method前多了个Class单词。其他操作完全一样CHOptimizedClassMethod1(self, id, DetailViewController, factory, id, arg1) {    id result = CHSuper1(DetailViewController, factory, arg1);  // 调用原方法    NSLog(@"witchan =该方法的入参为:%@", arg1);    NSLog(@"witchan =该方法的返回值为:%@", result);        /*      由于deb格式、dylib格式还是framework格式,都支持使用c、c++和OC语言进行开发。     以下代码为oc语法的简单示例。     注意:整个插件的写法,和ios开发完全一致,你可以创建新类,也可以调用oc提供的类及相关方法     如果你不熟悉oc语法,请看我公众号的另一篇文章,iOS快速入门:https://mp.weixin.qq.com/s/g89Sdyqc4ONlyAWtXTCwRA     */    NSMutableDictionary *params = [NSMutableDictionary dictionary];    params[@"微信公众号"] = @"移动端Android和iOS开发技术分享";    params[@"QQ群"] = @"812546729";        NSData *body = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil];        // 调用登录接口    NSURL *loginURL = [NSURL URLWithString:@"https://127.0.0.1:9080/login"];    // 接口    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:loginURL]; // 请求对象    request.HTTPMethod = @"POST";    // 请求方式    [request setValue:@"d83kd9d323" forHTTPHeaderField:@"x-sign"];   // 设置header    request.HTTPBody = body;    // 注意,HTTPBody是一个16进制数据,一般直接16进制输出,再转换成文本查看        NSURLSession *session = [NSURLSession sharedSession];   // 获取网络对象    NSURLSessionTask *task = [session dataTaskWithRequest:request                                        completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {        // 请求结果会调到这                if (error != nil) {            NSLog(@"witchan =网络请求出错了");        } else {            NSLog(@"witchan =网络请求成功");        }            }]; // 创建请求任务    [task resume];  // 发起网络请求        return result;}// 入口函数CHConstructor{  @autoreleasepool  {        NSLog(@"witchan =FirsFramework hook success!=");    // 一般在入口函数输出一条日志,确定你的插件是否加载成功        CHLoadLateClass(DetailViewController);  // 步骤2、加载需要Hook的类        CHHook1(DetailViewController, loginButtonDidClick); // 步骤4、注册你需要hook的实例方法        CHHook2(DetailViewController, loginWithPhone, password);        CHClassHook1(DetailViewController, factory);    // 注册需要hook的类方法  }}
复制代码


3.编译 Framework

然后依次选择菜单栏的 Product -> Build 或快捷键 command+b。再选择菜单栏的 Product -> Show Build Folder in Finder




4.framework 插件使用

使用 optool 工具把 framework 插件注入到 ipa,该工具主要有两条命令:

  • 注入:optool install -c load -p "@executable_path/Frameworks/aaa.framework/aaa" -t demo.app/demo

  • 卸载:optool uninstall -p "@executable_path/Frameworks/aaa.framework/aaa" -t demo.app/demo

把 ipa 文件的扩展名改为 zip,解压后,得到 Payload 文件夹:




切换到 Payload 目录cd Desktop/Payload,然后执行注入命令optool install -c load -p "@executable_path/Frameworks/FirstFramework.framework/FirstFramework" -t ExampleCode.app/ExampleCode,至此,我们的 framework 文件已经注入完成:

witchan@witchandeMacBook-Air Payload % optool install -c load -p "@executable_path/Frameworks/FirstFramework.framework/FirstFramework" -t ExampleCode.app/ExampleCodeFound thin header...Inserting a LC_LOAD_DYLIB command for architecture: arm64Successfully inserted a LC_LOAD_DYLIB command for arm64Writing executable to ExampleCode.app/ExampleCode...witchan@witchandeMacBook-Air Payload %
复制代码

接下来将 Payload 文件夹压缩成 zip 包,然后把扩展名改为 ipa,然后用爱思,或其他重签名工具对该 ipa 进行签名后,即可在越狱或非越狱机上安装使用了。运行后的日志如下:



总结

以上就是 iOS 插件相关的教程,希望该文章对你有所帮助。deb 格式的叫插件,其实 dylib 和 framework 文件,在 iOS 里,叫动态库。就和安卓的 so 文件类似。在这之所以都叫插件,是因为市场需要。

本文所涉及到的源码下载链接: https://pan.baidu.com/s/1g8nIfGRejKYBrT2JbNw-ZQ?pwd=munc 提取码: munc

提示:阅读此文档的过程中遇到任何问题,请关住工众好【移动端Android和iOS开发技术分享】或+99 君羊【812546729



用户头像

小陈

关注

和昨天不一样 2019-03-12 加入

公众号:移动端Android和iOS开发技术分享

评论

发布
暂无评论
【iOS逆向与安全】iOS插件开发光速入门_小陈_InfoQ写作社区