iOS逆向编程第九篇:重签名处理


为什么我们自己开发的App安装包不能随便安装到任意的手机呢?App安装包是自己的、手机是自己的,结果就是安装失败,有没有想过这个问题?下面我们来讲讲苹果公司设计的对App安装包的签名机制,并重点的……Mac电脑创建的CSR文件向苹果服务器申请证书,CSR文件其实本质是Mac电脑创建的一对RSA公私钥中的公钥M,我们把CSR文件传给苹果服务器,苹果服务器使用私钥A对CSR文件进行加密和hash签名……签名 苹果签名的所有细节处理都已经封装在Xcode中,所以我们可以使用Xcode来替我们做签名处理,只要替换Xcode签名的目标原文件达到欺骗Xcode的目的,使其对我们想要的原文件进行签名处理……,35,0,3,为什么我们自己开发的App安装包不能随便安装到任意的手机呢?App安装包是自己的、手机是自己的,结果就是安装失败,有没有想过这个问题?下面我们来讲讲苹果公司设计的对App安装包的签名机制,并重点的讲解下怎么进行重签名以及反重签名的做法。

一、代码签名原理

想要重签名我们的APP安装包,我们先来了解下APP原始包的签名得到APP安装包过程。先上整体的流程图,然后再来解释每一步操作的过程:

①:通过Mac电脑创建的CSR文件向苹果服务器申请证书,CSR文件其实本质是Mac电脑创建的一对RSA公私钥中的公钥M,我们把CSR文件传给苹果服务器,苹果服务器使用私钥A对CSR文件进行加密和hash签名处理,生成一个证书文件。

②:我们从苹果服务器下载证书和描述文件并安装到当前的Mac电脑中,Mac电脑会将对应的私钥M也证书绑定存放在一起。(在手动管理证书的年代,为什么不能从苹果服务器直接下载了证书使用,而是一定要从证书创建者的Mac电脑中导出证书呢?就是因为私钥M的存在,现在是不是就能理解了)

③:打包的过程中,Mac电脑会使用证书下的私钥M对我们的原始APP包进行签名处理;并把证书以及描述文件都打包到APP的安装包中。

④:当我们的设备安装APP时,会先通过设备内嵌的公钥A对证书、描述文件做解密等处理,获取其中的内容,然后验证证书中的HASH值,来判断证书是否合法;验证APP的签名数据,判断APP是否被篡改过;判断当前设备是否存在可安装的设备列表中,判断描述文件与info.plist中的BundleID是否一致等等。

⑤:最后完成APP的安装

Tips:提供一些查看或查找CSR文件、证书或描述文件内容使用到的命令

//查看CSR文件中的公钥内容
$cat CertificateSigningRequest.certSigningRequest

//查看CSR文件的其他信息(邮箱、加密方式、hash值算法)
$openssl asn1parse -i -in CertificateSigningRequest.certSigningRequest

//查看本机所有证书
$security find-identity -v -p codesigning

//查看描述文件的内容
$security cms -D -i 描述文件路径

二、通过终端命令手动重签名

我们在重签名之前,需要提前准备一些重签名必要的东西。

砸过壳的IPA包 :可以去PP助手中下载越狱应用,自己砸壳的文章之后再进行分享;

可正常使用证书 :重签名IPA包,意思是指替换掉旧的签名证书,使其能正常安装;

有了以上准备,我们就具体来试试手动重签名的操作。

第1步:解压砸过壳的IPA包,删除部分无法重签名的文件

A:删除`Payload` → `XXX.app` → `PlugIns`文件夹
B:删除`Payload` → `XXX.app` → `Watch`文件夹

第2步:对Payload → XXX.app → Framework文件夹下的XXX.framework进行签名。注意:如果IPA包中没有Framework文件夹,则可以跳过这步

//进入`XXX.app`目录下,执行如下命令(有很多`.framework`时需要多次执行)
$codesign -fs "证书" 需要签名的文件

第3步:给App的可执行文件读写权限。

//进入`XXX.app`目录下,执行如下命令
$chmod +x 可执行文件名称

第4步:拷贝embedded.mobileprovision文件到Payload中,修改info.plist中的Bundle identifier值

