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

为什么我们自己开发的App安装包不能随便安装到任意的手机呢?App安装包是自己的、手机是自己的,结果就是安装失败,有没有想过这个问题?下面我们来讲讲苹果公司设计的对App安装包的签名机制,并重点的讲解下怎么进行重签名以及反重签名的做法。

一、代码签名原理

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

①:通过Mac电脑创建的CSR文件苹果服务器申请证书CSR文件其实本质是Mac电脑创建的一对RSA公私钥中的公钥M,我们把CSR文件传给苹果服务器苹果服务器使用私钥ACSR文件进行加密和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步:PayloadXXX.appFramework文件夹下的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、通过注入FrameworkHook重签名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:@""].location+8;
            NSInteger toPosition = [embeddedProvisioningLines[i+1] rangeOfString:@""].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逆向编程第九篇:重签名处理)