iOS应用安全3 -- APP重签名

7,100 阅读11分钟

前言

作为iOS开发,我们应该都对证书、描述文件这些东西不陌生。我们知道他们在App安装到手机的过程中起到一些认证的作用,那么他们又是如何对我们的手机进行认证呢?我们能不能篡改这些东西来做一些特别的事情呢?😈

下面我们就从以下几点慢慢介绍苹果的双重签名机制和今天的重头戏---重签名。

  • Apple公司对App安装到手机的需求。
  • Apple公司的双重签名机制。
  • 手动对App重签名。
  • 使用脚本对App重签名。

一、Apple公司对App安装到手机的需求

想想如果App只能通过App store安装,Apple公司为了保证所有App都是由他们授权安装的,那么他们只需要一对公私钥(RSA)签名即可(苹果服务器的私钥签名App,iOS手机系统里的公钥验证签名)。 如果对RSA非对称加密或者签名原理不了解可以看看我前面两篇文章--->RSA加密 | 签名原理

接着上面说,但是现实中App的安装并不是只能通过App store常见的还有这些:

  1. xcode运行安装。
  2. Adhoc内部测试安装。
  3. 企业证书分发。
  4. 等等。。。

那么Apple公司为了让这些渠道也必须要自己的授权才能安装App怎么办?

二、Apple公司的双重签名机制

双重签名原理图:

双重签名

开始讲解签名前需要知道的

公私钥:

  1. Apple服务器和iphone手机拥有一对RSA公私钥,命名为公钥A和私钥A,Apple服务器保存了私钥A,每一部iphone手机在出厂之前就已经将公钥A(所有手机都一样)保存到手机的某个不知名的空间中。
  2. 每个Mac电脑本地也有一对RSA公私钥,命名为公钥M和私钥M。

描述文件:

配置描述文件是 XML 文件,包含以下内容:设备的安全策略和限制、VPN 配置信息、无线局域网设置、电子邮件帐户和日历帐户以及可允许iPhone、iPod touch和iPad配合您的企业系统使用的鉴定凭证。配置描述文件能快速地将设置和授权信息载入到设备上。有些VPN和无线局域网设置只能使用配置描述文件来设定。

描述文件主要包含了以下内容:

  1. 证书。
  2. App的bundle id。
  3. 受信任的设备的udid。
  4. 权限文件(如:Apple Pay,通知,VPN,iCloud等)

1、通过CSR文件申请证书

CSR是Certificate Signing Request的英文缩写,即证书请求文件,也就是证书申请者在申请数字证书时由CSP(加密服务提供者)在生成私钥的同时也生成证书请求文件,证书申请者只要把CSR文件提交给证书颁发机构后,证书颁发机构使用其根证书私钥签名就生成了证书公钥文件,也就是颁发给用户的证书。

简单来说,就是xcode将Mac中的公钥M进行了一层包装变成了CSR文件发送给Apple服务器。Apple服务器收到CSR文件之后取出其中的公钥M,使用Apple服务器保存的私钥A公钥M进行一次数字签名,得到的就是我们的证书。然后将证书添加到描述文件再发送给我们的Mac电脑。
描述文件的其他东西都是我们在开发者网站上填写的,Apple服务器可以直接获取。

2、Mac打包App

在Mac有了描述文件之后,我们就可以对App进行打包。打包的过程主要有:

  • 将项目编译成可执行文件MachO。
  • 使用Mac本地的私钥M对所有MachO文件进行签名。(之所以强调所有MachO是因为编译后并不只是项目被编译成了MachO文件,还有项目中使用的framework静态库等也有对应的MachO文件)。
  • 将MachO文件、MachO文件的签名文件、描述文件一起打包压缩成一个.ipa包。

3、安装.ipa包

打包好的.ipa安装包会安装在iphone手机上,然后iphone手机需要对这个ipa包进行验证,看看是否允许打开。

  1. 取出描述文件,取出证书,使用iphone手机里面的公钥A对证书进行解密,进而可以获得公钥M
  2. 有了公钥M,我们就可以通过公钥M验证MachO文件是否有被篡改,验证MachO的完整性。
  3. 验证描述文件中的其他内容是否全部都满足。
  4. 一切全部验证通过之后,iphone才能正常打开这个App。

以上这些就是Apple公司的双重签名机制的原理,是不是不得不说一句太牛逼了?一切都是那么的恰到好处,果然是苹果大佬。

手动对App重签名

从上面的双重签名原理可以知道,iphone打开app前主要验证了:

  • App的bundle id。
  • 描述文件。
  • App签名。

在App上传到App store的时候,App store会将你上传的ipa包进行一次加密,这个加密被称为套了一个壳。从App store下载的ipa包都是有这个壳的。重签名的前提需要使用砸过壳的ipa,砸过壳的ipa可以从爱思助手等一些助手上下载到。(pp助手现在已经不支持iOS了)

这次拿来做实验的是砸过壳的微信7.0.2(使用此App纯粹是为了学习安全知识,不会用在其他地方😄)。具体步骤:

1、解压WeChat7.0.2.ipa

鼠标右键WeChat7.0.2.ipa选择使用归档实用工具打开。解压后cd到解压文件夹里的payload目录。

cd ~/Desktop/Wechat7.0.2/Payload

WeChat.app
这个WeChat全称是WeChat.app,隐藏了后缀。本质是一个文件夹,里面存放的就是App运行需要的MachO文件、资源等。

2、查看App的证书信息

// 使用命令
codesign -d -vv WeChat.app

查看证书信息
由上可以看到App是使用腾讯企业的证书进行签名的。

3、查看App是否被砸壳

