-
-
[原创]【iOS逆向与安全】iOS插件开发光速入门
-
发表于: 2022-12-5 14:48 25009
-
前言
经过之前的学习,相信你已经能熟练的使用Frida-trace、IDA Pro等逆向工具。不过,仅仅到这肯定是不够的。接下来,学会把你逆向的结果打包成插件并运行,那iOS逆向,你也就真正的入门了。
一、目标
把逆向的结果制作成插件并运行
二、工具
mac系统
Xcode:插件开发工具
已越狱iOS设备:运行deb插件
optool:动态库注入工具,下载地址:https://github.com/alexzielenski/optool
MonkeyDev:越狱插件开发集成神器,下载地址:https://github.com/AloneMonkey/MonkeyDev
三、流程
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()
咱按以上步骤编写完的插件代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | #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; @end CHDeclareClass(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'.
解决办法:
2.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文件已经注入完成:
1 2 3 4 5 6 | witchan@witchandeMacBook - Air Payload % optool install - c load - p "@executable_path/Frameworks/FirstDeb.dylib" - t ExampleCode.app / ExampleCode Found thin header... Inserting a LC_LOAD_DYLIB command for architecture: arm64 Successfully inserted a LC_LOAD_DYLIB command for arm64 Writing executable to ExampleCode.app / ExampleCode... witchan@witchandeMacBook - Air Payload % |
接下来将Payload文件夹压缩成zip包,然后把扩展名改为ipa,然后用爱思,或其他重签名工具对该ipa进行签名后,即可在越狱或非越狱机上安装使用了。运行后的日志如下:
3.Framework插件开发
1. 创建插件工程
启动Xcode后,按如下流程依次点击:
2. 编写插件代码
创建AppHelper类
创建完成后的目录结构如下:
在这。我们使用之前的deb插件编写方式来写。先把CaptainHook.h文件的所有内容复制到AppHelper.h:
复制整个头文件的内容到AppHelper.h即可。然后把deb插件.m文件的全部代码复制到AppHelper.m,再在.m文件的顶部引入AppHelper.h文件,然后剩下的代码编写方式就和deb的编写完全一样。
最终的AppHelper.h的完整代码如下:
1 | 由于该文件较大,在这进行删除,完整代码请看文末。 |
AppHelper.m的完整代码如下(和deb相比,只多了#import "AppHelper.h"
):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | #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; @end CHDeclareClass(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文件已经注入完成:
1 2 3 4 5 6 | witchan@witchandeMacBook - Air Payload % optool install - c load - p "@executable_path/Frameworks/FirstFramework.framework/FirstFramework" - t ExampleCode.app / ExampleCode Found thin header... Inserting a LC_LOAD_DYLIB command for architecture: arm64 Successfully inserted a LC_LOAD_DYLIB command for arm64 Writing 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
】