首先我们要了解ios的应用签名机制
ios的签名机制
- iOS签名机制的作用
iOS签名机制的作用就是保证安装到用户手机上的App都是经过Apple官方允许的。当然,越狱设备除外。 在平时开发时,不管是真机调试,还是发布App到App Store,我们都需要经过以下几个步骤
- 首先,在Mac上生成CertificateSigningRequest.certSigningRequest文件
- 然后在Apple开发者网站上获取到ios_development.cer或者ios_distribution.cer证书文件,前者是开发证书,后者是发布证书
- 在Apple开发者网站上注册device,添加App ID。
- 选择device、App ID以及开发证书或者生产证书,生成mobileprovision文件
经过以上的一系列操作之后,我们最终就可以拿到ios_development.cer或者ios_distribution.cer证书文件,以及mobileprovision描述文件。通过安装这些文件到Mac上就可以进行真机调试。当然,如果你在Xcode中勾选了Automatically manage signing选项,那么Xcode会自动帮我们执行以上的所有操作。
那么,以上操作每一步执行的作用是什么?最后获得的ios_development.cer或者ios_distribution.cer证书文件,以及mobileprovision描述文件的作用又是什么?这些文件中又包含了什么内容?
iOS签名流程
要想知道上述所有文件的具体作用,就需要了解iOS签名的完整流程。在使用Xcode编译、运行完项目之后,Xcode其实帮我们做了签名操作。可以通过查看xcode的build信息
其实就是利用codeSign指令对.app文件进行签名操作。
想要执行iOS签名流程,就需要有以下公钥、私钥信息
- Mac设备的公钥和私钥,一般是Mac设备自己生成
- Apple官方的私钥,保存在Apple的后台
- Apple官方的公钥,每一台iPhone出厂之后都会保存Apple官方的公钥信息
签名流程分析
iOS项目在编译完成之后会生成.app文件,App的签名操作从拿到.app文件开始
-
第一步,拿到.app文件之后,使用Mac私钥对.app文件进行签名操作,生成的签名文件存放在.app目录下的_CodeSignature/CodeResources
-
第二步,使用Apple私钥对Mac公钥进行签名,生成证书文件
-
第三步,将第二步获取到的证书文件和devices、app id以及entitlement一起使用Apple私钥再次进行签名,生成mobileprovision文件,也就是描述文件。
-
第四步,将第一步签名过后的.app文件和第三步生成的mobileprovision文件一起压缩生成ipa安装包。
-
最后,完整的签名打包过程如下
验证流程
对.app安装包进行签名之后,压缩生成ipa安装包,然后将ipa安装包安装到iPhone上时,会进行签名验证操作。
- 第一步,使用iPhone上存放的Apple公钥验证mobileprovision文件中的签名。
- 第二步,签名验证成功之后,拿到mobileprovision中存放的devices、app id以及entitlement信息。同时拿到证书文件。然后使用Apple公钥对证书中的签名进行验证。验证成功之后拿到Mac公钥。
- 第三步,拿到Mac公钥之后,使用Mac公钥对App的签名文件进行验证,如果验证成功,则表明当前的App的源码没有被篡改过。然后此App就能安装到iPhone上
- 最后得到完整的签名加验证流程,如下
在文件mobileprovision中,有devices、app id以及entitlement信息,它们的作用分别是:
- devices标识着哪些设备可以安装此App,如果设备不在devices中,安装会失败。
- app id,指定此标识的App才能安装,如果App的唯一标识和此app id不对应,则安装失败。
- entitlement中存放在App所具备的权限信息,如果App所使用的权限和entitlement中存放的权限不一致,也会产生问题。
签名实际操作流程
上文学习了iOS签名的具体流程,现在,我们就来一步步的执行实际的操作来对我们所学的签名流程进行验证。同时了解ios_development.cer或者ios_distribution.cer证书文件,以及mobileprovision等文件的实际作用。
- 第一步,在Mac上生成CertificateSigningRequest.certSigningRequest文件,其实这个文件就是Mac设备的公钥。
- 第二步,登录Apple开发者网站,获取证书,安装到Mac设备。此步骤就是利用Apple的私钥,对Mac公钥进行签名,生成证书文件ios_development.cer和ios_distribution.cer
- 第三步,生成mobileprovision,此步骤就是将devices、app id、entitlement和证书文件,通过Apple私钥进行签名,生成最后的mobileprovision文件。而且生成的mobileprovision文件就决定了当前App可以安装的设备有哪些,可以安装的App的BundleId,以及App所拥有的权限。
Apple官方验证流程
Apple官方验证流程如下:
重签名
如果我们想要将App和我们自己编写的插件重新打包,安装到未越狱的iPhone上,那就需要学习如何对App进行重签名。
学习重签名之前,需要注意几点
- 第一,安装包中的可执行文件必须是进行过脱壳操作的,重签名才会生效,不然会安装失败
- 第二,重签名所需要的mobileprovision文件必须是付费开发者账号申请的才可以,免费开发者账号无法进行重签名。
- 第三,.app包中的所有动态库(.framework,.dylib)、AppExtension(PlugIns文件夹,拓展名是appex)、WatchApp(Watch文件夹)等都需要进行重签名操作
CodeSign指令重签名
具体步骤
- 首先,需要准备一个embedded.mobileprovision文件(必须是付费开发者账号生成的,里面的appid、device等需要匹配),然后将此文件放入.app包中
生成mobileprovision文件有两种方式,第一种是通过Xcode自动生成,在编译后的App包中可以找到,第二种是到Apple官网生成。
- 从embedded.mobileprovision文件中提取出entitlements.plist权限文件,具体有两步:
// 首先从embedded.mobileprovision文件中导出权限信息,存放到temp.plist中
security cms -D -i embedded.mobileprovision > temp.plist
# 然后使用PlistBuddy工具将temp.plist转换成Entitlements格式的文件entitlements.plist
/usr/libexec/PlistBuddy -x -c 'Print :Entitlements' temp.plist > entitlements.plist
- 查看Mac上可以使用的证书,获取到证书的Identity,后续签名需要使用,具体指令如下
security find-identity -v -p codesigning
得到的结果如下
➜ ~ security find-identity -v -p codesigning
1) id "iPhone Developer: xxxxx@xxxx.com (xxxxx)"
- 对.app包中的所有动态库、AppExtension等进行重签名,前提是修改过这些动态库或者AppExtension,如果没有修改的动态库或者AppExtension,可以跳过此步骤。指令如下:
# -fs 是 -f -s 的缩写
codesign -fs 证书ID xxx.dylib
- 对.app包进行签名,需要使用到之前生成的entitlements.plist文件,指令如下:
codesign -fs 证书ID --entitlements entitlements.plist xxx.app
Theos插件重签名
创建了Tweak项目之后,通过Cydia安装到越狱手机上,然后就可以改变App的行为。具体是怎么实现的呢?
- 首先,Tweak项目经过编译生成的是一个dylib动态库文件,存放在Tweak项目目录.theos/obj/debug/目录下。
- 执行make package打包之后生成对应的deb文件,存放在packages目录下。
- 执行make install之后,会通过Cydia安装到手机上,dylib文件存放在~/Library/MobileSubstrate/DynamicLibraries/目录下。
- 在App启动之后,会同时将dylib加载到内存中,App中如果访问被我们hook的类中的方法,会直接执行dylib中的方法。
动态库的注入
Tweak项目本质上是生成动态库,而且动态库不是存放在.app目录下,所以,想要将我们逆向过的App安装到别人的手机上,首先需要做的就是将Tweak项目生成的动态库注入到App中的可执行文件中,也就是Mach-O文件中。
可以使用insert_dylib库来将动态库注入到Mach-O文件中,可以通过insert_dylib库主页下载insert_dylib工具。在Release环境下编译,得到命令行工具,将命令行工具放在/usr/local/bin目录下。
insert_dylib库用法
insert_dylib的本质其实就是往Mach-O文件的Load Commands中添加了一个LC_LOAD_DYLIB或者LC_LOAD_WEAK_DYLIB。具体注入方法如下:
insert_dylib 动态库加载路径 Mach-O文件 --all-yes --weak
insert_dylib @executable_path/tweakTest.dylib Payload/Arm64Demo.app/Arm64Demo --all-yes --weak Payload/Arm64Demo.app/Arm64Demo
- --weak选项表示,即使当前注入的动态库找不到,App也不会报错
- --all-yes选项表示,后面所有的选项都选yes
- 后面再跟上Mach-O文件路径表示覆盖原来的文件,@executable_path代表可执行文件所在的目录,也就是Mach-O文件所在目录。
查看Mach-O的动态库依赖信息
查看动态库依赖信息两种方式
- 通过otool查看Mach-O的动态库依赖信息
otool -L Mach-O文件
- 也可以通过MachOView查看Mach-O的动态库依赖信息
更改动态库的加载地址
在向Mach-O文件中注入动态库之后,需要更改Mach-O文件中动态库的加载地址,否则在App运行时,会因为找不到动态库报错。
可以使用install_name_tool指令来修改Mach-O文件中动态库的加载地址:
install_name_tool -change 旧地址 新地址 Mach-O文件
install_name_tool -change /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate @loader_path/CydiaSubstrate Payload/Arm64Demo.app/tweakTest.dylib
需要注意的是,上述指令中的新地址,必须填写全路径地址,但是我们不知道App安装到手机上后dylib存放的具体地址,因此,可以使用以下两个常用的环境变量:
- @executable_path代表可执行文件所在的目录,也就是Mach-O文件所在目录。我们将dylib和可执行文件放在同一个目录下,然后将地址修改为@executable_path/dylib名称。这就表示在加载动态库时,到可执行文件所在的目录下去寻找动态库。
- @loader_path代表动态库所在的目录,此环境变量一般在动态库依赖其它动态库的情况下使用,如果我们需要注入的动态库还依赖其它动态库,那么就需要将所依赖的动态库和原动态库存放在同一目录下,然后更改动态库的加载地址为@loader_path/动态库名称。这表示到原动态库所在目录下加载需要依赖的动态库。
Theos开发的动态库插件注意事项
- 我们使用Theos开发的动态库插件(dylib)因为使用过Cydia安装的,所以它默认是依赖CydiaSubstrate插件的。CydiaSubstrate插件存放目录为iPhone的/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate。
- 如果想要将我们开发的动态库插件打包到ipa中, 就需要同时将CydiaSubstrate一起打包到ipa中,并且需要修改CydiaSubstrate的加载地址。
重签名GUI工具
iReSign
可以点击下载iReSign源码,运行里面的Mac应用,提供.app包的路径、entitlements.plist路径和embedded.mobileprovision的路径,就可以对.app进行重签名,然后打包生成ipa文件。
iOS App Signer
可以点击下载iOS App Signer源码,选择Release环境进行编译,拿到编译后的Mac应用,就可以直接使用。只需提供.app包的路径和embedded.mobileprovision的路径