JHHK

欢迎来到我的个人网站
行者常至 为者常成

iOS重签名与防重签

目录

有了前两篇
iOS签名原理
iOS签名与验签逻辑
的理论与实践介绍,我们来看看如何对一个APP进行重签名和防止重签名。

重签名

一、指令实现

当我们拿到一个xxx.ipa包,想要在未授权的设备上安装或者修改包内容后在设备上安装时,这时会因为验签失败导致无法安装,这时我们就需要对这个包解压得到xxx.app然后进行重签名。

要对一个APP签名需要三个东西:

Code Signing Entitlements:yyy.entitlements文件,提供APP的权限

Code Signing Identity:iPhone Developer yyy(JBLXXX6F92)带私钥的证书,签名APP包

Provisioning Profile:描述文件,用于验签

这三样东西从哪来,当然是从我们自己有权限的APP中来获得。

1、Provisioning Profile的获取

从我们自己的yyy.app包内显示包内容取出embedded.mobileprovision文件,或者从开发者网站下载后改名为embedded.mobileprovision,替换xxx.app包内的embedded.mobileprovision文件,用于安装时验签。

2、Code Signing Identity的获取

该证书(带私钥)位于我们的Mac,可从钥匙串中查看例如 iPhone Developer yyy(JBLSRC6F92),或者可以使用证书的ID

img

sha-1 就是该证书的ID。也可以通过指令在终端查看可用于签名的证书的id

security find-identity -v -p codesigning

Find an identity valid (certificate + private key).

$ security find-identity -v -p codesigning
  1) 308C7A35228D405EDA378BDA0412C9ED389528E5 "iPhone Developer: xxxx (xxxx)"
  2) 6AF7DAC8855CEF993AD8C6AF53AEF059A2CB26C8 "iPhone Distribution: xxxx Service co. LTD."
  3) 1D2879BD015CD866CB900745A49AFC32252CA381 "Apple Development: xxxx (xxxx)"
  4) 09AD074572F7EFC14E306B5BD92C90B4656D474D "iPhone Developer: xxxx (xxxx)"
  5) 6CC46A30E4CC1C906C4EBBD60D97CC20DCB8A8A3 "iPhone Developer: xxxx@yeah.net (xxxx)"
  6) 49BA747E31E484EB62CF6C82B7677357BE5A4BD2 "iPhone Distribution: xxxx Co., Ltd. (xxxx)"
  7) 459875A0ED41818E3EA7A25F962AFFADC5836591 "Mac Developer: xxxx@qq.com (xxxx)"
  8) 1ED104EDD7E3C72A0FE891AE6CE3A66197602C95 "Apple Development: xxxx (xxxx)"
  9) 8A58379560579700A1C2660F086E0DA1C4D9C655 "Apple Development: xxxx@yeah.net (xxxx)"
  9 valid identities found

3、Code Signing Entitlements获取

yyy.entitlements中的权限必须与embedded.mobileprovision中的权限匹配,所以我们从embedded.mobileprovision中提取权限文件。

security cms -D -i embedded.mobileprovision > temp.plist
/usr/libexec/PlistBuddy -x -c 'Print:Entitlements' temp.plist > entitlements.plist

4、重签名

如果xxx.app包内有Frameworks文件夹需要先对xxx.app内部的动态库、AppExtension等进行签名

codesign -fs 证书ID *.dylib     //对所有的dylib进行签名
codesign -fs 证书ID *.framework  //对所有的framework进行重签名

然后对xxx.app包进行重签名

codesign -fs 证书ID --entitlements entitlements.plist xxx.app

到此重签名已经完成,xxx.app就可以在我们自己的设备上安装了。

二、xcode与脚本实现

1、在现有工程或新建一个demo工程,配置好证书及描述文件后,先在手机上运行一次(拷贝描述文件)

2、将脚本appSign.sh拷贝到工程根目录,创建APP和Temp两个文件夹,将要重签的xxx.ipa包放入App文件夹内。

img

3、xcode配置脚本

img

4、Commond+R运行xcode,重签的APP就会安装到手机。

如果该过程遇到编译错误,appSign.sh: Permission denied,说明appSign.sh没有执行权限,执行如下指令后,重新进行步骤4

chmode u+x appSign.sh

appSign.sh文件

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


#注入
#yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/HankHook.framework/HankHook"

防重签

那么如何防止我们开发的APP被别人重签呢?明白了签名的过程,我们可以从不同的角度来切入,首先我们会想到签名文件,如果APP包被重签名了,那么_CodeSignature文件夹下的签名文件CodeResources必定会被修改,我们可以跟后台配合通过接口来判断APP是否被重签名了。

1、后台提供接口isResign来判断APP是否被重签,判断是否重签的依据是CodeResources文件的哈希值。 2、每次创建完构建版本后,将CodeResources文件的哈希值给到后台。

但是这样做有一个很严重的问题,APP包每次上传到App Store时苹果后台会用苹果的私钥对APP重签,所以用户下载到手机时包内的CodeResources文件已经不是我们上传时的CodeResources文件了。所以这样做会把苹果的重签一并过滤掉。此方法不可行

2、那么我们从embedded.mobileprovision文件入手呢,每次重签需要替换embedded.mobileprovision文件,而该文件内保存了我们的证书信息,我们来检测证书信息的变化来判断APP是否被重签了。

代码如下

- (void)checkCodesign {
    // 描述文件路径
    NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
    
    if ([[NSFileManager defaultManager] fileExistsAtPath:embeddedPath]) {
        
        // 读取application-identifier
        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 objectAtIndex:i] rangeOfString:@"application-identifier"].location != NSNotFound) {
                
                NSInteger fromPosition = [[embeddedProvisioningLines objectAtIndex:i+1] rangeOfString:@"<string>"].location+8;
                
                NSInteger toPosition = [[embeddedProvisioningLines objectAtIndex:i+1] rangeOfString:@"</string>"].location;
                
                NSRange range;
                range.location = fromPosition;
                range.length = toPosition - fromPosition;
                
                NSString *fullIdentifier = [[embeddedProvisioningLines objectAtIndex:i+1] substringWithRange:range];
                
                //                NSLog(@"%@", fullIdentifier);
                
                NSArray *identifierComponents = [fullIdentifier componentsSeparatedByString:@"."];
                NSString *appIdentifier = [identifierComponents firstObject];
                
                // 对比签名ID
                if (![appIdentifier isEqual:@"VX876XXXBH"]) {
                    NSString *message = @"检测到您正在使用非XXX官方渠道版本,为了交易安全,请卸载并从正规渠道下载安装最新版本!";
                    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"警告"
                                                                           message:message
                                                                          delegate:nil
                                                                 cancelButtonTitle:@"确认"
                                                                 otherButtonTitles:nil, nil];
                           [alert show];
                }
                break;
            }
        }
    }
}

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.