a. 将新证书的`embedded.mobileprovision`文件拷贝到`Payload`中
b. 将`info.plist`中的`Bundle identifier`值改为新证书对应的`Bundle identifier`值

第5步:生成.plist的权限文件

a. 进入`XXX.app`目录下,使用命令查看描述文件:$security cms -D -i 描述文件路径
b. 拷贝`Entitlements`键下的字典内容,将字典内容存储在新建的`XX.plist`文件
c. 把新建的`XX.plist`文件拷贝到`Playload`文件夹中

第6步:签名整个APP包

//进入`Payload`文件夹下,执行如下命令
$codesign -fs "证书名称" --no-strict --entitlements=XX.plist XXX.app

//查看APP的签名信息
$codesign -vv -d APP路径

//查看可执行文件的加密信息
$otool -l 可执行文件名称 | grep crypt

第7步:将已签名的APP包打包成IPA文件

//进入`Payload`的上级文件夹下,执行如下命令
$zip -ry XXX.ipa Payload

注意:手动重签名会出现很多安装异常的问题,因为可能有很多小细节没有处理或出现问题,所以一般都使用Xcode进行重签名处理。

三、通过Xcode进行重签名

苹果签名的所有细节处理都已经封装在Xcode中,所以我们可以使用Xcode来替我们做签名处理,只要替换Xcode签名的目标原文件达到欺骗Xcode的目的,使其对我们想要的原文件进行签名处理。

1、新建一个名为`AAA`的空工程,编译、运行使其安装到真机中。
2、解压砸过壳的`IPA`包,拷贝`Payload` → `XXX.app`到工程的`Products`下,重命名并替换`AAA.app`。
3、给`App`的可执行文件读写权限,进入`AAA.app`目录下执行命令:`$chmod +x 可执行文件名称`
4、删除部分无法重签名的文件;`①、删除Payload→AAA.app→PlugIns文件夹;②、删除Payload→AAA.app→Watch文件夹`
5、对`Payload`→`AAA.app`→`Framework`文件夹下的`XXX.framework`进行签名,进入`AAA.app`目录下执行命令:`$codesign -fs "证书" 需要签名的文件`
6、修改`AAA.app`→`info.plist`中的`Bundle identifier`值与当前工程的`Bundle identifier`值一致。
7、使用快捷键`command+R`运行当前的工程,此时Xcode已经完成了重签名处理。

最后注意:如果`IPA`包中没有`Framework`文件夹,则直接跳过第5步。

相比第一种手动重签名的方式,Xcode重签名就相对简单多了!

四、通过Run Script脚本进行重签名

相对手动重签名,Xcode重签名已经简单很多了,但是还不是最简单的

第1步:新建一个空工程,在工程目录下新建APP文件夹,将IPA包拷贝到APP目录下。

第2步:选择空工程Build Phases → + → New Run Script Phase添加一个脚本的入口

第3步:将如下的脚本内容,拷贝到Run Script中

# ${SRCROOT} 它是工程文件所在的目录
TEMP_PATH="${SRCROOT}/Temp"
#资源文件夹
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的用处

描述了这么多的重签名方法,有什么用处呢?当然不只是简单的在一个设备上安装多个相同应用这么简单。比如苹果商店有很多需要付费下载的应用,通过重签名后,就能免费进行安装和使用了;类似国内中的同步推、91助手等平台提供很多免费应用,而这些应用在App Store中可能就需要付费下载了。所以大概总结了以下几点重签名的用途:

1、破解需付费下载的应用,比如:同步推、91助手等平台。

2、通过注入Framework来Hook重签名APP中的方法,修改代码的执行顺序,比如:制作微信抢红包的外挂。

3、动态调试重签名的APP,查看界面布局等,比如:探究竞争对象发布的APP的新功能。

六、防止重签名的处理

在逆向编程中,重签名是一个很常用的的动态调试基本操作,所以做重签名的防护是很必要的一个步骤,下面来讲下防止别人重签名你的APP需要怎么处理。

我们先查看下Xcode使用的证书和APP中描述文件的对应关系:

第1步:进入Mac电脑中的钥匙串中,选择证书,双击签名使用的证书,查看并拷贝组织单位的编号。

第2步:进入XXX.app路径下,使用命令security cms -D -i embedded.mobileprovision查看embedded.mobileprovision内容,找到key = application-identifier对应的value值。

