通过上一章节了解了对称密码、公钥密码、单向散列函数、数字签名、证书等相关知识,本章节就开始介绍iOS的签名机制和重签名。
一、iOS签名机制
iOS签名机制的作用:保证安装到用户手机上的APP都是经过Apple官方允许的。
不管是真机调试,还是发布APP,开发者都需要经过一系列复杂的步骤:
- 生成
CertificateSigningRequest.certSigningRequest
文件; - 获得
ios_development.cer\ios_distribution.cer
证书文件; - 注册device、添加App ID;
- 获得
*.mobileprovision
文件。
对于真机调试,现在的Xcode已经自动帮开发者做了以上操作。
思考:每一步的作用是什么?.certSigningRequest
、.cer
、.mobileprovision
文件究竟里面包含了什么?有何用处?
1.1. 流程图
大致流程分为以下几步:
- 对应的是keychain里的 “从证书颁发机构请求证书”,本地生成了一对密钥,保存的
.certSigningRequest
就是公钥,私钥保存在本地电脑里。 - 苹果自身实现。
- 把
.certSigningRequest
传到苹果后台,Apple后台的私钥对Mac设备的公钥进行签名,生成证书ios_development.cer、ios_distribution.cer
,并下载到本地。 - 配置AppID/权限/设备等,最后下载
.mobileprovision
文件。 - Xcode会通过第3步下载回来的证书(存着公钥),在本地找到对应的私钥(第一步生成的),用本地私钥去签名 App(在Xcode中选择对应的证书时,实际上会找到keychain里对应的私钥去签名。这里私钥只有生成它的这台Mac有,如果别的Mac也要编译签名这个App怎么办?答案是在keychain里导出私钥(
.p12
文件),其他Mac打开并输入密码后会自动把这个私钥导入到钥匙串。),并把.mobileprovision
文件命名为embedded.mobileprovision
后一起打包到APP中。这里对App的签名数据保存分两部分,Mach-O可执行文件会把签名直接写入这个文件里,其他资源文件则会保存在_CodeSignature目录下。 - 在安装时,iOS系统取得证书,通过系统内置的公钥A,去验证
embedded.mobileprovision
的数字签名是否正确,里面的证书签名也会再验一遍。 - 确保
embedded.mobileprovision
里的数据都是苹果授权以后,就可以取出里面的数据,做各种验证,包括用公钥L验证APP签名,验证设备ID是否在ID列表上,AppID是否对应得上,权限开关是否跟APP里的Entitlements对应等。
证书:Mac公钥 + Apple签名信息。
Code Signing Identity:证书 + Mac私钥。
.p12文件:导出的Mac私钥就是p12文件格式。
Entitlements:授权机制决定了哪些系统资源在什么情况下允许被一个应用使用。简单的说它就是一个沙盒的配置列表,上面列出了哪些行为被允许,哪些会被拒绝。在Xcode的Capabilities中列举的功能都是需要配置授权的。
Provisioning Profile:证书 + 设备IDs + AppID + Entitlements + 签名信息。
上面的流程是双层签名认证。如果APP是从AppStore下载安装的,你会发现里面是没有mobileprovision
文件的。它的验证流程会简单很多,大概如下所示。
提交App Store的应用是采用单层签名验证,只需要苹果自身的密钥就能完成。而企业签名、真机调试等情形,不仅需要苹果参与签名,而且需要开发者自身参与签名,因此称之为双层签名验证。
二、重签名
如果希望将破坏了签名的安装包安装到非越狱的手机上,需要对安装包进行重签名的操作。
注意:
- 安装包中的可执行文件必须是经过脱壳的,重签名才会有效(通过Xcode或者其他脱壳软件市场安装的软件没有经过AppStore,所以不需要脱壳)。
- app包内部的所有动态库(
.framework
、.dylib
)、AppExtension(PlugIns文件夹,拓展名是appex
)、WatchApp(Watch文件夹)都需要重新签名。
重签名打包后,安装到设备的过程中,可能需要经常查看设备的日志信息,下面是查看日志的入口:
程序运行过程中:
Xcode -> Window -> Devices and Simulators -> View Device Logs
。程序安装过程中:
Xcode -> Window -> Devices and Simulators -> Open Console
或直接打开控制台.app
。
2.1. 重签名步骤
准备一个
embedded.mobileprovision
文件(必须是付费证书产生的,appId、device一定要匹配),并放入.app
包中。可以通过Xcode自动生成,然后在编译后的APP包中找到
可以去开发者证书网站生成下载
从
embedded.mobileprovision
文件中提取出entitlements.plist
权限文件1
2security cms -D -i embedded.mobileprovision > temp.plist
/usr/libexec/PlistBuddy -x -c 'Print :Entitlements' temp.plist > entitlements.plist查看可用的证书。
1
security find-identity -v -p codesigning
对
.app
内部的动态库、AppExtension等进行签名(注意:不需要entitlements文件)。1
codesign -fs 证书ID xxx.dylib
对
.app
包进行签名。
1 | codesign -fs 证书ID --entitlements entitlements.plist xxx.app |
2.2. 重签名GUI工具
上面2.1步骤的重签名步骤都需要命令行进行操作,在Github上已经有相关的图形化工具,如下:
iOS App Signer:https://github.com/DanTheMan827/ios-app-signer
可以对
.app
重签名打包成ipa需要在
.app
包中提供对应的embedded.mobileprovision
文件
2.3. 动态库注入
2.3.1. 动态库加载
APP运行时,会通过Mach-O文件的Load Commands中加载相关的动态库,但是我们新增的动态库(比如使用theos的tweak生成的动态库)并不在该文件中,所以会导致APP无法执行动态库的代码。
怎样才能让APP加载这个动态库呢?可以使用insert_dylib(https://github.com/Tyilo/insert_dylib)库将动态库注入到Mach-O文件中。
1 | insert_dylib 动态库加载路径 Mach-O文件 |
有2个常用参数选项:
--weak
,即使动态库找不到也不会报错。--all-yes
,后面所有的提示的选项都为yes。
动态库加载路径是一个绝对路径,而动态库和Mach-O文件在同一个目录下面,所以可以使用下面2个常用环境变量:
@executable_path
:代表可执行文件所在的目录。@loader_path
:代表动态库所在的目录。
默认情况下,上面的动态库注入命令完成后会在当前文件生成一个APP同名并且后缀为_patched
的可执行文件(例:app_patched)。如果想要直接覆盖原来的Mach-O文件,可以在命令最后加上新生成文件的名字。
1 | insert_dylib @executable_path/tweak_apptest.dylib apptest --all-yes --weak apptest |
insert_dylib的本质是往Mach-O文件的Load Commands中添加了一个LC_LOAD_DYLIB
或LC_LOAD_WEAK_DYLIB
。
可以通过otool查看Mach-O/动态库的动态库依赖信息:
otool -L Mach-O文件/动态库文件
。
2.3.2. 动态库依赖
动态库依赖其他非Apple官方提供的动态库时,需要把依赖的动态库也加入到ipa中。同时需要修改动态库的加载地址。
可以使用install_name_tool修改Mach-O文件中动态库的加载地址:
1 | install_name_tool -change 动态库依赖旧地址 动态库依赖新地址 被依赖的动态库地址 |
越狱的手机,通过Theos开发的动态库插件(dylib)默认都依赖于/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate
,每次加载动态库时都会到该目录下加载这个动态库。但是非越狱设备没有CydiaSubstrate
动态库,如果要将开发的动态库插件打包到ipa中,也需要将CydiaSubstrate
打包到ipa中,并且需要修改下CydiaSubstrate
的加载地址。
步骤:
- 把
CydiaSubstrate
放到tweak_apptest.dylib
同一个目录下(ipa根目录); - 执行下面的命令,让APP加载
tweak_apptest.dylib
时去指定位置查找并加载CydiaSubstrate
。
1 | install_name_tool -change /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate @loader_path/CydiaSubstrate tweak_apptest.dylib |
注意:动态库注入操作完成后,需要对动态库(包含依赖的动态库)和APP进行重新签名。