作为一名iOS开发者肯定知道iOS开发证书,那么证书具体是什么呢?为什么到我们本地的不是证书本身,而是一个称之为描述文件的东西。而这「描述文件」又具体是什么呢?
在知道这「描述文件」后,我们是不是可以通过将「描述文件」替换为我们自己的「描述文件」进行一些有意思的事情呢?比如这篇文章的主题,对一个APP进行重签名,让他在我们的XCode中运行起来,运用我们强大的LLDB进行调试,分析其UI和部分逻辑。当然作为一个正直的开发者,我们在这片文章肯定不会对她进「代码注入」这样让人不(兴)齿(奋)的事情的(下一篇就会 O(∩_∩)O哈哈~)。
片头先给福利:点击下载Demo:AppReSign,使用方法详见demo的Readme
接下来我们会从以下几点来玩一玩「APP重签名」
- 分析Apple对App加密的需求
- 什么是双层签名
- 手动对APP进行重签名
- 使用Shell脚本进行重签
1、分析Apple对App加密的需求
首先明确一点,对App加密肯定是用要非对称加密而不是对称加密!这我默认大家都懂了,应该不用解释。
Step 1 安装App到手机的渠道分析
如果所有的APP都是从App Store下载的那么,这就简单了,只需要对APP进行一层RSA加密就可以了,App Store持有私钥,我们iPhone持有公钥。那么问题就解决了。但事实上肯定不是这样,我们已知的渠道除了App Store就包括XCode打包,ADHOC证书分发,企业证书分发等等。为了解决这个问题,我们就得先了解Apple的具体需求是什么。
Step 2 分析需求
- App Store下载
- XCode直接安装
- 限制ADHoc打包的APP安装设备
- ...等等
2、什么是双层签名
针对以上三种(包括但不只)场景,苹果给出「双层签名」的机制。
什么是「双层签名」,先上一张PPT。
看不懂?没关系,下面会一步步,详细的接受双层签名的始末。
1、向服务器申请证书
- 什么是CSR文件 申请过开发者证书的都知道,要申请证书,都有「Mac本地通过CSR文件向服务器」这样的一步。那么这一步到底做了写什么呢? 先看看什么是CSR
CSR是Certificate Signing Request的英文缩写,即证书请求文件,也就是证书申请者在申请数字证书时由CSP(加密服务提供者)在生成私钥的同时也生成证书请求文件,证书申请者只要把CSR文件提交给证书颁发机构后,证书颁发机构使用其根证书私钥签名就生成了证书公钥文件,也就是颁发给用户的证书。
通俗一点,就是Mac本地有一对公钥(公钥M)和私钥(私钥M)(什么是公钥私钥?),在Mac上对公钥M进行了一层包装,这包装后的新东西就是CSR。
- 服务器通过CSR返回证书 本地有了CSR文件之后,将CSR上传到Apple的服务器。 Apple服务器其实也是有一对公钥(公钥A)和私钥(私钥A),在收到Mac上传过来的的CSR文件之后,取出其中的公钥M,用Apple服务器的私钥A对公钥M进行一次数字签名(什么是数字签名?)得到开发证书,然后将开发证书附入描述文件(描述文件还有哪些东西下文会有讲到),最后将描述文件传回给我们的Mac电脑。
2、描述文件
配置描述文件是 XML 文件,包含以下内容:设备的安全策略和限制、VPN 配置信息、无线局域网设置、电子邮件帐户和日历帐户以及可允许 iPhone、iPod touch 和 iPad 配合您的企业系统使用的鉴定凭证。配置描述文件能快速地将设置和授权信息载入到设备上。有些 VPN 和无线局域网设置只能使用配置描述文件来设定,而且如果您使用的不是 Microsoft Exchange,则将需要使用配置描述文件来设定设备密码策略。 描述文件包括的东西,如:
- 开发证书
- APP的Bundle Id
- 证书所信任的Device Id
- 还有一些权限文件(如:Apple Pay,通知,VPN,iCloud)
3、Mac打包APP
在Mac本地有了描述文件之后,就可以对APP进行打包了,打包这个过程除了对APP的压缩之外,更重要的其实是指对APP的加密。 之前本地把公钥M发送给Apple服务器,本地剩下的私钥M就是用来加密APP的,得到一个APP的签名文件,再将上一步得到的描述文件,以及MachO文件一起打包压缩,最终就得到了我们的ipa包了。 所以ipa其实包含以下三种东西
- MachO文件
- APP的签名文件
- 描述文件
4、iPhone验证ipa文件
上文提到的私钥A是用来加密了公钥M,与之对应的公钥A其实在我们激活iPhone的同时已经下载好了,一直躺在我们的iPhone里面。 有了公钥M,那么一切就简单了,
- 公钥A解开ipa中携带的描述文件中的证书得到公钥M
- 对公钥M进行Hash,对比证书中的Hash
- 对比通过之后,用公钥M对加密的APP进行解密,得到最终APP。
3、手动对APP进行重签名 (如果不想看具体过程,可以跳到本节末尾,有流程总结)
从上面可以得知,双层签名其实本质就是如下三个步骤:
- APP的Bundle id的验证
- 描述文件本身的验证
- 对APP签名的验证
注:对APP进行重新签名,前提是这个APP已经被砸壳了。 下载已经砸壳成功的APP,以下例子用微信举例:越狱版本微信7.0.2 提取码: 2w87
具体步骤:
Step 1 进入WeChat目录
解压出 Wechat7.0.2越狱 ,进入WeChat目录
// 进入WeChat的目录
cd /Users/dengbin/Desktop/分享/主目录/资料/Wechat7.0.2越狱/Payload
Step 2 查看APP的证书情况
codesign -d -vv「WeChat.app路径」
Step 3 查看验证APP是否被加密
进入APP的包内容可以看到其中有一个WeChat的可执行文件,也就是是MachO文件,就是要查看这个MachO文件时候被加密
otool -l WeChat | grep cryp
其中cryptid
为0
代表已经砸壳,即解密,为1
或者2
表示以第1类
或者第2类
加密方案加密。
Step 4 查看本地证书并记录需要用到的证书
security find-identity -v -p codesigning
Step 5 删除不可签名的插件(还有Watch中的插件)
由于本地存在一些插件,这些插件是不可被我们重签,并且这个过程我们用不着,所以我们索性就删掉。 其中包括:整个目录PlugIns目录和整个Watch目录(因为Watch.app中也有个PlugIns)
Step 6 Framework重签名
这一步就是比较繁琐的了,需要将Frameworks下的所有Framework进行重签名,运用到XCode提供的codesign
指令,参数中的证书就是Step 4
中的一个。具体使用哪个就看个人了
codesign –fs 「证书串」 「文件名」
Step 7 给MachO添加可执行权限
由于MachO本身就有可执行权限,所以这一步跳过。
Step 8 App重签名
-
新建一个WeChat同名工程(下文称NewWeChat,原来的微信APP称之为WeChat)
-
Build NewWeChat工程,进入被编译出的WeChat.App目录,找到其中的embedded.mobileprovision文件,将其复制到WeChat.App(越狱微信)中
-
在WeChat.App找到info.plist,并修改其中的BundleId为NewWeChat的BundleId
-
查看embedded文件
security cms -D -i 「 embedded文件路径」
找到其中的entitlements字段,并且复制entitlements字段和其中的内容
-
在NewWeChat中新建entitlements.plist文件,将上一步复制得到的「entitlements字段内容」拷贝入新的entitlements.plist文件,然后将entitlements.plist复制到WeChat.app的同级目录下。
-
对APP重新签名 进入WeChat目录,对APP使用新的描述文件进行重签
codesign -fs 「证书串」 --no-strict --entitlements=entitlements.plist
- 压缩Playload
zip –ry 「输出文件名」 「输入文件名」
Step 9 安装新的ipa
可以通过各种途径安装ipa,如Xcode,PP助手,fir,iTunes等等, 如果手机上有正版的微信,在安装完我们重签名的ipa包后会发现手机上就有两个微信啦!!!
如果想用LLDB调试微信,可以将重签后的WeChat.app替换NewWeChat Build后的WeChat.app,然后直接运行(Run)项目,就会发现我们可以用LLDB了。
Step 10 再次验证新的ipa是否真的重签成功
这一步其实是重复Step 3
codesign -d -vv「WeChat.app路径」
步骤总结:
1、cd WeChat // 进入WeChat的目录
2、codesign -d -vv「WeChat.app路径」 // 查看APP的证书情况
3、otool –l WeChat | grep cryp // 查看APP是否被加密
4、security find-identity -v -p codesigning // 查看本地证书
5、删除不可签名的插件(还有Watch中的插件)
6、codesign –fs 「证书串」 「文件名」 // Framework重签名
7、chmod +x 可执行文件 // 给文件添加权限
8、App重签名
① 新建一个项目并且命名为 WeChat(下文称NewWeChat,原来的微信APP称之为WeChat) -> Build -> 找到APP中的权限文件 embedded.mobileprovision
② 复制embedded.mobileprovision到WeChat.app中
③ 修改WeChat.app中info.plist的BundleId
④ security cms -D -i 「 embedded文件路径」 //查看WeChat中的embedded文件,复制WeChat中entitlements.plist文件的entitlements字段
⑤ 在NewWeChat中新建entitlements.plist文件,将上一步复制得到的「entitlements字段和其中的内容」拷贝入新的entitlements.plist文件
⑥ codesign -fs 「证书串」 --no-strict --entitlements=entitlements.plist //复制新的entitlements到WeChat.app的同级目录,并且对APP重新签名
⑦ zip –ry 「输出文件名」 「输入文件名」 // 压缩Playload
9、安装ipa
10、再次验证新的ipa是否真的重签成功
- 注意:有些人会安装失败,或者安装成功会有Crash的问题,这是因为当前描述文件在我们的手机还不受信任,用新工程NewWeChat在手机上跑一遍,然后删除NewWeChat,在尝试安装新的ipa,问题可以解决。
4、使用Shell脚本进行重签
上面所有的步骤其实是固定不变的,而且所有需要操作的文件相对于ipa文件的「相对地址」也是固定的,所以就可以用脚本来代替所有的操作。 以下脚本适用于适用XCode直接调试,点击下载Demo:AppReSign,使用方法详见demo的Readme
附上脚本代码
# ${SRCROOT} 它是工程文件所在的目录
TEMP_PATH="${SRCROOT}/Temp"
#资源文件夹,我们提前在工程目录下新建一个APP文件夹,里面放ipa包
ASSETS_PATH="${SRCROOT}/APP"
#目标ipa包路径
TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa"
#清空Temp文件夹
rm -rf "${SRCROOT}/Temp"
mkdir -p "${SRCROOT}/Temp"
#----------------------------------------
# 1. 解压IPA到Temp下
unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
# 拿到解压的临时的APP的路径
TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
# echo "路径是:$TEMP_APP_PATH"
#----------------------------------------
# 2. 将解压出来的.app拷贝进入工程下
# BUILT_PRODUCTS_DIR 工程生成的APP包的路径
# TARGET_NAME target名称
TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
echo "app路径:$TARGET_APP_PATH"
rm -rf "$TARGET_APP_PATH"
mkdir -p "$TARGET_APP_PATH"
cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH"
#----------------------------------------
# 3. 删除extension和WatchAPP.个人证书没法签名Extention
rm -rf "$TARGET_APP_PATH/PlugIns"
rm -rf "$TARGET_APP_PATH/Watch"
#----------------------------------------
# 4. 更新info.plist文件 CFBundleIdentifier
# 设置:"Set : KEY Value" "目标文件路径"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"
#----------------------------------------
# 5. 给MachO文件上执行权限
# 拿到MachO文件的路径
APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
#上可执行权限
chmod +x "$TARGET_APP_PATH/$APP_BINARY"
#----------------------------------------
# 6. 重签名第三方 FrameWorks
TARGET_APP_FRAMEWORKS_PATH="$TARGET_APP_PATH/Frameworks"
if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ];
then
for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
do
# 签名
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
done
fi
这边文章主要讲了关于双层签名的原理,已经利用双层签名的原理对APP(已越狱)进行重签,但其实其中有一点非常非常重要的内容在这片文章知识被一笔带过,那就是LLDB,能够用LLDB调试我们的APP意味着无限的可能。 所以对LLDB运用,也是咱们逆向的必经之路。后续文章也会提到,待更新。
注意!!!
警告!!!
警告!!!
警告!!!
重签后不要用自己的账号登录,有封号的危险!!!