结合文章开篇所述的重签名步骤来思考,在任何必要的时候(例如:APP启动等),是否可以通过检测APP签名证书中的组织单位ID是否与Xcdoe工程中的内容一致来判断当前APP是否已经被重签名过。

void checkAppCodesignReplaced(NSString *bundleId)
{
//描述文件路径
NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
//读取application-identifier注意描述文件的编码要使用:NSASCIIStringEncoding
NSString *embeddedProvisioning = [NSString stringWithContentsOfFile:embeddedPath encoding:NSASCIIStringEncoding error:nil];
NSArray *embeddedProvisioningLines = [embeddedProvisioning componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (int i = 0; i < embeddedProvisioningLines.count; i ++) {
if ([embeddedProvisioningLines[i] rangeOfString:@"application-identifier"].location != NSNotFound) {
NSInteger fromPosition = [embeddedProvisioningLines[i+1] rangeOfString:@"<string>"].location+8;
NSInteger toPosition = [embeddedProvisioningLines[i+1] rangeOfString:@"</string>"].location;
NSRange range = NSMakeRange(fromPosition, (toPosition - fromPosition));
NSString *fullIdentifier = [embeddedProvisioningLines[i+1] substringWithRange:range];
NSArray *identifierComponents = [fullIdentifier componentsSeparatedByString:@"."];
NSString *appIdentifier = [identifierComponents firstObject];

//对比签名ID
if (![appIdentifier isEqual:bundleId]) {
asm( //exit
"mov X0, #0\n"
"mov w16, #1\n"
"svc #0x80"
);
}
break;
}
}
}

注意:使用内联汇编代码(asm)是防止逆向工程师通过符号断点来定位exit的调用位置。

七、后续

在APP中仅仅加入防止重签名是远远不够的,对于逆向开发工程师来说,这种操作很容易就破解了。比如说使用HOOK,替换判断组织单位的编号是否一致的方法,亦或是修改汇编代码,使用b指令直接跳过验证方法等等。所以我们还需要做很多其他的处理,才能达到APP安全防护的目的,比如说:反HOOK防护、ptrace防护、混淆关键代码、隐藏敏感方法调用等。此篇文章记录到此,其他的安全防护处理,在之后的文章另做的技术记录。
iOS App 签名的原理,https://www.jianshu.com/p/6fa292eaabce,iOS_Link,2 年前,…… iOS 设备)只能在AppStore下载软件。其实了解的同学知道,这是苹果爸爸为了保证iOS 平台对第三方 APP 绝对的控制权和每一个安装到 iOS 上的 APP 的安全性。而采用的一种签名机制。那么……证书(这里就不讲述证书的申请过程了),这个证书包含了很多信息,除了 设备 ID / AppID,还有其他信息也需要在这里用苹果签名,像这个 APP 里 iCloud / push / 后台运行 等权限……苹果都想控制,苹果把这些权限开关统一称为 Entitlements,它也需要通过签名去授权。 整个App 签名(正常发布的App)流程大致如下: 在你的 Mac 开发机器生成一对公私钥,这里称为公钥L……,520,0,7,很多果粉(非程序员),会有这样的疑问,为什么Android、Windows、Mac OS等系统可以随便在哪里(应用商城)下载一个软件就能安装使用。而iPhone (iPad、iPod 等非越狱 iOS 设备)只能在AppStore下载软件。其实了解的同学知道,这是苹果爸爸为了保证iOS 平台对第三方 APP 绝对的控制权和每一个安装到 iOS 上的 APP 的安全性。而采用的一种签名机制。那么问题来了,什么是签名机制呢?它的原理是什么?

数字签名

通常我们说的签名就是数字签名,它是基于RSA 非对称加密实现的。对称加密是通过同一份密钥加密和解密数据,而非对称加密则有两份密钥,分别是公钥和私钥,用公钥加密的数据,要用私钥才能解密,用私钥加密的数据,要用公钥才能解密。

那数字签名是怎么一回事呢?

数字签名的作用是我对某一份数据打个标记,表示我认可了这份数据(签了个名),然后我发送给其他人,其他人可以知道这份数据是经过我认证的,数据没有被篡改过。

iOS App签名的原理

iOS设备安装App 的几种方式

开发 App 时可以直接把开发中的应用安装进手机进行调试。

In-House 企业内部分发,可以直接安装企业证书签名后的 APP。

AD-Hoc 相当于企业分发的限制版,限制安装设备数量,较少用。

最普遍的从AppStore 下载安装。

iOSApp的签名流程

iOS签名时需要一个证书(这里就不讲述证书的申请过程了),这个证书包含了很多信息,除了 设备 ID / AppID,还有其他信息也需要在这里用苹果签名,像这个 APP 里 iCloud / push / 后台运行 等权限苹果都想控制,苹果把这些权限开关统一称为 Entitlements,它也需要通过签名去授权。

整个App 签名(正常发布的App)流程大致如下:

App 签名流程

在你的 Mac 开发机器生成一对公私钥,这里称为公钥L,私钥L。L:Local

苹果自己有固定的一对公私钥,跟上面 AppStore 例子一样,私钥在苹果后台,公钥在每个 iOS 设备上。这里称为公钥A,私钥A。A:Apple

把公钥 L 传到苹果后台,用苹果后台里的私钥 A 去签名公钥 L。得到一份数据包含了公钥 L 以及其签名,把这份数据称为证书。

在苹果后台申请 AppID,配置好设备 ID 列表和 APP 可使用的权限,再加上第③步的证书,组成的数据用私钥 A 签名,把数据和签名一起组成一个 Provisioning Profile 文件,下载到本地 Mac 开发机。

在开发时,编译完一个 APP 后,用本地的私钥 L 对这个 APP 进行签名,同时把第④步得到的 Provisioning Profile 文件打包进 APP 里,文件名为embedded.mobileprovision,把 APP 安装到手机上。

在安装时,iOS 系统取得证书,通过系统内置的公钥 A,去验证embedded.mobileprovision的数字签名是否正确,里面的证书签名也会再验一遍。

确保了embedded.mobileprovision里的数据都是苹果授权以后,就可以取出里面的数据,做各种验证,包括用公钥 L 验证APP签名,验证设备 ID 是否在 ID 列表上,AppID 是否对应得上,权限开关是否跟 APP 里的 Entitlements 对应等。

前面以开发包为例子说了签名和验证的流程,另外两种方式 In-House 企业签名和 AD-Hoc 流程也是差不多的,只是企业签名不限制安装的设备数,另外需要用户在 iOS 系统设置上手动点击信任这个企业才能通过验证。

而 AppStore 的签名验证方式有些不一样,前面我们说到最简单的签名方式,苹果在后台直接用私钥签名 App 就可以了,实际上苹果确实是这样做的,如果去下载一个 AppStore 的安装包,会发现它里面是没有embedded.mobileprovision文件的,也就是它安装和启动的流程是不依赖这个文件,验证流程也就跟上述几种类型不一样了。

据猜测,因为上传到 AppStore 的包苹果会重新对内容加密,原来的本地私钥签名就没有用了,需要重新签名,从 AppStore 下载的包苹果也并不打算控制它的有效期,不需要内置一个embedded.mobileprovision去做校验,直接在苹果用后台的私钥重新签名,iOS 安装时用本地公钥验证 App 签名就可以了。

那为什么发布 AppStore 的包还是要跟开发版一样搞各种证书和 Provisioning Profile?猜测因为苹果想做统一管理,Provisioning Profile 里包含一些权限控制,AppID 的检验等,苹果不想在上传 AppStore 包时重新用另一种协议做一遍这些验证,就不如统一把这部分放在 Provisioning Profile 里,上传 AppStore 时只要用同样的流程验证这个 Provisioning Profile 是否合法就可以了。

所以 App 上传到 AppStore 后,就跟你的 证书 / Provisioning Profile 都没有关系了,无论他们是否过期或被废除,都不会影响 AppStore 上的安装包。

【特别鸣谢】

https://segmentfault.com/a/1190000004144556

http://www.cocoachina.com/ios/20170602/19427.html

http://www.ruanyifeng.com/blog/2013/06/rsa_algorithm_part_one.html

http://www.ruanyifeng.com/blog/2013/07/rsa_algorithm_part_two.html

 

企业签名QQ:281442504

分享到