如果我们不确定这个ipa是否已经被砸壳,可以使用命令查看WeChat.app里面的WeChat可执行文件:

WeChat.app目录

otool -l WeChat | grep cryp

WeChat是否被砸壳
如上:cryptid = 0代表为已经砸壳,为1或者2代表使用1类或2类加密方案加密。

4、查看本地的证书

security find-identity -v -p codesigning

事关隐私,就不截图了。查看本地的证书,一会重签名的时候就使用其中的某个证书,当然具体要使用哪个完全看你自己,想要哪个用哪个。

5、删除不可签名的东西

在WeChat.app文件夹里面有些东西是不可以签名的,所以我们就把他们删掉。典型的就是PlugIns文件夹和Watch文件夹(不是所有的App都有,没有就算了),所以我们直接删除就好。

6、重签Framework

在WeChat.app里面找到Frameworks文件夹,打开,里面的全部framework都要进行重签名。

frameworks
使用codesign可以对文件进行签名,具体要使用哪个证书就看自己了。

codesign -fs [证书名称] [要签名的文件]

涉及到证书名称就不截图了。这里需要注意一点,上面命令里的「要签名的文件」并不是Frameworks文件夹里面的.framework。有过了解的人可能知道.framework其本质还是文件夹,二维码真正要重签的是.framework里面的MachO可执行文件。 一定要注意,签错了就没效果了,谁踩过坑谁知道。😂😂😂

7、给MachO文件添加可执行权限

MachO本身就是可执行文件,一般情况下是有可执行权限的,为了保险起见也可以手动再添加一次可执行权限。

// 查看MachO的权限
ls -l WeChat
// 设置权限
chmod 755 WeChat

可执行权限

8、重签名App

8.1、创建一个和微信同名(WeChat)的工程,为了区分以下都称为MyWeChat。选择你重签名证书的team,在手机上先运行一遍。

WeChat工程
8.2、找到MyWeChat编译产生的.app包,显示包内容。
查看编译的WeChat.app
8.3、将embedded.mobileprovision拷贝到WeChat.app中。
embedded
8.4、修改WeChat.app中的info.plist里面的bundle id为MyWeChat工程的bundle id。
8.5、查看embedded.mobileprovision文件的内容。使用以下命令:

// embedded.mobileprovision处填写embedded.mobileprovision文件路径
security cms -D -i embedded.mobileprovision

找到Entitlements字段并且复制其中的内容。

Entitlements
8.6、在MyWeChat工程中新建一个entitlements.plist文件,点击Open As-->Source Code。将刚刚复制的内容粘贴到<dict> </dict>中间,再将这个文件拷贝到WeChat.app的统计目录下。
entitlements.plist
8.7、对App使用新的权限文件重签名

// --no-strict 不严谨的 
// --entitlements=entitlements.plist权限所在文件是entitlements.plist
codesign -fs 签名证书 --no-strict --entitlements=entitlements.plist WeChat.app

8.8、至此,重签名已经完成,接下来只需要将Payload文件夹重新压缩成ipa即可,使用命令:

zip –ry WeChat.ipa Payload

zip

9、安装ipa

安装ipa的方法有很多种Xcode,iTunes等都可以安装。安装成功之后手机上就会有两个微信。bundle id不一样。

微信

10、简单方法

是不是感觉上面第8步特别麻烦?介绍个更简单的方法,其实在执行完上面的第7步---给MachO文件添加可执行权限之后,就可以新建一个同名的工程,先在手机上跑一遍,然后

查看编译的WeChat.app
再然后直接用第7步完成后的WeChat覆盖掉项目编译的Products的WeChat.app
覆盖
再然后使用Xcode cmd+R 运行就可以了,相对来说要比上面的方面太多。

使用脚本对App重签名

你感觉刚刚第10步里面的简单方法很简单了?别急,现在还有更简单的方法,那就是使用脚本对App重签名。
仔细看我们可以发现,上面的那些步骤其实就是一些固定的流程,不管是什么App,重签名的步骤都是那些,所以我们就可以写一个脚本重签名。

至于这个脚本怎么写,也不用我们再写了,前辈们已经写好了,网上随便一搜就能搜到,我这里也再附上脚本代码:

# ${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

脚本代码上有注释,差不多就是我们上面重签名步骤的脚本实现。

下面主要介绍一下脚本的用法:

  1. 首先随便新建一个工程,名字可以任意起,不一定和要进行重签名的App同名。
  2. 然后找到项目的Targets-->build phases-->点击+号-->添加一个Run script-->将上面的脚本拷贝到run script里面。
  3. 在项目的根目录下新建一个文件夹APP,将要重签名的App的.ipa包放到这个文件夹下,如下图。

cqm

剩下的就只需要在手机上运行项目就可以了。

总结

这篇文章主要介绍了Apple双重签名的原理,针对这个原理我们找到了重签名的漏洞。这个漏洞就是Apple虽然让每一个App都必须经过他的授权才能在iphone运行,但是他还是没办法阻止我们替换开发者证书的签名。

讲了这么多,不知道会不会有人疑惑为什么要重签名?

重签名就是为了能够让别人的代码加上我们自己的证书经过Apple的允许在手机上进行调试和运行。
而一旦我们能够进行lldb调试,那么我们就可以通过一系列的工具和手段找到别人App的漏洞。再通过代码注入(下篇文章讲)以达到让别人的App调用我们写的代码逻辑的目的。

转折

通过破解别人代码的过程思考自己的App,是不是存在相同的问题,进而给自己的App添加一些防护手段,保护自己的App,这才是终极目的。😄

本文地址https://juejin.cn/post/6844904098030944264